@playwright/experimental-ct-core 1.41.0 → 1.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/generated/indexSource.js +7 -0
- package/lib/injected/importRegistry.js +46 -0
- package/lib/injected/index.js +23 -0
- package/lib/injected/packed/index.js +89 -0
- package/lib/injected/serializers.js +87 -0
- package/lib/mount.js +19 -57
- package/lib/tsxTransform.js +74 -115
- package/lib/vitePlugin.js +41 -73
- package/package.json +3 -3
- package/types/component.d.ts +18 -14
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.source = void 0;
|
|
7
|
+
const source = exports.source = "\"use strict\";\n\n// packages/playwright-ct-core/src/injected/importRegistry.ts\nfunction isImportRef(value) {\n return typeof value === \"object\" && value && value.__pw_type === \"importRef\";\n}\nvar ImportRegistry = class {\n constructor() {\n this._registry = /* @__PURE__ */ new Map();\n }\n initialize(components) {\n for (const [name, value] of Object.entries(components))\n this._registry.set(name, value);\n }\n async resolveImportRef(importRef) {\n const importFunction = this._registry.get(importRef.id);\n if (!importFunction)\n throw new Error(`Unregistered component: ${importRef.id}. Following components are registered: ${[...this._registry.keys()]}`);\n let importedObject = await importFunction();\n if (!importedObject)\n throw new Error(`Could not resolve component: ${importRef.id}.`);\n if (importRef.property) {\n importedObject = importedObject[importRef.property];\n if (!importedObject)\n throw new Error(`Could not instantiate component: ${importRef.id}.${importRef.property}.`);\n }\n return importedObject;\n }\n};\n\n// packages/playwright-ct-core/src/injected/serializers.ts\nfunction isFunctionRef(value) {\n return value && typeof value === \"object\" && value.__pw_type === \"function\";\n}\nasync function unwrapObject(value) {\n return transformObjectAsync(value, async (v) => {\n if (isFunctionRef(v)) {\n const result = (...args) => {\n window.__ctDispatchFunction(v.ordinal, args);\n };\n return { result };\n }\n if (isImportRef(v))\n return { result: await window.__pwRegistry.resolveImportRef(v) };\n });\n}\nfunction transformObject(value, mapping) {\n const result = mapping(value);\n if (result)\n return result.result;\n if (value === null || typeof value !== \"object\")\n return value;\n if (value instanceof Date || value instanceof RegExp || value instanceof URL)\n return value;\n if (Array.isArray(value)) {\n const result3 = [];\n for (const item of value)\n result3.push(transformObject(item, mapping));\n return result3;\n }\n const result2 = {};\n for (const [key, prop] of Object.entries(value))\n result2[key] = transformObject(prop, mapping);\n return result2;\n}\nasync function transformObjectAsync(value, mapping) {\n const result = await mapping(value);\n if (result)\n return result.result;\n if (value === null || typeof value !== \"object\")\n return value;\n if (value instanceof Date || value instanceof RegExp || value instanceof URL)\n return value;\n if (Array.isArray(value)) {\n const result3 = [];\n for (const item of value)\n result3.push(await transformObjectAsync(item, mapping));\n return result3;\n }\n const result2 = {};\n for (const [key, prop] of Object.entries(value))\n result2[key] = await transformObjectAsync(prop, mapping);\n return result2;\n}\n\n// packages/playwright-ct-core/src/injected/index.ts\nwindow.__pwRegistry = new ImportRegistry();\nwindow.__pwUnwrapObject = unwrapObject;\nwindow.__pwTransformObject = transformObject;\n";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ImportRegistry = void 0;
|
|
7
|
+
exports.isImportRef = isImportRef;
|
|
8
|
+
/**
|
|
9
|
+
* Copyright (c) Microsoft Corporation.
|
|
10
|
+
*
|
|
11
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
+
* you may not use this file except in compliance with the License.
|
|
13
|
+
* You may obtain a copy of the License at
|
|
14
|
+
*
|
|
15
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
+
*
|
|
17
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
+
* See the License for the specific language governing permissions and
|
|
21
|
+
* limitations under the License.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
function isImportRef(value) {
|
|
25
|
+
return typeof value === 'object' && value && value.__pw_type === 'importRef';
|
|
26
|
+
}
|
|
27
|
+
class ImportRegistry {
|
|
28
|
+
constructor() {
|
|
29
|
+
this._registry = new Map();
|
|
30
|
+
}
|
|
31
|
+
initialize(components) {
|
|
32
|
+
for (const [name, value] of Object.entries(components)) this._registry.set(name, value);
|
|
33
|
+
}
|
|
34
|
+
async resolveImportRef(importRef) {
|
|
35
|
+
const importFunction = this._registry.get(importRef.id);
|
|
36
|
+
if (!importFunction) throw new Error(`Unregistered component: ${importRef.id}. Following components are registered: ${[...this._registry.keys()]}`);
|
|
37
|
+
let importedObject = await importFunction();
|
|
38
|
+
if (!importedObject) throw new Error(`Could not resolve component: ${importRef.id}.`);
|
|
39
|
+
if (importRef.property) {
|
|
40
|
+
importedObject = importedObject[importRef.property];
|
|
41
|
+
if (!importedObject) throw new Error(`Could not instantiate component: ${importRef.id}.${importRef.property}.`);
|
|
42
|
+
}
|
|
43
|
+
return importedObject;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.ImportRegistry = ImportRegistry;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _importRegistry = require("./importRegistry");
|
|
4
|
+
var _serializers = require("./serializers");
|
|
5
|
+
/**
|
|
6
|
+
* Copyright (c) Microsoft Corporation.
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
* you may not use this file except in compliance with the License.
|
|
10
|
+
* You may obtain a copy of the License at
|
|
11
|
+
*
|
|
12
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
*
|
|
14
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
* See the License for the specific language governing permissions and
|
|
18
|
+
* limitations under the License.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
window.__pwRegistry = new _importRegistry.ImportRegistry();
|
|
22
|
+
window.__pwUnwrapObject = _serializers.unwrapObject;
|
|
23
|
+
window.__pwTransformObject = _serializers.transformObject;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// packages/playwright-ct-core/src/injected/importRegistry.ts
|
|
4
|
+
function isImportRef(value) {
|
|
5
|
+
return typeof value === "object" && value && value.__pw_type === "importRef";
|
|
6
|
+
}
|
|
7
|
+
var ImportRegistry = class {
|
|
8
|
+
constructor() {
|
|
9
|
+
this._registry = /* @__PURE__ */ new Map();
|
|
10
|
+
}
|
|
11
|
+
initialize(components) {
|
|
12
|
+
for (const [name, value] of Object.entries(components))
|
|
13
|
+
this._registry.set(name, value);
|
|
14
|
+
}
|
|
15
|
+
async resolveImportRef(importRef) {
|
|
16
|
+
const importFunction = this._registry.get(importRef.id);
|
|
17
|
+
if (!importFunction)
|
|
18
|
+
throw new Error(`Unregistered component: ${importRef.id}. Following components are registered: ${[...this._registry.keys()]}`);
|
|
19
|
+
let importedObject = await importFunction();
|
|
20
|
+
if (!importedObject)
|
|
21
|
+
throw new Error(`Could not resolve component: ${importRef.id}.`);
|
|
22
|
+
if (importRef.property) {
|
|
23
|
+
importedObject = importedObject[importRef.property];
|
|
24
|
+
if (!importedObject)
|
|
25
|
+
throw new Error(`Could not instantiate component: ${importRef.id}.${importRef.property}.`);
|
|
26
|
+
}
|
|
27
|
+
return importedObject;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// packages/playwright-ct-core/src/injected/serializers.ts
|
|
32
|
+
function isFunctionRef(value) {
|
|
33
|
+
return value && typeof value === "object" && value.__pw_type === "function";
|
|
34
|
+
}
|
|
35
|
+
async function unwrapObject(value) {
|
|
36
|
+
return transformObjectAsync(value, async (v) => {
|
|
37
|
+
if (isFunctionRef(v)) {
|
|
38
|
+
const result = (...args) => {
|
|
39
|
+
window.__ctDispatchFunction(v.ordinal, args);
|
|
40
|
+
};
|
|
41
|
+
return { result };
|
|
42
|
+
}
|
|
43
|
+
if (isImportRef(v))
|
|
44
|
+
return { result: await window.__pwRegistry.resolveImportRef(v) };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function transformObject(value, mapping) {
|
|
48
|
+
const result = mapping(value);
|
|
49
|
+
if (result)
|
|
50
|
+
return result.result;
|
|
51
|
+
if (value === null || typeof value !== "object")
|
|
52
|
+
return value;
|
|
53
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof URL)
|
|
54
|
+
return value;
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
const result3 = [];
|
|
57
|
+
for (const item of value)
|
|
58
|
+
result3.push(transformObject(item, mapping));
|
|
59
|
+
return result3;
|
|
60
|
+
}
|
|
61
|
+
const result2 = {};
|
|
62
|
+
for (const [key, prop] of Object.entries(value))
|
|
63
|
+
result2[key] = transformObject(prop, mapping);
|
|
64
|
+
return result2;
|
|
65
|
+
}
|
|
66
|
+
async function transformObjectAsync(value, mapping) {
|
|
67
|
+
const result = await mapping(value);
|
|
68
|
+
if (result)
|
|
69
|
+
return result.result;
|
|
70
|
+
if (value === null || typeof value !== "object")
|
|
71
|
+
return value;
|
|
72
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof URL)
|
|
73
|
+
return value;
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
const result3 = [];
|
|
76
|
+
for (const item of value)
|
|
77
|
+
result3.push(await transformObjectAsync(item, mapping));
|
|
78
|
+
return result3;
|
|
79
|
+
}
|
|
80
|
+
const result2 = {};
|
|
81
|
+
for (const [key, prop] of Object.entries(value))
|
|
82
|
+
result2[key] = await transformObjectAsync(prop, mapping);
|
|
83
|
+
return result2;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// packages/playwright-ct-core/src/injected/index.ts
|
|
87
|
+
window.__pwRegistry = new ImportRegistry();
|
|
88
|
+
window.__pwUnwrapObject = unwrapObject;
|
|
89
|
+
window.__pwTransformObject = transformObject;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.transformObject = transformObject;
|
|
7
|
+
exports.transformObjectAsync = transformObjectAsync;
|
|
8
|
+
exports.unwrapObject = unwrapObject;
|
|
9
|
+
exports.wrapObject = wrapObject;
|
|
10
|
+
var _importRegistry = require("./importRegistry");
|
|
11
|
+
/**
|
|
12
|
+
* Copyright (c) Microsoft Corporation.
|
|
13
|
+
*
|
|
14
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
15
|
+
* you may not use this file except in compliance with the License.
|
|
16
|
+
* You may obtain a copy of the License at
|
|
17
|
+
*
|
|
18
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
19
|
+
*
|
|
20
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
21
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
22
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
23
|
+
* See the License for the specific language governing permissions and
|
|
24
|
+
* limitations under the License.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
function isFunctionRef(value) {
|
|
28
|
+
return value && typeof value === 'object' && value.__pw_type === 'function';
|
|
29
|
+
}
|
|
30
|
+
function wrapObject(value, callbacks) {
|
|
31
|
+
return transformObject(value, v => {
|
|
32
|
+
if (typeof v === 'function') {
|
|
33
|
+
const ordinal = callbacks.length;
|
|
34
|
+
callbacks.push(v);
|
|
35
|
+
const result = {
|
|
36
|
+
__pw_type: 'function',
|
|
37
|
+
ordinal
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
result
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function unwrapObject(value) {
|
|
46
|
+
return transformObjectAsync(value, async v => {
|
|
47
|
+
if (isFunctionRef(v)) {
|
|
48
|
+
const result = (...args) => {
|
|
49
|
+
window.__ctDispatchFunction(v.ordinal, args);
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
result
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if ((0, _importRegistry.isImportRef)(v)) return {
|
|
56
|
+
result: await window.__pwRegistry.resolveImportRef(v)
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function transformObject(value, mapping) {
|
|
61
|
+
const result = mapping(value);
|
|
62
|
+
if (result) return result.result;
|
|
63
|
+
if (value === null || typeof value !== 'object') return value;
|
|
64
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof URL) return value;
|
|
65
|
+
if (Array.isArray(value)) {
|
|
66
|
+
const result = [];
|
|
67
|
+
for (const item of value) result.push(transformObject(item, mapping));
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
const result2 = {};
|
|
71
|
+
for (const [key, prop] of Object.entries(value)) result2[key] = transformObject(prop, mapping);
|
|
72
|
+
return result2;
|
|
73
|
+
}
|
|
74
|
+
async function transformObjectAsync(value, mapping) {
|
|
75
|
+
const result = await mapping(value);
|
|
76
|
+
if (result) return result.result;
|
|
77
|
+
if (value === null || typeof value !== 'object') return value;
|
|
78
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof URL) return value;
|
|
79
|
+
if (Array.isArray(value)) {
|
|
80
|
+
const result = [];
|
|
81
|
+
for (const item of value) result.push(await transformObjectAsync(item, mapping));
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
const result2 = {};
|
|
85
|
+
for (const [key, prop] of Object.entries(value)) result2[key] = await transformObjectAsync(prop, mapping);
|
|
86
|
+
return result2;
|
|
87
|
+
}
|
package/lib/mount.js
CHANGED
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.fixtures = void 0;
|
|
7
|
+
var _serializers = require("./injected/serializers");
|
|
7
8
|
/**
|
|
8
9
|
* Copyright (c) Microsoft Corporation.
|
|
9
10
|
*
|
|
@@ -35,7 +36,7 @@ const fixtures = exports.fixtures = {
|
|
|
35
36
|
}, use, info) => {
|
|
36
37
|
if (!info._configInternal.defineConfigWasUsed) throw new Error('Component testing requires the use of the defineConfig() in your playwright-ct.config.{ts,js}: https://aka.ms/playwright/ct-define-config');
|
|
37
38
|
await page._wrapApiCall(async () => {
|
|
38
|
-
await page.exposeFunction('
|
|
39
|
+
await page.exposeFunction('__ctDispatchFunction', (ordinal, args) => {
|
|
39
40
|
boundCallbacksForMount[ordinal](...args);
|
|
40
41
|
});
|
|
41
42
|
await page.goto(process.env.PLAYWRIGHT_TEST_BASE_URL);
|
|
@@ -45,9 +46,9 @@ const fixtures = exports.fixtures = {
|
|
|
45
46
|
mount: async ({
|
|
46
47
|
page
|
|
47
48
|
}, use) => {
|
|
48
|
-
await use(async (
|
|
49
|
+
await use(async (componentRef, options) => {
|
|
49
50
|
const selector = await page._wrapApiCall(async () => {
|
|
50
|
-
return await innerMount(page,
|
|
51
|
+
return await innerMount(page, componentRef, options);
|
|
51
52
|
}, true);
|
|
52
53
|
const locator = page.locator(selector);
|
|
53
54
|
return Object.assign(locator, {
|
|
@@ -58,45 +59,31 @@ const fixtures = exports.fixtures = {
|
|
|
58
59
|
});
|
|
59
60
|
},
|
|
60
61
|
update: async options => {
|
|
61
|
-
if (
|
|
62
|
-
await innerUpdate(page,
|
|
62
|
+
if (isJsxComponent(options)) return await innerUpdate(page, options);
|
|
63
|
+
await innerUpdate(page, componentRef, options);
|
|
63
64
|
}
|
|
64
65
|
});
|
|
65
66
|
});
|
|
66
67
|
boundCallbacksForMount = [];
|
|
67
68
|
}
|
|
68
69
|
};
|
|
69
|
-
function
|
|
70
|
-
return
|
|
70
|
+
function isJsxComponent(component) {
|
|
71
|
+
return typeof component === 'object' && component && component.__pw_type === 'jsx';
|
|
71
72
|
}
|
|
72
|
-
async function innerUpdate(page,
|
|
73
|
-
const component = createComponent(
|
|
74
|
-
wrapFunctions(component, page, boundCallbacksForMount);
|
|
73
|
+
async function innerUpdate(page, componentRef, options = {}) {
|
|
74
|
+
const component = (0, _serializers.wrapObject)(createComponent(componentRef, options), boundCallbacksForMount);
|
|
75
75
|
await page.evaluate(async ({
|
|
76
76
|
component
|
|
77
77
|
}) => {
|
|
78
|
-
|
|
79
|
-
for (const [key, value] of Object.entries(object)) {
|
|
80
|
-
if (typeof value === 'string' && value.startsWith('__pw_func_')) {
|
|
81
|
-
const ordinal = +value.substring('__pw_func_'.length);
|
|
82
|
-
object[key] = (...args) => {
|
|
83
|
-
window['__ct_dispatch'](ordinal, args);
|
|
84
|
-
};
|
|
85
|
-
} else if (typeof value === 'object' && value) {
|
|
86
|
-
unwrapFunctions(value);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
unwrapFunctions(component);
|
|
78
|
+
component = await window.__pwUnwrapObject(component);
|
|
91
79
|
const rootElement = document.getElementById('root');
|
|
92
80
|
return await window.playwrightUpdate(rootElement, component);
|
|
93
81
|
}, {
|
|
94
82
|
component
|
|
95
83
|
});
|
|
96
84
|
}
|
|
97
|
-
async function innerMount(page,
|
|
98
|
-
const component = createComponent(
|
|
99
|
-
wrapFunctions(component, page, boundCallbacksForMount);
|
|
85
|
+
async function innerMount(page, componentRef, options = {}) {
|
|
86
|
+
const component = (0, _serializers.wrapObject)(createComponent(componentRef, options), boundCallbacksForMount);
|
|
100
87
|
|
|
101
88
|
// WebKit does not wait for deferred scripts.
|
|
102
89
|
await page.waitForFunction(() => !!window.playwrightMount);
|
|
@@ -104,19 +91,7 @@ async function innerMount(page, jsxOrType, options = {}) {
|
|
|
104
91
|
component,
|
|
105
92
|
hooksConfig
|
|
106
93
|
}) => {
|
|
107
|
-
|
|
108
|
-
for (const [key, value] of Object.entries(object)) {
|
|
109
|
-
if (typeof value === 'string' && value.startsWith('__pw_func_')) {
|
|
110
|
-
const ordinal = +value.substring('__pw_func_'.length);
|
|
111
|
-
object[key] = (...args) => {
|
|
112
|
-
window['__ct_dispatch'](ordinal, args);
|
|
113
|
-
};
|
|
114
|
-
} else if (typeof value === 'object' && value) {
|
|
115
|
-
unwrapFunctions(value);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
unwrapFunctions(component);
|
|
94
|
+
component = await window.__pwUnwrapObject(component);
|
|
120
95
|
let rootElement = document.getElementById('root');
|
|
121
96
|
if (!rootElement) {
|
|
122
97
|
rootElement = document.createElement('div');
|
|
@@ -131,24 +106,11 @@ async function innerMount(page, jsxOrType, options = {}) {
|
|
|
131
106
|
});
|
|
132
107
|
return selector;
|
|
133
108
|
}
|
|
134
|
-
function createComponent(
|
|
135
|
-
if (
|
|
109
|
+
function createComponent(component, options = {}) {
|
|
110
|
+
if (component.__pw_type === 'jsx') return component;
|
|
136
111
|
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
options
|
|
112
|
+
__pw_type: 'object-component',
|
|
113
|
+
type: component,
|
|
114
|
+
...options
|
|
141
115
|
};
|
|
142
|
-
}
|
|
143
|
-
function wrapFunctions(object, page, callbacks) {
|
|
144
|
-
for (const [key, value] of Object.entries(object)) {
|
|
145
|
-
const type = typeof value;
|
|
146
|
-
if (type === 'function') {
|
|
147
|
-
const functionName = '__pw_func_' + callbacks.length;
|
|
148
|
-
callbacks.push(value);
|
|
149
|
-
object[key] = functionName;
|
|
150
|
-
} else if (type === 'object' && value) {
|
|
151
|
-
wrapFunctions(value, page, callbacks);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
116
|
}
|
package/lib/tsxTransform.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.collectComponentUsages = collectComponentUsages;
|
|
7
|
-
exports.componentInfo = componentInfo;
|
|
8
6
|
exports.default = void 0;
|
|
7
|
+
exports.importInfo = importInfo;
|
|
9
8
|
var _path = _interopRequireDefault(require("path"));
|
|
10
9
|
var _babelBundle = require("playwright/lib/transform/babelBundle");
|
|
11
10
|
var _util = require("playwright/lib/util");
|
|
11
|
+
var _transform = require("playwright/lib/transform/transform");
|
|
12
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
13
|
/**
|
|
14
14
|
* Copyright (c) Microsoft Corporation.
|
|
@@ -27,160 +27,119 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
const t = _babelBundle.types;
|
|
30
|
-
|
|
31
|
-
let
|
|
32
|
-
let componentIdentifiers;
|
|
30
|
+
let jsxComponentNames;
|
|
31
|
+
let importInfos;
|
|
33
32
|
var _default = exports.default = (0, _babelBundle.declare)(api => {
|
|
34
33
|
api.assertVersion(7);
|
|
35
34
|
const result = {
|
|
36
35
|
name: 'playwright-debug-transform',
|
|
37
36
|
visitor: {
|
|
38
|
-
Program
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
Program: {
|
|
38
|
+
enter(path) {
|
|
39
|
+
jsxComponentNames = collectJsxComponentUsages(path.node);
|
|
40
|
+
importInfos = new Map();
|
|
41
|
+
},
|
|
42
|
+
exit(path) {
|
|
43
|
+
let firstDeclaration;
|
|
44
|
+
let lastImportDeclaration;
|
|
45
|
+
path.get('body').forEach(p => {
|
|
46
|
+
if (p.isImportDeclaration()) lastImportDeclaration = p;else if (!firstDeclaration) firstDeclaration = p;
|
|
47
|
+
});
|
|
48
|
+
const insertionPath = lastImportDeclaration || firstDeclaration;
|
|
49
|
+
if (!insertionPath) return;
|
|
50
|
+
for (const [localName, componentImport] of [...importInfos.entries()].reverse()) {
|
|
51
|
+
insertionPath.insertAfter(t.variableDeclaration('const', [t.variableDeclarator(t.identifier(localName), t.objectExpression([t.objectProperty(t.identifier('__pw_type'), t.stringLiteral('importRef')), t.objectProperty(t.identifier('id'), t.stringLiteral(componentImport.id))]))]));
|
|
52
|
+
}
|
|
53
|
+
(0, _transform.setTransformData)('playwright-ct-core', [...importInfos.values()]);
|
|
54
|
+
}
|
|
43
55
|
},
|
|
44
56
|
ImportDeclaration(p) {
|
|
45
57
|
const importNode = p.node;
|
|
46
58
|
if (!t.isStringLiteral(importNode.source)) return;
|
|
47
|
-
|
|
59
|
+
const ext = _path.default.extname(importNode.source.value);
|
|
60
|
+
|
|
61
|
+
// Convert all non-JS imports into refs.
|
|
62
|
+
if (!allJsExtensions.has(ext)) {
|
|
63
|
+
for (const specifier of importNode.specifiers) {
|
|
64
|
+
if (t.isImportNamespaceSpecifier(specifier)) continue;
|
|
65
|
+
const {
|
|
66
|
+
localName,
|
|
67
|
+
info
|
|
68
|
+
} = importInfo(importNode, specifier, this.filename);
|
|
69
|
+
importInfos.set(localName, info);
|
|
70
|
+
}
|
|
71
|
+
p.skip();
|
|
72
|
+
p.remove();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Convert JS imports that are used as components in JSX expressions into refs.
|
|
77
|
+
let importCount = 0;
|
|
48
78
|
for (const specifier of importNode.specifiers) {
|
|
49
|
-
const specifierName = specifier.local.name;
|
|
50
|
-
const componentName = componentNames.has(specifierName) ? specifierName : [...componentNames].find(c => c.startsWith(specifierName + '.'));
|
|
51
|
-
if (!componentName) continue;
|
|
52
79
|
if (t.isImportNamespaceSpecifier(specifier)) continue;
|
|
53
80
|
const {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
81
|
+
localName,
|
|
82
|
+
info
|
|
83
|
+
} = importInfo(importNode, specifier, this.filename);
|
|
84
|
+
if (jsxComponentNames.has(localName)) {
|
|
85
|
+
importInfos.set(localName, info);
|
|
86
|
+
++importCount;
|
|
87
|
+
}
|
|
58
88
|
}
|
|
59
89
|
|
|
60
|
-
// All the imports were
|
|
61
|
-
if (
|
|
90
|
+
// All the imports were from JSX => delete.
|
|
91
|
+
if (importCount && importCount === importNode.specifiers.length) {
|
|
62
92
|
p.skip();
|
|
63
93
|
p.remove();
|
|
64
94
|
}
|
|
65
95
|
},
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
},
|
|
72
|
-
JSXElement(path) {
|
|
73
|
-
const jsxElement = path.node;
|
|
74
|
-
const jsxName = jsxElement.openingElement.name;
|
|
75
|
-
let nameOrExpression = '';
|
|
76
|
-
if (t.isJSXIdentifier(jsxName)) nameOrExpression = jsxName.name;else if (t.isJSXMemberExpression(jsxName) && t.isJSXIdentifier(jsxName.object) && t.isJSXIdentifier(jsxName.property)) nameOrExpression = jsxName.object.name + '.' + jsxName.property.name;
|
|
77
|
-
if (!nameOrExpression) return;
|
|
78
|
-
const componentName = fullNames.get(nameOrExpression) || nameOrExpression;
|
|
79
|
-
const props = [];
|
|
80
|
-
for (const jsxAttribute of jsxElement.openingElement.attributes) {
|
|
81
|
-
if (t.isJSXAttribute(jsxAttribute)) {
|
|
82
|
-
let namespace;
|
|
83
|
-
let name;
|
|
84
|
-
if (t.isJSXNamespacedName(jsxAttribute.name)) {
|
|
85
|
-
namespace = jsxAttribute.name.namespace;
|
|
86
|
-
name = jsxAttribute.name.name;
|
|
87
|
-
} else if (t.isJSXIdentifier(jsxAttribute.name)) {
|
|
88
|
-
name = jsxAttribute.name;
|
|
89
|
-
}
|
|
90
|
-
if (!name) continue;
|
|
91
|
-
const attrName = (namespace ? namespace.name + ':' : '') + name.name;
|
|
92
|
-
if (t.isStringLiteral(jsxAttribute.value)) props.push(t.objectProperty(t.stringLiteral(attrName), jsxAttribute.value));else if (t.isJSXExpressionContainer(jsxAttribute.value) && t.isExpression(jsxAttribute.value.expression)) props.push(t.objectProperty(t.stringLiteral(attrName), jsxAttribute.value.expression));else if (jsxAttribute.value === null) props.push(t.objectProperty(t.stringLiteral(attrName), t.booleanLiteral(true)));else props.push(t.objectProperty(t.stringLiteral(attrName), t.nullLiteral()));
|
|
93
|
-
} else if (t.isJSXSpreadAttribute(jsxAttribute)) {
|
|
94
|
-
props.push(t.spreadElement(jsxAttribute.argument));
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const children = [];
|
|
98
|
-
for (const child of jsxElement.children) {
|
|
99
|
-
if (t.isJSXText(child)) children.push(t.stringLiteral(child.value));else if (t.isJSXElement(child)) children.push(child);else if (t.isJSXExpressionContainer(child) && !t.isJSXEmptyExpression(child.expression)) children.push(child.expression);else if (t.isJSXSpreadChild(child)) children.push(t.spreadElement(child.expression));
|
|
100
|
-
}
|
|
101
|
-
const component = [t.objectProperty(t.identifier('__pw_component_marker'), t.booleanLiteral(true)), t.objectProperty(t.identifier('kind'), t.stringLiteral('jsx')), t.objectProperty(t.identifier('type'), t.stringLiteral(componentName)), t.objectProperty(t.identifier('props'), t.objectExpression(props))];
|
|
102
|
-
if (children.length) component.push(t.objectProperty(t.identifier('children'), t.arrayExpression(children)));
|
|
103
|
-
path.replaceWith(t.objectExpression(component));
|
|
96
|
+
MemberExpression(path) {
|
|
97
|
+
if (!t.isIdentifier(path.node.object)) return;
|
|
98
|
+
if (!importInfos.has(path.node.object.name)) return;
|
|
99
|
+
if (!t.isIdentifier(path.node.property)) return;
|
|
100
|
+
path.replaceWith(t.objectExpression([t.spreadElement(t.identifier(path.node.object.name)), t.objectProperty(t.identifier('property'), t.stringLiteral(path.node.property.name))]));
|
|
104
101
|
}
|
|
105
102
|
}
|
|
106
103
|
};
|
|
107
104
|
return result;
|
|
108
105
|
});
|
|
109
|
-
function
|
|
110
|
-
const importedLocalNames = new Set();
|
|
106
|
+
function collectJsxComponentUsages(node) {
|
|
111
107
|
const names = new Set();
|
|
112
|
-
const identifiers = new Set();
|
|
113
108
|
(0, _babelBundle.traverse)(node, {
|
|
114
109
|
enter: p => {
|
|
115
|
-
// First look at all the imports.
|
|
116
|
-
if (t.isImportDeclaration(p.node)) {
|
|
117
|
-
const importNode = p.node;
|
|
118
|
-
if (!t.isStringLiteral(importNode.source)) return;
|
|
119
|
-
for (const specifier of importNode.specifiers) {
|
|
120
|
-
if (t.isImportNamespaceSpecifier(specifier)) continue;
|
|
121
|
-
importedLocalNames.add(specifier.local.name);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
110
|
// Treat JSX-everything as component usages.
|
|
126
111
|
if (t.isJSXElement(p.node)) {
|
|
127
112
|
if (t.isJSXIdentifier(p.node.openingElement.name)) names.add(p.node.openingElement.name.name);
|
|
128
|
-
if (t.isJSXMemberExpression(p.node.openingElement.name) && t.isJSXIdentifier(p.node.openingElement.name.object) && t.isJSXIdentifier(p.node.openingElement.name.property)) names.add(p.node.openingElement.name.object.name
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Treat mount(identifier, ...) as component usage if it is in the importedLocalNames list.
|
|
132
|
-
if (t.isAwaitExpression(p.node) && t.isCallExpression(p.node.argument) && t.isIdentifier(p.node.argument.callee) && p.node.argument.callee.name === 'mount') {
|
|
133
|
-
const callExpression = p.node.argument;
|
|
134
|
-
const arg = callExpression.arguments[0];
|
|
135
|
-
if (!t.isIdentifier(arg) || !importedLocalNames.has(arg.name)) return;
|
|
136
|
-
names.add(arg.name);
|
|
137
|
-
identifiers.add(arg);
|
|
113
|
+
if (t.isJSXMemberExpression(p.node.openingElement.name) && t.isJSXIdentifier(p.node.openingElement.name.object) && t.isJSXIdentifier(p.node.openingElement.name.property)) names.add(p.node.openingElement.name.object.name);
|
|
138
114
|
}
|
|
139
115
|
}
|
|
140
116
|
});
|
|
141
|
-
return
|
|
142
|
-
names,
|
|
143
|
-
identifiers
|
|
144
|
-
};
|
|
117
|
+
return names;
|
|
145
118
|
}
|
|
146
|
-
function
|
|
119
|
+
function importInfo(importNode, specifier, filename) {
|
|
120
|
+
const importSource = importNode.source.value;
|
|
147
121
|
const isModuleOrAlias = !importSource.startsWith('.');
|
|
148
122
|
const unresolvedImportPath = _path.default.resolve(_path.default.dirname(filename), importSource);
|
|
149
123
|
// Support following notations for Button.tsx:
|
|
150
124
|
// - import { Button } from './Button.js' - via resolveImportSpecifierExtension
|
|
151
125
|
// - import { Button } from './Button' - via require.resolve
|
|
152
126
|
const importPath = isModuleOrAlias ? importSource : (0, _util.resolveImportSpecifierExtension)(unresolvedImportPath) || require.resolve(unresolvedImportPath);
|
|
153
|
-
const
|
|
154
|
-
const
|
|
127
|
+
const idPrefix = importPath.replace(/[^\w_\d]/g, '_');
|
|
128
|
+
const result = {
|
|
129
|
+
id: idPrefix,
|
|
155
130
|
importPath,
|
|
156
|
-
isModuleOrAlias
|
|
131
|
+
isModuleOrAlias,
|
|
132
|
+
remoteName: undefined
|
|
157
133
|
};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const suffix = componentName.substring(specifierName.length + 1);
|
|
163
|
-
fullNameSuffix = '_' + suffix;
|
|
164
|
-
importedNameProperty = '.' + suffix;
|
|
134
|
+
if (t.isImportDefaultSpecifier(specifier)) {} else if (t.isIdentifier(specifier.imported)) {
|
|
135
|
+
result.remoteName = specifier.imported.name;
|
|
136
|
+
} else {
|
|
137
|
+
result.remoteName = specifier.imported.value;
|
|
165
138
|
}
|
|
166
|
-
if (
|
|
167
|
-
fullName: prefix + fullNameSuffix,
|
|
168
|
-
importedNameProperty,
|
|
169
|
-
deps: [],
|
|
170
|
-
...pathInfo
|
|
171
|
-
};
|
|
172
|
-
if (t.isIdentifier(specifier.imported)) return {
|
|
173
|
-
fullName: prefix + '_' + specifier.imported.name + fullNameSuffix,
|
|
174
|
-
importedName: specifier.imported.name,
|
|
175
|
-
importedNameProperty,
|
|
176
|
-
deps: [],
|
|
177
|
-
...pathInfo
|
|
178
|
-
};
|
|
139
|
+
if (result.remoteName) result.id += '_' + result.remoteName;
|
|
179
140
|
return {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
importedNameProperty,
|
|
183
|
-
deps: [],
|
|
184
|
-
...pathInfo
|
|
141
|
+
localName: specifier.local.name,
|
|
142
|
+
info: result
|
|
185
143
|
};
|
|
186
|
-
}
|
|
144
|
+
}
|
|
145
|
+
const allJsExtensions = new Set(['.js', '.jsx', '.cjs', '.mjs', '.ts', '.tsx', '.cts', '.mts', '']);
|
package/lib/vitePlugin.js
CHANGED
|
@@ -7,12 +7,11 @@ exports.createPlugin = createPlugin;
|
|
|
7
7
|
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
8
8
|
var _fs = _interopRequireDefault(require("fs"));
|
|
9
9
|
var _path = _interopRequireDefault(require("path"));
|
|
10
|
-
var _babelBundle = require("playwright/lib/transform/babelBundle");
|
|
11
10
|
var _utilsBundle2 = require("playwright/lib/utilsBundle");
|
|
12
11
|
var _utils = require("playwright-core/lib/utils");
|
|
13
12
|
var _compilationCache = require("playwright/lib/transform/compilationCache");
|
|
14
|
-
var _tsxTransform = require("./tsxTransform");
|
|
15
13
|
var _vite = require("vite");
|
|
14
|
+
var _indexSource = require("./generated/indexSource");
|
|
16
15
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
16
|
/**
|
|
18
17
|
* Copyright (c) Microsoft Corporation.
|
|
@@ -88,7 +87,7 @@ function createPlugin(registerSourceFile, frameworkPluginFactory) {
|
|
|
88
87
|
const buildInfoFile = _path.default.join(baseAndUserConfig.build.outDir, 'metainfo.json');
|
|
89
88
|
let buildExists = false;
|
|
90
89
|
let buildInfo;
|
|
91
|
-
const registerSource = await _fs.default.promises.readFile(registerSourceFile, 'utf-8');
|
|
90
|
+
const registerSource = _indexSource.source + '\n' + (await _fs.default.promises.readFile(registerSourceFile, 'utf-8'));
|
|
92
91
|
const registerSourceHash = (0, _utils.calculateSha1)(registerSource);
|
|
93
92
|
try {
|
|
94
93
|
buildInfo = JSON.parse(await _fs.default.promises.readFile(buildInfoFile, 'utf-8'));
|
|
@@ -102,15 +101,15 @@ function createPlugin(registerSourceFile, frameworkPluginFactory) {
|
|
|
102
101
|
viteVersion: _vite.version,
|
|
103
102
|
registerSourceHash,
|
|
104
103
|
components: [],
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
sources: {},
|
|
105
|
+
deps: {}
|
|
107
106
|
};
|
|
108
107
|
}
|
|
109
108
|
log('build exists:', buildExists);
|
|
110
109
|
const componentRegistry = new Map();
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
const componentsByImportingFile = new Map();
|
|
111
|
+
// 1. Populate component registry based on tests' component imports.
|
|
112
|
+
await populateComponentsFromTests(componentRegistry, componentsByImportingFile);
|
|
114
113
|
|
|
115
114
|
// 2. Check if the set of required components has changed.
|
|
116
115
|
const hasNewComponents = await checkNewComponents(buildInfo, componentRegistry);
|
|
@@ -149,7 +148,8 @@ function createPlugin(registerSourceFile, frameworkPluginFactory) {
|
|
|
149
148
|
if (frameworkPluginFactory && !((_baseAndUserConfig$pl = baseAndUserConfig.plugins) !== null && _baseAndUserConfig$pl !== void 0 && _baseAndUserConfig$pl.length)) frameworkOverrides.plugins = [await frameworkPluginFactory()];
|
|
150
149
|
|
|
151
150
|
// But only add out own plugin when we actually build / transform.
|
|
152
|
-
|
|
151
|
+
const depsCollector = new Map();
|
|
152
|
+
if (sourcesDirty) frameworkOverrides.plugins.push(vitePlugin(registerSource, templateDir, buildInfo, componentRegistry, depsCollector));
|
|
153
153
|
frameworkOverrides.build = {
|
|
154
154
|
target: 'esnext',
|
|
155
155
|
minify: false,
|
|
@@ -166,18 +166,28 @@ function createPlugin(registerSourceFile, frameworkPluginFactory) {
|
|
|
166
166
|
log('build');
|
|
167
167
|
await (0, _vite.build)(finalConfig);
|
|
168
168
|
await _fs.default.promises.rename(`${finalConfig.build.outDir}/${relativeTemplateDir}/index.html`, `${finalConfig.build.outDir}/index.html`);
|
|
169
|
+
buildInfo.deps = Object.fromEntries(depsCollector.entries());
|
|
169
170
|
}
|
|
170
|
-
if (
|
|
171
|
+
if (hasNewComponents || sourcesDirty) {
|
|
171
172
|
log('write manifest');
|
|
172
173
|
await _fs.default.promises.writeFile(buildInfoFile, JSON.stringify(buildInfo, undefined, 2));
|
|
173
174
|
}
|
|
174
|
-
for (const
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
175
|
+
for (const projectSuite of suite.suites) {
|
|
176
|
+
for (const fileSuite of projectSuite.suites) {
|
|
177
|
+
// For every test file...
|
|
178
|
+
const testFile = fileSuite.location.file;
|
|
179
|
+
const deps = new Set();
|
|
180
|
+
// Collect its JS dependencies (helpers).
|
|
181
|
+
for (const file of [testFile, ...((0, _compilationCache.internalDependenciesForTestFile)(testFile) || [])]) {
|
|
182
|
+
// For each helper, get all the imported components.
|
|
183
|
+
for (const componentFile of componentsByImportingFile.get(file) || []) {
|
|
184
|
+
// For each component, get all the dependencies.
|
|
185
|
+
for (const d of depsCollector.get(componentFile) || []) deps.add(d);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Now we have test file => all components along with dependencies.
|
|
189
|
+
(0, _compilationCache.setExternalDependencies)(testFile, [...deps]);
|
|
179
190
|
}
|
|
180
|
-
(0, _compilationCache.setExternalDependencies)(filename, [...deps]);
|
|
181
191
|
}
|
|
182
192
|
const previewServer = await (0, _vite.preview)(finalConfig);
|
|
183
193
|
stoppableServer = (0, _utilsBundle2.stoppable)(previewServer.httpServer, 0);
|
|
@@ -208,31 +218,16 @@ async function checkSources(buildInfo) {
|
|
|
208
218
|
}
|
|
209
219
|
return false;
|
|
210
220
|
}
|
|
211
|
-
async function
|
|
212
|
-
const
|
|
213
|
-
for (const
|
|
214
|
-
for (const
|
|
215
|
-
|
|
216
|
-
let hasNewTests = false;
|
|
217
|
-
for (const testFile of testFiles) {
|
|
218
|
-
var _buildInfo$tests$test;
|
|
219
|
-
const timestamp = (await _fs.default.promises.stat(testFile)).mtimeMs;
|
|
220
|
-
if (((_buildInfo$tests$test = buildInfo.tests[testFile]) === null || _buildInfo$tests$test === void 0 ? void 0 : _buildInfo$tests$test.timestamp) !== timestamp) {
|
|
221
|
-
const components = await parseTestFile(testFile);
|
|
222
|
-
log('changed test:', testFile);
|
|
223
|
-
for (const component of components) componentRegistry.set(component.fullName, component);
|
|
224
|
-
buildInfo.tests[testFile] = {
|
|
225
|
-
timestamp,
|
|
226
|
-
components: components.map(c => c.fullName)
|
|
227
|
-
};
|
|
228
|
-
hasNewTests = true;
|
|
229
|
-
}
|
|
221
|
+
async function populateComponentsFromTests(componentRegistry, componentsByImportingFile) {
|
|
222
|
+
const importInfos = await (0, _compilationCache.getUserData)('playwright-ct-core');
|
|
223
|
+
for (const [file, importList] of importInfos) {
|
|
224
|
+
for (const importInfo of importList) componentRegistry.set(importInfo.id, importInfo);
|
|
225
|
+
componentsByImportingFile.set(file, importList.filter(i => !i.isModuleOrAlias).map(i => i.importPath));
|
|
230
226
|
}
|
|
231
|
-
return hasNewTests;
|
|
232
227
|
}
|
|
233
228
|
async function checkNewComponents(buildInfo, componentRegistry) {
|
|
234
229
|
const newComponents = [...componentRegistry.keys()];
|
|
235
|
-
const oldComponents = new Map(buildInfo.components.map(c => [c.
|
|
230
|
+
const oldComponents = new Map(buildInfo.components.map(c => [c.id, c]));
|
|
236
231
|
let hasNewComponents = false;
|
|
237
232
|
for (const c of newComponents) {
|
|
238
233
|
if (!oldComponents.has(c)) {
|
|
@@ -240,37 +235,10 @@ async function checkNewComponents(buildInfo, componentRegistry) {
|
|
|
240
235
|
break;
|
|
241
236
|
}
|
|
242
237
|
}
|
|
243
|
-
for (const c of oldComponents.values()) componentRegistry.set(c.
|
|
238
|
+
for (const c of oldComponents.values()) componentRegistry.set(c.id, c);
|
|
244
239
|
return hasNewComponents;
|
|
245
240
|
}
|
|
246
|
-
|
|
247
|
-
const text = await _fs.default.promises.readFile(testFile, 'utf-8');
|
|
248
|
-
const ast = (0, _babelBundle.parse)(text, {
|
|
249
|
-
errorRecovery: true,
|
|
250
|
-
plugins: ['typescript', 'jsx'],
|
|
251
|
-
sourceType: 'module'
|
|
252
|
-
});
|
|
253
|
-
const componentUsages = (0, _tsxTransform.collectComponentUsages)(ast);
|
|
254
|
-
const componentNames = componentUsages.names;
|
|
255
|
-
const result = [];
|
|
256
|
-
(0, _babelBundle.traverse)(ast, {
|
|
257
|
-
enter: p => {
|
|
258
|
-
if (_babelBundle.types.isImportDeclaration(p.node)) {
|
|
259
|
-
const importNode = p.node;
|
|
260
|
-
if (!_babelBundle.types.isStringLiteral(importNode.source)) return;
|
|
261
|
-
for (const specifier of importNode.specifiers) {
|
|
262
|
-
const specifierName = specifier.local.name;
|
|
263
|
-
const componentName = componentNames.has(specifierName) ? specifierName : [...componentNames].find(c => c.startsWith(specifierName + '.'));
|
|
264
|
-
if (!componentName) continue;
|
|
265
|
-
if (_babelBundle.types.isImportNamespaceSpecifier(specifier)) continue;
|
|
266
|
-
result.push((0, _tsxTransform.componentInfo)(specifier, importNode.source.value, testFile, componentName));
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
return result;
|
|
272
|
-
}
|
|
273
|
-
function vitePlugin(registerSource, templateDir, buildInfo, componentRegistry) {
|
|
241
|
+
function vitePlugin(registerSource, templateDir, buildInfo, importInfos, depsCollector) {
|
|
274
242
|
buildInfo.sources = {};
|
|
275
243
|
let moduleResolver;
|
|
276
244
|
return {
|
|
@@ -311,11 +279,11 @@ function vitePlugin(registerSource, templateDir, buildInfo, componentRegistry) {
|
|
|
311
279
|
const folder = _path.default.dirname(id);
|
|
312
280
|
const lines = [content, ''];
|
|
313
281
|
lines.push(registerSource);
|
|
314
|
-
for (const
|
|
282
|
+
for (const value of importInfos.values()) {
|
|
315
283
|
const importPath = value.isModuleOrAlias ? value.importPath : './' + _path.default.relative(folder, value.importPath).replace(/\\/g, '/');
|
|
316
|
-
|
|
284
|
+
lines.push(`const ${value.id} = () => import('${importPath}').then((mod) => mod.${value.remoteName || 'default'});`);
|
|
317
285
|
}
|
|
318
|
-
lines.push(`
|
|
286
|
+
lines.push(`__pwRegistry.initialize({ ${[...importInfos.keys()].join(',\n ')} });`);
|
|
319
287
|
return {
|
|
320
288
|
code: lines.join('\n'),
|
|
321
289
|
map: {
|
|
@@ -324,12 +292,12 @@ function vitePlugin(registerSource, templateDir, buildInfo, componentRegistry) {
|
|
|
324
292
|
};
|
|
325
293
|
},
|
|
326
294
|
async writeBundle() {
|
|
327
|
-
for (const
|
|
328
|
-
const id = await moduleResolver(component.importPath);
|
|
329
|
-
if (!id) continue;
|
|
295
|
+
for (const importInfo of importInfos.values()) {
|
|
330
296
|
const deps = new Set();
|
|
297
|
+
const id = await moduleResolver(importInfo.importPath);
|
|
298
|
+
if (!id) continue;
|
|
331
299
|
collectViteModuleDependencies(this, id, deps);
|
|
332
|
-
|
|
300
|
+
depsCollector.set(importInfo.importPath, [...deps]);
|
|
333
301
|
}
|
|
334
302
|
}
|
|
335
303
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playwright/experimental-ct-core",
|
|
3
|
-
"version": "1.41.
|
|
3
|
+
"version": "1.41.1",
|
|
4
4
|
"description": "Playwright Component Testing Helpers",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"playwright-core": "1.41.
|
|
30
|
+
"playwright-core": "1.41.1",
|
|
31
31
|
"vite": "^4.4.12",
|
|
32
|
-
"playwright": "1.41.
|
|
32
|
+
"playwright": "1.41.1"
|
|
33
33
|
},
|
|
34
34
|
"bin": {
|
|
35
35
|
"playwright": "cli.js"
|
package/types/component.d.ts
CHANGED
|
@@ -14,33 +14,32 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import type { ImportRegistry } from '../src/injected/importRegistry';
|
|
18
|
+
|
|
17
19
|
type JsonPrimitive = string | number | boolean | null;
|
|
18
20
|
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
|
19
21
|
type JsonArray = JsonValue[];
|
|
20
22
|
export type JsonObject = { [Key in string]?: JsonValue };
|
|
21
23
|
|
|
22
|
-
// JsxComponentChild can be anything, consider cases like: <>{1}</>, <>{null}</>
|
|
23
|
-
export type JsxComponentChild = JsxComponent | string | number | boolean | null;
|
|
24
24
|
export type JsxComponent = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
type: string,
|
|
25
|
+
__pw_type: 'jsx',
|
|
26
|
+
type: any,
|
|
28
27
|
props: Record<string, any>,
|
|
29
|
-
children?: JsxComponentChild[],
|
|
30
28
|
};
|
|
31
29
|
|
|
32
30
|
export type MountOptions = {
|
|
33
|
-
props?: Record<string, any>,
|
|
34
|
-
slots?: Record<string, string | string[]>,
|
|
35
|
-
on?: Record<string, Function>,
|
|
36
31
|
hooksConfig?: any,
|
|
37
32
|
};
|
|
38
33
|
|
|
39
|
-
export type
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
export type ObjectComponentOptions = {
|
|
35
|
+
props?: Record<string, any>;
|
|
36
|
+
slots?: Record<string, string | string[]>;
|
|
37
|
+
on?: Record<string, Function>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type ObjectComponent = ObjectComponentOptions & {
|
|
41
|
+
__pw_type: 'object-component',
|
|
42
|
+
type: any,
|
|
44
43
|
};
|
|
45
44
|
|
|
46
45
|
export type Component = JsxComponent | ObjectComponent;
|
|
@@ -56,5 +55,10 @@ declare global {
|
|
|
56
55
|
__pw_hooks_after_mount?: (<HooksConfig extends JsonObject = JsonObject>(
|
|
57
56
|
params: { hooksConfig?: HooksConfig; [key: string]: any }
|
|
58
57
|
) => Promise<void>)[];
|
|
58
|
+
__pwRegistry: ImportRegistry;
|
|
59
|
+
// Can't start with __pw due to core reuse bindings logic for __pw*.
|
|
60
|
+
__ctDispatchFunction: (ordinal: number, args: any[]) => void;
|
|
61
|
+
__pwUnwrapObject: (value: any) => Promise<any>;
|
|
62
|
+
__pwTransformObject: (value: any, mapping: (v: any) => { result: any } | undefined) => any;
|
|
59
63
|
}
|
|
60
64
|
}
|