@@ -7,81 +7,167 @@ import path from "node:path";
77
88import { default as convertToSelectorUtil } from "@cloudscape-design/test-utils-converter" ;
99
10+ import { pluralizeComponentName } from "./pluralize.js" ;
1011import { pascalCase , writeSourceFile } from "./utils.js" ;
1112
1213const components = globbySync ( [ "src/test-utils/dom/**/index.ts" , "!src/test-utils/dom/index.ts" ] ) . map ( ( fileName ) =>
1314 fileName . replace ( "src/test-utils/dom/" , "" ) . replace ( "/index.ts" , "" ) ,
1415) ;
1516
16- generateSelectorUtils ( ) ;
17- generateDomIndexFile ( ) ;
18- generateSelectorsIndexFile ( ) ;
19- compileTypescript ( ) ;
20-
21- function generateSelectorUtils ( ) {
22- components . forEach ( ( componentName ) => {
23- const domFileName = `./src/test-utils/dom/${ componentName } /index.ts` ;
24- const domFileContent = fs . readFileSync ( domFileName , "utf-8" ) ;
25- const selectorsFileName = `./src/test-utils/selectors/${ componentName } /index.ts` ;
26- const selectorsFileContent = convertToSelectorUtil . default ( domFileContent ) ;
27- writeSourceFile ( selectorsFileName , selectorsFileContent ) ;
28- } ) ;
17+ function toWrapper ( componentClass ) {
18+ return `${ componentClass } Wrapper` ;
2919}
3020
31- function generateDomIndexFile ( ) {
32- const content = generateIndexFileContent ( {
33- testUtilType : "dom" ,
34- buildFinderInterface : ( componentName ) =>
35- `find${ pascalCase ( componentName ) } (selector?: string): ${ pascalCase ( componentName ) } Wrapper | null;` ,
36- } ) ;
37- writeSourceFile ( "./src/test-utils/dom/index.ts" , content ) ;
21+ const testUtilsSrcDir = path . resolve ( "src/test-utils" ) ;
22+ const configs = {
23+ common : {
24+ buildFinder : ( { componentName, componentNamePlural } ) => `
25+ ElementWrapper.prototype.find${ componentName } = function(selector) {
26+ const rootSelector = \`.$\{${ toWrapper ( componentName ) } .rootSelector}\`;
27+ // casting to 'any' is needed to avoid this issue with generics
28+ // https://github.com/microsoft/TypeScript/issues/29132
29+ return (this as any).findComponent(selector ? appendSelector(selector, rootSelector) : rootSelector, ${ toWrapper ( componentName ) } );
30+ };
31+
32+ ElementWrapper.prototype.findAll${ componentNamePlural } = function(selector) {
33+ return this.findAllComponents(${ toWrapper ( componentName ) } , selector);
34+ };` ,
35+ } ,
36+ dom : {
37+ 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); }` ,
38+ buildFinderInterface : ( { componentName, componentNamePlural } ) => `
39+ /**
40+ * Returns the wrapper of the first ${ componentName } that matches the specified CSS selector.
41+ * If no CSS selector is specified, returns the wrapper of the first ${ componentName } .
42+ * If no matching ${ componentName } is found, returns \`null\`.
43+ *
44+ * @param {string} [selector] CSS Selector
45+ * @returns {${ toWrapper ( componentName ) } | null}
46+ */
47+ find${ componentName } (selector?: string): ${ toWrapper ( componentName ) } | null;
48+
49+ /**
50+ * Returns an array of ${ componentName } wrapper that matches the specified CSS selector.
51+ * If no CSS selector is specified, returns all of the ${ componentNamePlural } inside the current wrapper.
52+ * If no matching ${ componentName } is found, returns an empty array.
53+ *
54+ * @param {string} [selector] CSS Selector
55+ * @returns {Array<${ toWrapper ( componentName ) } >}
56+ */
57+ findAll${ componentNamePlural } (selector?: string): Array<${ toWrapper ( componentName ) } >;` ,
58+ } ,
59+ selectors : {
60+ defaultExport : `export default function wrapper(root: string = 'body') { return new ElementWrapper(root); }` ,
61+ buildFinderInterface : ( { componentName, componentNamePlural } ) => `
62+ /**
63+ * Returns a wrapper that matches the ${ componentNamePlural } with the specified CSS selector.
64+ * If no CSS selector is specified, returns a wrapper that matches ${ componentNamePlural } .
65+ *
66+ * @param {string} [selector] CSS Selector
67+ * @returns {${ toWrapper ( componentName ) } }
68+ */
69+ find${ componentName } (selector?: string): ${ toWrapper ( componentName ) } ;
70+
71+ /**
72+ * Returns a multi-element wrapper that matches ${ componentNamePlural } with the specified CSS selector.
73+ * If no CSS selector is specified, returns a multi-element wrapper that matches ${ componentNamePlural } .
74+ *
75+ * @param {string} [selector] CSS Selector
76+ * @returns {MultiElementWrapper<${ toWrapper ( componentName ) } >}
77+ */
78+ findAll${ componentNamePlural } (selector?: string): MultiElementWrapper<${ toWrapper ( componentName ) } >;` ,
79+ } ,
80+ } ;
81+
82+ function generateFindersInterfaces ( { testUtilMetaData, testUtilType, configs } ) {
83+ const { buildFinderInterface } = configs [ testUtilType ] ;
84+ const findersInterfaces = testUtilMetaData . map ( buildFinderInterface ) ;
85+
86+ // we need to redeclare the interface in its original definition, extending a re-export will not work
87+ // https://github.com/microsoft/TypeScript/issues/12607
88+ const interfaces = `declare module '@cloudscape-design/test-utils-core/dist/${ testUtilType } ' {
89+ interface ElementWrapper {
90+ ${ findersInterfaces . join ( "\n" ) }
91+ }
92+ }` ;
93+
94+ return interfaces ;
3895}
3996
40- function generateSelectorsIndexFile ( ) {
41- const content = generateIndexFileContent ( {
42- testUtilType : "selectors" ,
43- buildFinderInterface : ( componentName ) =>
44- `find${ pascalCase ( componentName ) } (selector?: string): ${ pascalCase ( componentName ) } Wrapper;` ,
45- } ) ;
46- writeSourceFile ( "./src/test-utils/selectors/index.ts" , content ) ;
97+ function generateFindersImplementations ( { testUtilMetaData, configs } ) {
98+ const { buildFinder } = configs . common ;
99+ const findersImplementations = testUtilMetaData . map ( buildFinder ) ;
100+ return findersImplementations . join ( "\n" ) ;
47101}
48102
49- function generateIndexFileContent ( { testUtilType, buildFinderInterface } ) {
103+ function generateIndexFileContent ( testUtilType , testUtilMetaData ) {
104+ const config = configs [ testUtilType ] ;
105+ if ( config === undefined ) {
106+ throw new Error ( "Unknown test util type" ) ;
107+ }
108+
50109 return [
51110 // language=TypeScript
52111 `import { ElementWrapper } from '@cloudscape-design/test-utils-core/${ testUtilType } ';` ,
53112 `import { appendSelector } from '@cloudscape-design/test-utils-core/utils';` ,
54113 `export { ElementWrapper };` ,
55- ...components . map ( ( componentName ) => {
56- const componentImport = `./${ componentName } /index` ;
114+ ...testUtilMetaData . map ( ( metaData ) => {
115+ const { componentName, relPathtestUtilFile } = metaData ;
116+
57117 return `
58- import ${ pascalCase ( componentName ) } Wrapper from '${ componentImport } ';
59- export { ${ pascalCase ( componentName ) } Wrapper };
118+ import ${ toWrapper ( componentName ) } from '${ relPathtestUtilFile } ';
119+ export { ${ componentName } Wrapper };
60120 ` ;
61121 } ) ,
62- // we need to redeclare the interface in its original definition, extending a re-export will not work
63- // https://github.com/microsoft/TypeScript/issues/12607
64- `declare module '@cloudscape-design/test-utils-core/dist/${ testUtilType } ' {
65- interface ElementWrapper {
66- ${ components . map ( ( componentName ) => buildFinderInterface ( componentName ) ) . join ( "\n" ) }
67- }
68- }` ,
69- ...components . map ( ( componentName ) => {
70- // language=TypeScript
71- return `ElementWrapper.prototype.find${ pascalCase ( componentName ) } = function(selector) {
72- const rootSelector = \`.$\{${ pascalCase ( componentName ) } Wrapper.rootSelector}\`;
73- // casting to 'any' is needed to avoid this issue with generics
74- // https://github.com/microsoft/TypeScript/issues/29132
75- return (this as any).findComponent(selector ? appendSelector(selector, rootSelector) : rootSelector, ${ pascalCase (
76- componentName ,
77- ) } Wrapper);
78- };` ;
79- } ) ,
80- `export { createWrapper as default } from '@cloudscape-design/test-utils-core/${ testUtilType } ';` ,
122+ generateFindersInterfaces ( { testUtilMetaData, testUtilType, configs } ) ,
123+ generateFindersImplementations ( { testUtilMetaData, configs } ) ,
124+ config . defaultExport ,
81125 ] . join ( "\n" ) ;
82126}
83127
128+ function generateTestUtilMetaData ( ) {
129+ const metaData = components . reduce ( ( allMetaData , componentFolderName ) => {
130+ const absPathComponentFolder = path . resolve ( testUtilsSrcDir , componentFolderName ) ;
131+ const relPathtestUtilFile = `./${ path . relative ( testUtilsSrcDir , absPathComponentFolder ) } ` ;
132+
133+ const componentNameKebab = componentFolderName ;
134+ const componentName = pascalCase ( componentNameKebab ) ;
135+ const componentNamePlural = pluralizeComponentName ( componentName ) ;
136+
137+ const componentMetaData = {
138+ componentName,
139+ componentNamePlural,
140+ relPathtestUtilFile,
141+ } ;
142+
143+ return allMetaData . concat ( componentMetaData ) ;
144+ } , [ ] ) ;
145+
146+ return metaData ;
147+ }
148+
84149function compileTypescript ( ) {
85150 const config = path . resolve ( "src/test-utils/tsconfig.json" ) ;
86151 execaSync ( "tsc" , [ "-p" , config , "--sourceMap" , "--inlineSources" ] , { stdio : "inherit" } ) ;
87152}
153+
154+ function generateSelectorUtils ( ) {
155+ components . forEach ( ( componentName ) => {
156+ const domFileName = `./src/test-utils/dom/${ componentName } /index.ts` ;
157+ const domFileContent = fs . readFileSync ( domFileName , "utf-8" ) ;
158+ const selectorsFileName = `./src/test-utils/selectors/${ componentName } /index.ts` ;
159+ const selectorsFileContent = convertToSelectorUtil . default ( domFileContent ) ;
160+ writeSourceFile ( selectorsFileName , selectorsFileContent ) ;
161+ } ) ;
162+ }
163+
164+ function generateIndexFile ( testUtilType ) {
165+ const testUtilMetaData = generateTestUtilMetaData ( testUtilType ) ;
166+ const content = generateIndexFileContent ( testUtilType , testUtilMetaData ) ;
167+ writeSourceFile ( `./src/test-utils/${ testUtilType } /index.ts` , content ) ;
168+ }
169+
170+ generateSelectorUtils ( ) ;
171+ generateIndexFile ( "dom" ) ;
172+ generateIndexFile ( "selectors" ) ;
173+ compileTypescript ( ) ;
0 commit comments