1
1
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
// SPDX-License-Identifier: Apache-2.0
3
+ import { execaSync } from "execa" ;
4
+ import { globbySync } from "globby" ;
3
5
import fs from "node:fs" ;
4
6
import path from "node:path" ;
7
+
5
8
import { default as convertToSelectorUtil } from "@cloudscape-design/test-utils-converter" ;
6
- import { execaSync } from "execa" ;
7
- import { globbySync } from "globby " ;
9
+
10
+ import { pluralizeComponentName } from "./pluralize.js " ;
8
11
import { pascalCase , writeSourceFile } from "./utils.js" ;
9
12
10
13
const components = globbySync ( [ "src/test-utils/dom/**/index.ts" , "!src/test-utils/dom/index.ts" ] ) . map ( ( fileName ) =>
11
14
fileName . replace ( "src/test-utils/dom/" , "" ) . replace ( "/index.ts" , "" ) ,
12
15
) ;
13
16
17
+ function toWrapper ( componentClass ) {
18
+ return `${ componentClass } Wrapper` ;
19
+ }
20
+
21
+ const configs = {
22
+ common : {
23
+ buildFinder : ( { componentName, componentNamePlural } ) => `
24
+ ElementWrapper.prototype.find${ componentName } = function(selector) {
25
+ const rootSelector = \`.$\{${ toWrapper ( componentName ) } .rootSelector}\`;
26
+ // casting to 'any' is needed to avoid this issue with generics
27
+ // https://github.com/microsoft/TypeScript/issues/29132
28
+ return (this as any).findComponent(selector ? appendSelector(selector, rootSelector) : rootSelector, ${ toWrapper ( componentName ) } );
29
+ };
30
+
31
+ ElementWrapper.prototype.findAll${ componentNamePlural } = function(selector) {
32
+ return this.findAllComponents(${ toWrapper ( componentName ) } , selector);
33
+ };` ,
34
+ } ,
35
+ dom : {
36
+ 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); }` ,
37
+ buildFinderInterface : ( { componentName, componentNamePlural } ) => `
38
+ /**
39
+ * Returns the wrapper of the first ${ componentName } that matches the specified CSS selector.
40
+ * If no CSS selector is specified, returns the wrapper of the first ${ componentName } .
41
+ * If no matching ${ componentName } is found, returns \`null\`.
42
+ *
43
+ * @param {string} [selector] CSS Selector
44
+ * @returns {${ toWrapper ( componentName ) } | null}
45
+ */
46
+ find${ componentName } (selector?: string): ${ toWrapper ( componentName ) } | null;
47
+
48
+ /**
49
+ * Returns an array of ${ componentName } wrapper that matches the specified CSS selector.
50
+ * If no CSS selector is specified, returns all of the ${ componentNamePlural } inside the current wrapper.
51
+ * If no matching ${ componentName } is found, returns an empty array.
52
+ *
53
+ * @param {string} [selector] CSS Selector
54
+ * @returns {Array<${ toWrapper ( componentName ) } >}
55
+ */
56
+ findAll${ componentNamePlural } (selector?: string): Array<${ toWrapper ( componentName ) } >;` ,
57
+ } ,
58
+ selectors : {
59
+ defaultExport : `export default function wrapper(root: string = 'body') { return new ElementWrapper(root); }` ,
60
+ buildFinderInterface : ( { componentName, componentNamePlural } ) => `
61
+ /**
62
+ * Returns a wrapper that matches the ${ componentNamePlural } with the specified CSS selector.
63
+ * If no CSS selector is specified, returns a wrapper that matches ${ componentNamePlural } .
64
+ *
65
+ * @param {string} [selector] CSS Selector
66
+ * @returns {${ toWrapper ( componentName ) } }
67
+ */
68
+ find${ componentName } (selector?: string): ${ toWrapper ( componentName ) } ;
69
+
70
+ /**
71
+ * Returns a multi-element wrapper that matches ${ componentNamePlural } with the specified CSS selector.
72
+ * If no CSS selector is specified, returns a multi-element wrapper that matches ${ componentNamePlural } .
73
+ *
74
+ * @param {string} [selector] CSS Selector
75
+ * @returns {MultiElementWrapper<${ toWrapper ( componentName ) } >}
76
+ */
77
+ findAll${ componentNamePlural } (selector?: string): MultiElementWrapper<${ toWrapper ( componentName ) } >;` ,
78
+ } ,
79
+ } ;
80
+
81
+ function generateTestUtilMetaData ( ) {
82
+ const testUtilsSrcDir = path . resolve ( "src/test-utils" ) ;
83
+ const metaData = components . reduce ( ( allMetaData , componentFolderName ) => {
84
+ const absPathComponentFolder = path . resolve ( testUtilsSrcDir , componentFolderName ) ;
85
+ const relPathTestUtilFile = `./${ path . relative ( testUtilsSrcDir , absPathComponentFolder ) } ` ;
86
+
87
+ const componentNameKebab = componentFolderName ;
88
+ const componentName = pascalCase ( componentNameKebab ) ;
89
+ const componentNamePlural = pluralizeComponentName ( componentName ) ;
90
+
91
+ const componentMetaData = {
92
+ componentName,
93
+ componentNamePlural,
94
+ relPathTestUtilFile,
95
+ } ;
96
+
97
+ return allMetaData . concat ( componentMetaData ) ;
98
+ } , [ ] ) ;
99
+
100
+ return metaData ;
101
+ }
102
+
103
+ function generateFindersInterfaces ( { testUtilMetaData, testUtilType, configs } ) {
104
+ const { buildFinderInterface } = configs [ testUtilType ] ;
105
+ const findersInterfaces = testUtilMetaData . map ( buildFinderInterface ) ;
106
+
107
+ // we need to redeclare the interface in its original definition, extending a re-export will not work
108
+ // https://github.com/microsoft/TypeScript/issues/12607
109
+ const interfaces = `declare module '@cloudscape-design/test-utils-core/dist/${ testUtilType } ' {
110
+ interface ElementWrapper {
111
+ ${ findersInterfaces . join ( "\n" ) }
112
+ }
113
+ }` ;
114
+
115
+ return interfaces ;
116
+ }
117
+
118
+ function generateFindersImplementations ( { testUtilMetaData, configs } ) {
119
+ const { buildFinder } = configs . common ;
120
+ const findersImplementations = testUtilMetaData . map ( buildFinder ) ;
121
+ return findersImplementations . join ( "\n" ) ;
122
+ }
123
+
14
124
generateSelectorUtils ( ) ;
15
125
generateDomIndexFile ( ) ;
16
126
generateSelectorsIndexFile ( ) ;
@@ -29,57 +139,46 @@ function generateSelectorUtils() {
29
139
function generateDomIndexFile ( ) {
30
140
const content = generateIndexFileContent ( {
31
141
testUtilType : "dom" ,
32
- buildFinderInterface : ( componentName ) =>
33
- `find${ pascalCase ( componentName ) } (selector?: string): ${ pascalCase ( componentName ) } Wrapper | null;` ,
142
+ testUtilMetaData : generateTestUtilMetaData ( ) ,
34
143
} ) ;
35
144
writeSourceFile ( "./src/test-utils/dom/index.ts" , content ) ;
36
145
}
37
146
38
147
function generateSelectorsIndexFile ( ) {
39
148
const content = generateIndexFileContent ( {
40
149
testUtilType : "selectors" ,
41
- buildFinderInterface : ( componentName ) =>
42
- `find${ pascalCase ( componentName ) } (selector?: string): ${ pascalCase ( componentName ) } Wrapper;` ,
150
+ testUtilMetaData : generateTestUtilMetaData ( ) ,
43
151
} ) ;
44
152
writeSourceFile ( "./src/test-utils/selectors/index.ts" , content ) ;
45
153
}
46
154
47
- function generateIndexFileContent ( { testUtilType, buildFinderInterface } ) {
155
+ function generateIndexFileContent ( { testUtilType, testUtilMetaData } ) {
156
+ const config = configs [ testUtilType ] ;
157
+ if ( config === undefined ) {
158
+ throw new Error ( "Unknown test util type" ) ;
159
+ }
160
+
48
161
return [
49
162
// language=TypeScript
50
163
`import { ElementWrapper } from '@cloudscape-design/test-utils-core/${ testUtilType } ';` ,
164
+ `import '@cloudscape-design/components/test-utils/${ testUtilType } ';` ,
51
165
`import { appendSelector } from '@cloudscape-design/test-utils-core/utils';` ,
52
166
`export { ElementWrapper };` ,
53
- ...components . map ( ( componentName ) => {
54
- const componentImport = `./${ componentName } /index` ;
167
+ ...testUtilMetaData . map ( ( metaData ) => {
168
+ const { componentName, relPathTestUtilFile } = metaData ;
169
+
55
170
return `
56
- import ${ pascalCase ( componentName ) } Wrapper from '${ componentImport } ';
57
- export { ${ pascalCase ( componentName ) } Wrapper };
171
+ import ${ toWrapper ( componentName ) } from '${ relPathTestUtilFile } ';
172
+ export { ${ componentName } Wrapper };
58
173
` ;
59
174
} ) ,
60
- // we need to redeclare the interface in its original definition, extending a re-export will not work
61
- // https://github.com/microsoft/TypeScript/issues/12607
62
- `declare module '@cloudscape-design/test-utils-core/dist/${ testUtilType } ' {
63
- interface ElementWrapper {
64
- ${ components . map ( ( componentName ) => buildFinderInterface ( componentName ) ) . join ( "\n" ) }
65
- }
66
- }` ,
67
- ...components . map ( ( componentName ) => {
68
- // language=TypeScript
69
- return `ElementWrapper.prototype.find${ pascalCase ( componentName ) } = function(selector) {
70
- const rootSelector = \`.$\{${ pascalCase ( componentName ) } Wrapper.rootSelector}\`;
71
- // casting to 'any' is needed to avoid this issue with generics
72
- // https://github.com/microsoft/TypeScript/issues/29132
73
- return (this as any).findComponent(selector ? appendSelector(selector, rootSelector) : rootSelector, ${ pascalCase (
74
- componentName ,
75
- ) } Wrapper);
76
- };` ;
77
- } ) ,
78
- `export { createWrapper as default } from '@cloudscape-design/test-utils-core/${ testUtilType } ';` ,
175
+ generateFindersInterfaces ( { testUtilMetaData, testUtilType, configs } ) ,
176
+ generateFindersImplementations ( { testUtilMetaData, configs } ) ,
177
+ config . defaultExport ,
79
178
] . join ( "\n" ) ;
80
179
}
81
180
82
181
function compileTypescript ( ) {
83
182
const config = path . resolve ( "src/test-utils/tsconfig.json" ) ;
84
- execaSync ( "tsc" , [ "-p" , config , "--sourceMap" ] , { stdio : "inherit" } ) ;
183
+ execaSync ( "tsc" , [ "-p" , config , "--sourceMap" , "--inlineSources" ] , { stdio : "inherit" } ) ;
85
184
}
0 commit comments