|
2 | 2 | // SPDX-License-Identifier: Apache-2.0 |
3 | 3 | import { execaSync } from "execa"; |
4 | 4 | import { globbySync } from "globby"; |
5 | | -import fs from "node:fs"; |
6 | 5 | import path from "node:path"; |
7 | 6 |
|
8 | | -import { default as convertToSelectorUtil } from "@cloudscape-design/test-utils-converter"; |
| 7 | +import { generateTestUtils } from "@cloudscape-design/test-utils-converter"; |
9 | 8 |
|
10 | 9 | import { pluralizeComponentName } from "./pluralize.js"; |
11 | | -import { pascalCase, writeSourceFile } from "./utils.js"; |
| 10 | +import { pascalCase } from "./utils.js"; |
12 | 11 |
|
13 | | -const components = globbySync(["src/test-utils/dom/**/index.ts", "!src/test-utils/dom/index.ts"]).map((fileName) => |
14 | | - fileName.replace("src/test-utils/dom/", "").replace("/index.ts", ""), |
15 | | -); |
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)}>;`, |
| 12 | +const componentNames = globbySync(["src/test-utils/dom/**/index.ts", "!src/test-utils/dom/index.ts"]).map( |
| 13 | + (filePath) => { |
| 14 | + const fileNameKebabCase = filePath.replace("src/test-utils/dom/", "").replace("/index.ts", ""); |
| 15 | + return pascalCase(fileNameKebabCase); |
78 | 16 | }, |
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 | | - |
124 | | -generateSelectorUtils(); |
125 | | -generateDomIndexFile(); |
126 | | -generateSelectorsIndexFile(); |
127 | | -compileTypescript(); |
128 | | - |
129 | | -function generateSelectorUtils() { |
130 | | - components.forEach((componentName) => { |
131 | | - const domFileName = `./src/test-utils/dom/${componentName}/index.ts`; |
132 | | - const domFileContent = fs.readFileSync(domFileName, "utf-8"); |
133 | | - const selectorsFileName = `./src/test-utils/selectors/${componentName}/index.ts`; |
134 | | - const selectorsFileContent = convertToSelectorUtil.default(domFileContent); |
135 | | - writeSourceFile(selectorsFileName, selectorsFileContent); |
136 | | - }); |
137 | | -} |
138 | | - |
139 | | -function generateDomIndexFile() { |
140 | | - const content = generateIndexFileContent({ |
141 | | - testUtilType: "dom", |
142 | | - testUtilMetaData: generateTestUtilMetaData(), |
143 | | - }); |
144 | | - writeSourceFile("./src/test-utils/dom/index.ts", content); |
145 | | -} |
146 | | - |
147 | | -function generateSelectorsIndexFile() { |
148 | | - const content = generateIndexFileContent({ |
149 | | - testUtilType: "selectors", |
150 | | - testUtilMetaData: generateTestUtilMetaData(), |
151 | | - }); |
152 | | - writeSourceFile("./src/test-utils/selectors/index.ts", content); |
153 | | -} |
154 | | - |
155 | | -function generateIndexFileContent({ testUtilType, testUtilMetaData }) { |
156 | | - const config = configs[testUtilType]; |
157 | | - if (config === undefined) { |
158 | | - throw new Error("Unknown test util type"); |
159 | | - } |
160 | | - |
161 | | - return [ |
162 | | - // language=TypeScript |
163 | | - `import { ElementWrapper } from '@cloudscape-design/test-utils-core/${testUtilType}';`, |
164 | | - `import '@cloudscape-design/components/test-utils/${testUtilType}';`, |
165 | | - `import { appendSelector } from '@cloudscape-design/test-utils-core/utils';`, |
166 | | - `export { ElementWrapper };`, |
167 | | - ...testUtilMetaData.map((metaData) => { |
168 | | - const { componentName, relPathTestUtilFile } = metaData; |
| 17 | +); |
169 | 18 |
|
170 | | - return ` |
171 | | - import ${toWrapper(componentName)} from '${relPathTestUtilFile}'; |
172 | | - export { ${componentName}Wrapper }; |
173 | | - `; |
174 | | - }), |
175 | | - generateFindersInterfaces({ testUtilMetaData, testUtilType, configs }), |
176 | | - generateFindersImplementations({ testUtilMetaData, configs }), |
177 | | - config.defaultExport, |
178 | | - ].join("\n"); |
179 | | -} |
| 19 | +generateTestUtils({ |
| 20 | + testUtilsPath: path.resolve("src/test-utils"), |
| 21 | + components: componentNames.map((name) => ({ |
| 22 | + name, |
| 23 | + pluralName: pluralizeComponentName(name), |
| 24 | + })), |
| 25 | +}); |
180 | 26 |
|
181 | 27 | function compileTypescript() { |
182 | 28 | const config = path.resolve("src/test-utils/tsconfig.json"); |
183 | 29 | execaSync("tsc", ["-p", config, "--sourceMap", "--inlineSources"], { stdio: "inherit" }); |
184 | 30 | } |
| 31 | + |
| 32 | +compileTypescript(); |
0 commit comments