diff --git a/build-tools/tasks/test-utils.js b/build-tools/tasks/test-utils.js index a1e43ca91c..07ac0dffe5 100644 --- a/build-tools/tasks/test-utils.js +++ b/build-tools/tasks/test-utils.js @@ -3,11 +3,11 @@ const { src, dest, series, parallel } = require('gulp'); const execa = require('execa'); const path = require('path'); -const pluralize = require('pluralize'); const { pascalCase } = require('change-case'); const { default: convertToSelectorUtil } = require('@cloudscape-design/test-utils-converter'); const { through, task } = require('../utils/gulp-utils'); const { writeFile, listPublicItems } = require('../utils/files'); +const { pluralizeComponentName } = require('../utils/pluralize'); const themes = require('../utils/themes'); function toWrapper(componentClass) { @@ -18,7 +18,7 @@ const testUtilsSrcDir = path.resolve('src/test-utils'); const configs = { dom: { defaultExport: `export default function wrapper(root: Element = document.body) { if (document && document.body && !document.body.contains(root)) { console.warn('[AwsUi] [test-utils] provided element is not part of the document body, interactions may work incorrectly')}; return new ElementWrapper(root); }`, - buildFinderInterface: ({ componentName }) => + buildFinderInterface: ({ componentName, componentNamePlural }) => `/** * Returns the wrapper of the first ${componentName} that matches the specified CSS selector. * If no CSS selector specified, returns the wrapper of the first ${componentName}. @@ -29,13 +29,13 @@ const configs = { */ find${componentName}(selector?: string): ${toWrapper(componentName)} | null; /** - * Returns the wrappers of all ${pluralize(componentName)} that match the specified CSS selector. - * If no CSS selector specified, returns all of the ${pluralize(componentName)} inside current wrapper. + * Returns the wrappers of all ${componentNamePlural} that match the specified CSS selector. + * If no CSS selector specified, returns all of the ${componentNamePlural} inside current wrapper. * If no matching ${componentName} is found, returns an empty array. * * @returns {Array<${toWrapper(componentName)}>} */ - findAll${pluralize(componentName)}(selector?: string): Array<${toWrapper(componentName)}>; + findAll${componentNamePlural}(selector?: string): Array<${toWrapper(componentName)}>; /** * Returns the first ${componentName} that matches the specified test id. * Looks for \`data-testid\` attribute assigned to the component. @@ -48,7 +48,7 @@ const configs = { }, selectors: { defaultExport: `export default function wrapper(root: string = 'body') { return new ElementWrapper(root); }`, - buildFinderInterface: ({ componentName }) => + buildFinderInterface: ({ componentName, componentNamePlural }) => `/** * Returns a wrapper that matches a ${componentName} with the specified CSS selector. * If no CSS selector is specified, returns a wrapper that matches ${componentName}. @@ -58,12 +58,12 @@ const configs = { */ find${componentName}(selector?: string): ${toWrapper(componentName)}; /** - * Returns a wrapper that matches all of the ${pluralize(componentName)} satisfying the specified CSS selector. - * If no CSS selector specified, returns a wrapper that matches all of the ${pluralize(componentName)} inside current wrapper. + * Returns a wrapper that matches all of the ${componentNamePlural} satisfying the specified CSS selector. + * If no CSS selector specified, returns a wrapper that matches all of the ${componentNamePlural} inside current wrapper. * * @returns {MultiElementWrapper<${toWrapper(componentName)}>} */ - findAll${pluralize(componentName)}(selector?: string): MultiElementWrapper<${toWrapper(componentName)}>; + findAll${componentNamePlural}(selector?: string): MultiElementWrapper<${toWrapper(componentName)}>; /** * Returns a wrapper that matches ${componentName} with the specified test id. * Matches \`data-testid\` attribute assigned to the component. @@ -102,7 +102,7 @@ function generateIndexFileContent(testUtilType, testUtilMetaData) { ${testUtilMetaData.map(metaData => buildFinderInterface(metaData)).join('\n')} } }`, - ...testUtilMetaData.map(({ componentName }) => { + ...testUtilMetaData.map(({ componentName, componentNamePlural }) => { const wrapperName = toWrapper(componentName); // language=TypeScript return ` @@ -111,7 +111,7 @@ function generateIndexFileContent(testUtilType, testUtilMetaData) { return this.findComponent(selector ? appendSelector(selector, rootSelector) : rootSelector, ${wrapperName}); }; - ElementWrapper.prototype.findAll${pluralize(componentName)} = function(selector) { + ElementWrapper.prototype.findAll${componentNamePlural} = function(selector) { const rootSelector = \`.$\{${wrapperName}.rootSelector}\`; return this.findAllComponents(selector ? appendSelector(selector, rootSelector) : rootSelector, ${wrapperName}); }; @@ -135,9 +135,11 @@ function generateTestUtilMetaData(testUtilType) { const componentNameKebab = componentFolderName; const componentName = pascalCase(componentNameKebab); + const componentNamePlural = pluralizeComponentName(componentName); const componentMetaData = { componentName, + componentNamePlural, relPathtestUtilFile, }; diff --git a/build-tools/utils/pluralize.js b/build-tools/utils/pluralize.js new file mode 100644 index 0000000000..affc84b951 --- /dev/null +++ b/build-tools/utils/pluralize.js @@ -0,0 +1,90 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +const pluralizationMap = { + Alert: 'Alerts', + AnchorNavigation: 'AnchorNavigations', + Annotation: 'Annotations', + AppLayout: 'AppLayouts', + AreaChart: 'AreaCharts', + AttributeEditor: 'AttributeEditors', + Autosuggest: 'Autosuggests', + Badge: 'Badges', + BarChart: 'BarCharts', + Box: 'Boxes', + BreadcrumbGroup: 'BreadcrumbGroups', + Button: 'Buttons', + ButtonDropdown: 'ButtonDropdowns', + ButtonGroup: 'ButtonGroups', + Calendar: 'Calendars', + Cards: 'Cards', + Checkbox: 'Checkboxes', + CodeEditor: 'CodeEditors', + CollectionPreferences: 'CollectionPreferences', + ColumnLayout: 'ColumnLayouts', + Container: 'Containers', + ContentLayout: 'ContentLayouts', + CopyToClipboard: 'CopyToClipboards', + DateInput: 'DateInputs', + DatePicker: 'DatePickers', + DateRangePicker: 'DateRangePickers', + Drawer: 'Drawers', + ExpandableSection: 'ExpandableSections', + FileUpload: 'FileUploads', + Flashbar: 'Flashbars', + Form: 'Forms', + FormField: 'FormFields', + Grid: 'Grids', + Header: 'Headers', + HelpPanel: 'HelpPanels', + Hotspot: 'Hotspots', + Icon: 'Icons', + Input: 'Inputs', + KeyValuePairs: 'KeyValuePairs', + LineChart: 'LineCharts', + Link: 'Links', + LiveRegion: 'LiveRegions', + MixedLineBarChart: 'MixedLineBarCharts', + Modal: 'Modals', + Multiselect: 'Multiselects', + Pagination: 'Paginations', + PieChart: 'PieCharts', + Popover: 'Popovers', + ProgressBar: 'ProgressBars', + PromptInput: 'PromptInputs', + PropertyFilter: 'PropertyFilters', + RadioGroup: 'RadioGroups', + S3ResourceSelector: 'S3ResourceSelectors', + SegmentedControl: 'SegmentedControls', + Select: 'Selects', + SideNavigation: 'SideNavigations', + Slider: 'Sliders', + SpaceBetween: 'SpaceBetweens', + Spinner: 'Spinners', + SplitPanel: 'SplitPanels', + StatusIndicator: 'StatusIndicators', + Steps: 'Steps', + Table: 'Tables', + Tabs: 'Tabs', + TagEditor: 'TagEditors', + TextContent: 'TextContents', + TextFilter: 'TextFilters', + Textarea: 'Textareas', + Tiles: 'Tiles', + TimeInput: 'TimeInputs', + Toggle: 'Toggles', + ToggleButton: 'ToggleButtons', + TokenGroup: 'TokenGroups', + TopNavigation: 'TopNavigations', + TutorialPanel: 'TutorialPanels', + Wizard: 'Wizards', +}; + +function pluralizeComponentName(componentName) { + if (!(componentName in pluralizationMap)) { + throw new Error(`Could not find the plural case for ${componentName}.`); + } + + return pluralizationMap[componentName]; +} + +module.exports = { pluralizeComponentName }; diff --git a/package-lock.json b/package-lock.json index 5e57b48475..295c045772 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", "@juggle/resize-observer": "^3.3.1", + "@types/pluralize": "^0.0.33", "ace-builds": "^1.34.0", "balanced-match": "^1.0.2", "clsx": "^1.1.0", @@ -3949,6 +3950,11 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/pluralize": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/pluralize/-/pluralize-0.0.33.tgz", + "integrity": "sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==" + }, "node_modules/@types/pngjs": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.5.tgz", diff --git a/package.json b/package.json index f89b4ed34f..e2c46cbbd1 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", "@juggle/resize-observer": "^3.3.1", + "@types/pluralize": "^0.0.33", "ace-builds": "^1.34.0", "balanced-match": "^1.0.2", "clsx": "^1.1.0", diff --git a/src/__tests__/test-utils-wrapper.test.tsx b/src/__tests__/test-utils-wrapper.test.tsx index 4689a78431..9f32578522 100644 --- a/src/__tests__/test-utils-wrapper.test.tsx +++ b/src/__tests__/test-utils-wrapper.test.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import fs from 'fs'; import path from 'path'; +import pluralize from 'pluralize'; import * as components from '../../lib/components'; import * as dom from '../../lib/components/test-utils/dom'; @@ -10,24 +11,14 @@ import * as selectors from '../../lib/components/test-utils/selectors'; jest.mock('@cloudscape-design/test-utils-core/dom'); jest.mock('@cloudscape-design/test-utils-core/selectors'); -interface ComponentWrapperClass { - rootSelector: string; - new (): WrapperType; -} - function getComponentsList() { - const exceptions = { + const exceptions: Record = { AnnotationContext: 'Annotation', - } as const; - - type ImportedComponentName = keyof typeof components; - type ComponentNameException = keyof typeof exceptions; - type ComponentNameReplacement = (typeof exceptions)[ComponentNameException]; - type ComponentName = Exclude | ComponentNameReplacement; + }; return Object.keys(components) - .map(component => (exceptions as Record)[component] ?? component) - .filter(Boolean) as ComponentName[]; + .map(component => exceptions[component] ?? component) + .filter(Boolean); } describe('Test utils element wrapper', () => { @@ -69,11 +60,12 @@ describe('Test utils element wrapper', () => { ] as const; describe.each(testUtilTypes)('$type wrapper', ({ testWrapper, testUtilImport }) => { + const componentNamePluralized = pluralize(componentName); + test(`find${componentName} calls findComponents with ${componentName}'s root selector`, () => { - const wrapperName = `${componentName}Wrapper` as const; - const WrapperClass = testUtilImport[wrapperName] as ComponentWrapperClass; + const WrapperClass = testUtilImport[`${componentName}Wrapper` as 'AlertWrapper']; - testWrapper[`find${componentName}`](); + testWrapper[`find${componentName}` as 'findAlert'](); expect(testUtilImport.ElementWrapper.prototype.findComponent).toHaveBeenCalledWith( `.${WrapperClass.rootSelector}`, @@ -81,12 +73,10 @@ describe('Test utils element wrapper', () => { ); }); - test(`findAll${componentName} calls findAllComponents with ${componentName}'s root selector`, () => { - const wrapperName = `${componentName}Wrapper` as const; - const WrapperClass = testUtilImport[wrapperName] as ComponentWrapperClass; + test(`findAll${componentNamePluralized} calls findAllComponents with ${componentName}'s root selector`, () => { + const WrapperClass = testUtilImport[`${componentName}Wrapper` as 'AlertWrapper']; - const pluralizedComponentName = pluralNamesMap[componentName]; - testWrapper[`findAll${pluralizedComponentName}`](); + testWrapper[`findAll${componentNamePluralized}` as 'findAllAlerts'](); expect(testUtilImport.ElementWrapper.prototype.findAllComponents).toHaveBeenCalledWith( `.${WrapperClass.rootSelector}`, @@ -95,10 +85,9 @@ describe('Test utils element wrapper', () => { }); test(`find${componentName}ByTestId calls findComponent with data-testid appended to ${componentName}'s root selector`, () => { - const wrapperName = `${componentName}Wrapper` as const; - const WrapperClass = testUtilImport[wrapperName] as ComponentWrapperClass; + const WrapperClass = testUtilImport[`${componentName}Wrapper` as 'AlertWrapper']; - testWrapper[`find${componentName}ByTestId`]('test-id'); + testWrapper[`find${componentName}ByTestId` as 'findAlertByTestId']('test-id'); expect(testUtilImport.ElementWrapper.prototype.findComponent).toHaveBeenCalledWith( `.${WrapperClass.rootSelector}[data-testid="test-id"]`, @@ -108,82 +97,3 @@ describe('Test utils element wrapper', () => { }); }); }); - -const pluralNamesMap = { - Alert: 'Alerts', - AnchorNavigation: 'AnchorNavigations', - Annotation: 'Annotations', - AppLayout: 'AppLayouts', - AreaChart: 'AreaCharts', - AttributeEditor: 'AttributeEditors', - Autosuggest: 'Autosuggests', - Badge: 'Badges', - BarChart: 'BarCharts', - Box: 'Boxes', - BreadcrumbGroup: 'BreadcrumbGroups', - Button: 'Buttons', - ButtonDropdown: 'ButtonDropdowns', - ButtonGroup: 'ButtonGroups', - Calendar: 'Calendars', - Cards: 'Cards', - Checkbox: 'Checkboxes', - CodeEditor: 'CodeEditors', - CollectionPreferences: 'CollectionPreferences', - ColumnLayout: 'ColumnLayouts', - Container: 'Containers', - ContentLayout: 'ContentLayouts', - CopyToClipboard: 'CopyToClipboards', - DateInput: 'DateInputs', - DatePicker: 'DatePickers', - DateRangePicker: 'DateRangePickers', - Drawer: 'Drawers', - ExpandableSection: 'ExpandableSections', - FileUpload: 'FileUploads', - Flashbar: 'Flashbars', - Form: 'Forms', - FormField: 'FormFields', - Grid: 'Grids', - Header: 'Headers', - HelpPanel: 'HelpPanels', - Hotspot: 'Hotspots', - Icon: 'Icons', - Input: 'Inputs', - KeyValuePairs: 'KeyValuePairs', - LineChart: 'LineCharts', - Link: 'Links', - LiveRegion: 'LiveRegions', - MixedLineBarChart: 'MixedLineBarCharts', - Modal: 'Modals', - Multiselect: 'Multiselects', - Pagination: 'Paginations', - PieChart: 'PieCharts', - Popover: 'Popovers', - ProgressBar: 'ProgressBars', - PromptInput: 'PromptInputs', - PropertyFilter: 'PropertyFilters', - RadioGroup: 'RadioGroups', - S3ResourceSelector: 'S3ResourceSelectors', - SegmentedControl: 'SegmentedControls', - Select: 'Selects', - SideNavigation: 'SideNavigations', - Slider: 'Sliders', - SpaceBetween: 'SpaceBetweens', - Spinner: 'Spinners', - SplitPanel: 'SplitPanels', - StatusIndicator: 'StatusIndicators', - Steps: 'Steps', - Table: 'Tables', - Tabs: 'Tabs', - TagEditor: 'TagEditors', - TextContent: 'TextContents', - TextFilter: 'TextFilters', - Textarea: 'Textareas', - Tiles: 'Tiles', - TimeInput: 'TimeInputs', - Toggle: 'Toggles', - ToggleButton: 'ToggleButtons', - TokenGroup: 'TokenGroups', - TopNavigation: 'TopNavigations', - TutorialPanel: 'TutorialPanels', - Wizard: 'Wizards', -} as const;