Skip to content

Commit cc4991e

Browse files
authored
fix: bump playwright to 1.42.0 (#41)
1 parent 44b0ca5 commit cc4991e

File tree

9 files changed

+858
-279
lines changed

9 files changed

+858
-279
lines changed

Diff for: .github/workflows/deploy.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ jobs:
1111
steps:
1212
- uses: pnpm/action-setup@v2
1313
with:
14-
version: '8.x'
14+
version: '9.x'
1515

1616
- uses: actions/checkout@v3
1717

1818
- uses: actions/setup-node@v3
1919
with:
20-
node-version: '16.x'
20+
node-version: '18.x'
2121
registry-url: 'https://registry.npmjs.org'
2222
scope: '@sand4rt'
2323

Diff for: .github/workflows/test.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ jobs:
1111
steps:
1212
- uses: pnpm/action-setup@v2
1313
with:
14-
version: '8.x'
14+
version: '9.x'
1515

1616
- uses: actions/checkout@v3
1717

1818
- uses: actions/setup-node@v3
1919
with:
20-
node-version: '16.x'
20+
node-version: '18.x'
2121

2222
- name: Install dependencies
2323
run: pnpm install --frozen-lockfile
@@ -26,7 +26,7 @@ jobs:
2626
run: pnpm build
2727

2828
- name: Install Playwright Browsers
29-
run: npx playwright install --with-deps
29+
run: pnpm dlx playwright@1.42.0 install --with-deps
3030

3131
- name: Run Playwright tests
3232
run: pnpm test

Diff for: ct-web-lit/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"devDependencies": {
1616
"@sand4rt/experimental-ct-web": "workspace:*",
17-
"@playwright/test": "^1.41.0",
17+
"@playwright/test": "^1.42.0",
1818
"typescript": "^5.1.3",
1919
"vite": "^4.4.7"
2020
}

Diff for: ct-web/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
},
1212
"devDependencies": {
1313
"@sand4rt/experimental-ct-web": "workspace:*",
14-
"@playwright/test": "^1.41.0",
14+
"@playwright/test": "^1.42.0",
1515
"typescript": "^5.1.3",
1616
"vite": "^4.4.7"
1717
}

Diff for: playwright-ct-web/index.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core');
1818
const path = require('path');
1919

20-
function plugin() {
21-
// Only fetch upon request to avoid resolution in workers.
22-
const { createPlugin } = require('@playwright/experimental-ct-core/lib/vitePlugin');
23-
return createPlugin(path.join(__dirname, 'registerSource.mjs'));
20+
const defineConfig = (config, ...configs) => {
21+
const originalConfig = originalDefineConfig({
22+
...config,
23+
'@playwright/experimental-ct-core': {
24+
registerSourceFile: path.join(__dirname, 'registerSource.mjs'),
25+
},
26+
}, ...configs);
27+
originalConfig['@playwright/test'].babelPlugins = [[require.resolve('./transform')]];
28+
return originalConfig ;
2429
};
2530

26-
const defineConfig = (config, ...configs) => originalDefineConfig({ ...config, _plugins: [plugin] }, ...configs);
27-
2831
module.exports = { test, expect, devices, defineConfig };

Diff for: playwright-ct-web/package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sand4rt/experimental-ct-web",
3-
"version": "1.41.0",
3+
"version": "1.42.0",
44
"description": "Playwright Component Testing for Web Components",
55
"homepage": "https://playwright.dev",
66
"repository": {
@@ -43,15 +43,15 @@
4343
}
4444
},
4545
"dependencies": {
46-
"@playwright/experimental-ct-core": "^1.41.0"
46+
"@playwright/experimental-ct-core": "1.42.0"
4747
},
4848
"devDependencies": {
49-
"@playwright/test": "1.41.0"
49+
"@playwright/test": "1.42.0"
5050
},
5151
"peerDependencies": {
52-
"@playwright/test": ">=1.41.0"
52+
"@playwright/test": ">=1.42.0"
5353
},
5454
"bin": {
55-
"playwright": "./cli.js"
55+
"playwright": "cli.js"
5656
}
5757
}

Diff for: playwright-ct-web/registerSource.mjs

+13-72
Original file line numberDiff line numberDiff line change
@@ -17,61 +17,11 @@
1717
// @ts-check
1818
// This file is injected into the registry as text, no dependencies are allowed.
1919

20-
/** @typedef {import('@playwright/experimental-ct-core/types/component').Component} Component */
21-
/** @typedef {import('@playwright/experimental-ct-core/types/component').JsxComponent} JsxComponent */
2220
/** @typedef {import('@playwright/experimental-ct-core/types/component').ObjectComponent} ObjectComponent */
2321
/** @typedef {new (...args: any[]) => HTMLElement} FrameworkComponent */
2422

25-
/** @type {Map<string, () => Promise<FrameworkComponent>>} */
26-
const __pwLoaderRegistry = new Map();
27-
/** @type {Map<string, FrameworkComponent>} */
28-
const __pwRegistry = new Map();
2923
const __pwListeners = new Map();
3024

31-
/**
32-
* @param {Record<string, () => Promise<FrameworkComponent>>} components
33-
*/
34-
export function pwRegister(components) {
35-
for (const [name, value] of Object.entries(components))
36-
__pwLoaderRegistry.set(name, value);
37-
}
38-
39-
/**
40-
* @param {Component} component
41-
* @returns {component is JsxComponent | ObjectComponent}
42-
*/
43-
function isComponent(component) {
44-
return !(typeof component !== 'object' || Array.isArray(component));
45-
}
46-
47-
/**
48-
* @param {Component} component
49-
*/
50-
async function __pwResolveComponent(component) {
51-
if (!isComponent(component))
52-
return
53-
54-
let componentFactory = __pwLoaderRegistry.get(component.type);
55-
if (!componentFactory) {
56-
// Lookup by shorthand.
57-
for (const [name, value] of __pwLoaderRegistry) {
58-
if (component.type.endsWith(`_${name}`)) {
59-
componentFactory = value;
60-
break;
61-
}
62-
}
63-
}
64-
65-
if (!componentFactory && component.type[0].toUpperCase() === component.type[0])
66-
throw new Error(`Unregistered component: ${component.type}. Following components are registered: ${[...__pwRegistry.keys()]}`);
67-
68-
if(componentFactory)
69-
__pwRegistry.set(component.type, await componentFactory())
70-
71-
if ('children' in component)
72-
await Promise.all(component.children.map(child => __pwResolveComponent(child)))
73-
}
74-
7525
/**
7626
* @param {HTMLElement} webComponent
7727
*/
@@ -151,27 +101,18 @@ function __pwCreateSlot(value) {
151101
}
152102

153103
/**
154-
* @param {Component} component
104+
* @param {ObjectComponent} component
155105
*/
156106
function __pwCreateComponent(component) {
157-
const Component = __pwRegistry.get(component.type);
158-
if (!Component)
159-
throw new Error(
160-
`Unregistered component: ${
161-
component.type
162-
}. Following components are registered: ${[...__pwRegistry.keys()]}`
163-
);
164-
165-
const webComponent = new Component();
166-
__pwUpdateProps(webComponent, component.options?.props);
167-
__pwUpdateSlots(webComponent, component.options?.slots);
168-
__pwUpdateEvents(webComponent, component.options?.on);
107+
const webComponent = new component.type();
108+
__pwUpdateProps(webComponent, component.props);
109+
__pwUpdateSlots(webComponent, component.slots);
110+
__pwUpdateEvents(webComponent, component.on);
169111
return webComponent;
170112
}
171113

172114
window.playwrightMount = async (component, rootElement, hooksConfig) => {
173-
await __pwResolveComponent(component);
174-
if (component.kind !== 'object')
115+
if (component.__pw_type === 'jsx')
175116
throw new Error('JSX mount notation is not supported');
176117

177118
const webComponent = __pwCreateComponent(component);
@@ -186,17 +127,17 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
186127
};
187128

188129
window.playwrightUpdate = async (rootElement, component) => {
189-
await __pwResolveComponent(component);
190-
if (component.kind === 'jsx')
130+
if (component.__pw_type === 'jsx')
191131
throw new Error('JSX mount notation is not supported');
192132

193133
const webComponent = /** @type {?HTMLElement} */ (rootElement.firstChild);
194-
if (!webComponent) throw new Error('Component was not mounted');
134+
if (!webComponent)
135+
throw new Error('Component was not mounted');
195136

196-
__pwUpdateProps(webComponent, component.options?.props);
197-
__pwUpdateSlots(webComponent, component.options?.slots);
198-
__pwRemoveEvents(webComponent, component.options?.on);
199-
__pwUpdateEvents(webComponent, component.options?.on);
137+
__pwUpdateProps(webComponent, component.props);
138+
__pwUpdateSlots(webComponent, component.slots);
139+
__pwRemoveEvents(webComponent, component.on);
140+
__pwUpdateEvents(webComponent, component.on);
200141
};
201142

202143
window.playwrightUnmount = async (rootElement) => {

Diff for: playwright-ct-web/transform.js

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"use strict";
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
exports.default = void 0;
7+
exports.importInfo = importInfo;
8+
var _path = _interopRequireDefault(require("path"));
9+
var _babelBundle = require("playwright/lib/transform/babelBundle");
10+
var _transform = require("playwright/lib/transform/transform");
11+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12+
/**
13+
* Copyright (c) Microsoft Corporation.
14+
*
15+
* Licensed under the Apache License, Version 2.0 (the 'License');
16+
* you may not use this file except in compliance with the License.
17+
* You may obtain a copy of the License at
18+
*
19+
* http://www.apache.org/licenses/LICENSE-2.0
20+
*
21+
* Unless required by applicable law or agreed to in writing, software
22+
* distributed under the License is distributed on an 'AS IS' BASIS,
23+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24+
* See the License for the specific language governing permissions and
25+
* limitations under the License.
26+
*/
27+
28+
const t = _babelBundle.types;
29+
let jsxComponentNames;
30+
let classComponentNames;
31+
let importInfos;
32+
var _default = exports.default = (0, _babelBundle.declare)(api => {
33+
api.assertVersion(7);
34+
const result = {
35+
name: 'playwright-debug-transform',
36+
visitor: {
37+
Program: {
38+
enter(path) {
39+
jsxComponentNames = collectJsxComponentUsages(path.node);
40+
classComponentNames = collectClassMountUsages(path.node);
41+
importInfos = new Map();
42+
},
43+
exit(path) {
44+
let firstDeclaration;
45+
let lastImportDeclaration;
46+
path.get('body').forEach(p => {
47+
if (p.isImportDeclaration()) lastImportDeclaration = p;else if (!firstDeclaration) firstDeclaration = p;
48+
});
49+
const insertionPath = lastImportDeclaration || firstDeclaration;
50+
if (!insertionPath) return;
51+
for (const [localName, componentImport] of [...importInfos.entries()].reverse()) {
52+
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))]))]));
53+
}
54+
(0, _transform.setTransformData)('playwright-ct-core', [...importInfos.values()]);
55+
}
56+
},
57+
ImportDeclaration(p) {
58+
const importNode = p.node;
59+
if (!t.isStringLiteral(importNode.source)) return;
60+
const ext = _path.default.extname(importNode.source.value);
61+
62+
// Convert all non-JS imports into refs.
63+
if (artifactExtensions.has(ext)) {
64+
for (const specifier of importNode.specifiers) {
65+
if (t.isImportNamespaceSpecifier(specifier)) continue;
66+
const {
67+
localName,
68+
info
69+
} = importInfo(importNode, specifier, this.filename);
70+
importInfos.set(localName, info);
71+
}
72+
p.skip();
73+
p.remove();
74+
return;
75+
}
76+
77+
// Convert JS imports that are used as components in JSX expressions into refs.
78+
let importCount = 0;
79+
for (const specifier of importNode.specifiers) {
80+
if (t.isImportNamespaceSpecifier(specifier)) continue;
81+
const {
82+
localName,
83+
info
84+
} = importInfo(importNode, specifier, this.filename);
85+
if (jsxComponentNames.has(localName) || classComponentNames.has(localName)) {
86+
importInfos.set(localName, info);
87+
++importCount;
88+
}
89+
}
90+
91+
// All the imports were from JSX => delete.
92+
if (importCount && importCount === importNode.specifiers.length) {
93+
p.skip();
94+
p.remove();
95+
}
96+
},
97+
MemberExpression(path) {
98+
if (!t.isIdentifier(path.node.object)) return;
99+
if (!importInfos.has(path.node.object.name)) return;
100+
if (!t.isIdentifier(path.node.property)) return;
101+
path.replaceWith(t.objectExpression([t.spreadElement(t.identifier(path.node.object.name)), t.objectProperty(t.identifier('property'), t.stringLiteral(path.node.property.name))]));
102+
}
103+
}
104+
};
105+
return result;
106+
});
107+
function collectJsxComponentUsages(node) {
108+
const names = new Set();
109+
(0, _babelBundle.traverse)(node, {
110+
enter: p => {
111+
// Treat JSX-everything as component usages.
112+
if (t.isJSXElement(p.node)) {
113+
if (t.isJSXIdentifier(p.node.openingElement.name)) names.add(p.node.openingElement.name.name);
114+
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);
115+
}
116+
}
117+
});
118+
return names;
119+
}
120+
function collectClassMountUsages(node) {
121+
const names = new Set();
122+
(0, _babelBundle.traverse)(node, {
123+
enter: p => {
124+
// Treat calls to mount and all identifiers in arguments as component usages e.g. mount(Component)
125+
if (t.isCallExpression(p.node) && t.isIdentifier(p.node.callee) && p.node.callee.name === 'mount') {
126+
p.traverse({
127+
Identifier: p => {
128+
names.add(p.node.name);
129+
}
130+
});
131+
}
132+
}
133+
});
134+
return names;
135+
}
136+
function importInfo(importNode, specifier, filename) {
137+
const importSource = importNode.source.value;
138+
const idPrefix = _path.default.join(filename, '..', importSource).replace(/[^\w_\d]/g, '_');
139+
const result = {
140+
id: idPrefix,
141+
filename,
142+
importSource,
143+
remoteName: undefined
144+
};
145+
if (t.isImportDefaultSpecifier(specifier)) {} else if (t.isIdentifier(specifier.imported)) {
146+
result.remoteName = specifier.imported.name;
147+
} else {
148+
result.remoteName = specifier.imported.value;
149+
}
150+
if (result.remoteName) result.id += '_' + result.remoteName;
151+
return {
152+
localName: specifier.local.name,
153+
info: result
154+
};
155+
}
156+
const artifactExtensions = new Set([
157+
// Frameworks
158+
'.vue', '.svelte',
159+
// Images
160+
'.jpg', '.jpeg', '.png', '.gif', '.svg', '.bmp', '.webp', '.ico',
161+
// CSS
162+
'.css',
163+
// Fonts
164+
'.woff', '.woff2', '.ttf', '.otf', '.eot']);

0 commit comments

Comments
 (0)