Skip to content

Commit

Permalink
fix: bump playwright to 1.42.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sand4rt committed May 8, 2024
1 parent 44b0ca5 commit 07fec13
Show file tree
Hide file tree
Showing 7 changed files with 853 additions and 274 deletions.
2 changes: 1 addition & 1 deletion ct-web-lit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"devDependencies": {
"@sand4rt/experimental-ct-web": "workspace:*",
"@playwright/test": "^1.41.0",
"@playwright/test": "^1.42.0",
"typescript": "^5.1.3",
"vite": "^4.4.7"
}
Expand Down
2 changes: 1 addition & 1 deletion ct-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"devDependencies": {
"@sand4rt/experimental-ct-web": "workspace:*",
"@playwright/test": "^1.41.0",
"@playwright/test": "^1.42.0",
"typescript": "^5.1.3",
"vite": "^4.4.7"
}
Expand Down
15 changes: 9 additions & 6 deletions playwright-ct-web/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@
const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core');
const path = require('path');

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

const defineConfig = (config, ...configs) => originalDefineConfig({ ...config, _plugins: [plugin] }, ...configs);

module.exports = { test, expect, devices, defineConfig };
10 changes: 5 additions & 5 deletions playwright-ct-web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sand4rt/experimental-ct-web",
"version": "1.41.0",
"version": "1.42.0",
"description": "Playwright Component Testing for Web Components",
"homepage": "https://playwright.dev",
"repository": {
Expand Down Expand Up @@ -43,15 +43,15 @@
}
},
"dependencies": {
"@playwright/experimental-ct-core": "^1.41.0"
"@playwright/experimental-ct-core": "1.42.0"
},
"devDependencies": {
"@playwright/test": "1.41.0"
"@playwright/test": "1.42.0"
},
"peerDependencies": {
"@playwright/test": ">=1.41.0"
"@playwright/test": ">=1.42.0"
},
"bin": {
"playwright": "./cli.js"
"playwright": "cli.js"
}
}
85 changes: 13 additions & 72 deletions playwright-ct-web/registerSource.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,61 +17,11 @@
// @ts-check
// This file is injected into the registry as text, no dependencies are allowed.

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

/** @type {Map<string, () => Promise<FrameworkComponent>>} */
const __pwLoaderRegistry = new Map();
/** @type {Map<string, FrameworkComponent>} */
const __pwRegistry = new Map();
const __pwListeners = new Map();

/**
* @param {Record<string, () => Promise<FrameworkComponent>>} components
*/
export function pwRegister(components) {
for (const [name, value] of Object.entries(components))
__pwLoaderRegistry.set(name, value);
}

/**
* @param {Component} component
* @returns {component is JsxComponent | ObjectComponent}
*/
function isComponent(component) {
return !(typeof component !== 'object' || Array.isArray(component));
}

/**
* @param {Component} component
*/
async function __pwResolveComponent(component) {
if (!isComponent(component))
return

let componentFactory = __pwLoaderRegistry.get(component.type);
if (!componentFactory) {
// Lookup by shorthand.
for (const [name, value] of __pwLoaderRegistry) {
if (component.type.endsWith(`_${name}`)) {
componentFactory = value;
break;
}
}
}

if (!componentFactory && component.type[0].toUpperCase() === component.type[0])
throw new Error(`Unregistered component: ${component.type}. Following components are registered: ${[...__pwRegistry.keys()]}`);

if(componentFactory)
__pwRegistry.set(component.type, await componentFactory())

if ('children' in component)
await Promise.all(component.children.map(child => __pwResolveComponent(child)))
}

/**
* @param {HTMLElement} webComponent
*/
Expand Down Expand Up @@ -151,27 +101,18 @@ function __pwCreateSlot(value) {
}

/**
* @param {Component} component
* @param {ObjectComponent} component
*/
function __pwCreateComponent(component) {
const Component = __pwRegistry.get(component.type);
if (!Component)
throw new Error(
`Unregistered component: ${
component.type
}. Following components are registered: ${[...__pwRegistry.keys()]}`
);

const webComponent = new Component();
__pwUpdateProps(webComponent, component.options?.props);
__pwUpdateSlots(webComponent, component.options?.slots);
__pwUpdateEvents(webComponent, component.options?.on);
const webComponent = new component.type();
__pwUpdateProps(webComponent, component.props);
__pwUpdateSlots(webComponent, component.slots);
__pwUpdateEvents(webComponent, component.on);
return webComponent;
}

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

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

window.playwrightUpdate = async (rootElement, component) => {
await __pwResolveComponent(component);
if (component.kind === 'jsx')
if (component.__pw_type === 'jsx')
throw new Error('JSX mount notation is not supported');

const webComponent = /** @type {?HTMLElement} */ (rootElement.firstChild);
if (!webComponent) throw new Error('Component was not mounted');
if (!webComponent)
throw new Error('Component was not mounted');

__pwUpdateProps(webComponent, component.options?.props);
__pwUpdateSlots(webComponent, component.options?.slots);
__pwRemoveEvents(webComponent, component.options?.on);
__pwUpdateEvents(webComponent, component.options?.on);
__pwUpdateProps(webComponent, component.props);
__pwUpdateSlots(webComponent, component.slots);
__pwRemoveEvents(webComponent, component.on);
__pwUpdateEvents(webComponent, component.on);
};

window.playwrightUnmount = async (rootElement) => {
Expand Down
164 changes: 164 additions & 0 deletions playwright-ct-web/transform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
"use strict";

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
exports.importInfo = importInfo;
var _path = _interopRequireDefault(require("path"));
var _babelBundle = require("playwright/lib/transform/babelBundle");
var _transform = require("playwright/lib/transform/transform");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

const t = _babelBundle.types;
let jsxComponentNames;
let classComponentNames;
let importInfos;
var _default = exports.default = (0, _babelBundle.declare)(api => {
api.assertVersion(7);
const result = {
name: 'playwright-debug-transform',
visitor: {
Program: {
enter(path) {
jsxComponentNames = collectJsxComponentUsages(path.node);
classComponentNames = collectClassMountUsages(path.node);
importInfos = new Map();
},
exit(path) {
let firstDeclaration;
let lastImportDeclaration;
path.get('body').forEach(p => {
if (p.isImportDeclaration()) lastImportDeclaration = p;else if (!firstDeclaration) firstDeclaration = p;
});
const insertionPath = lastImportDeclaration || firstDeclaration;
if (!insertionPath) return;
for (const [localName, componentImport] of [...importInfos.entries()].reverse()) {
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))]))]));
}
(0, _transform.setTransformData)('playwright-ct-core', [...importInfos.values()]);
}
},
ImportDeclaration(p) {
const importNode = p.node;
if (!t.isStringLiteral(importNode.source)) return;
const ext = _path.default.extname(importNode.source.value);

// Convert all non-JS imports into refs.
if (artifactExtensions.has(ext)) {
for (const specifier of importNode.specifiers) {
if (t.isImportNamespaceSpecifier(specifier)) continue;
const {
localName,
info
} = importInfo(importNode, specifier, this.filename);
importInfos.set(localName, info);
}
p.skip();
p.remove();
return;
}

// Convert JS imports that are used as components in JSX expressions into refs.
let importCount = 0;
for (const specifier of importNode.specifiers) {
if (t.isImportNamespaceSpecifier(specifier)) continue;
const {
localName,
info
} = importInfo(importNode, specifier, this.filename);
if (jsxComponentNames.has(localName) || classComponentNames.has(localName)) {
importInfos.set(localName, info);
++importCount;
}
}

// All the imports were from JSX => delete.
if (importCount && importCount === importNode.specifiers.length) {
p.skip();
p.remove();
}
},
MemberExpression(path) {
if (!t.isIdentifier(path.node.object)) return;
if (!importInfos.has(path.node.object.name)) return;
if (!t.isIdentifier(path.node.property)) return;
path.replaceWith(t.objectExpression([t.spreadElement(t.identifier(path.node.object.name)), t.objectProperty(t.identifier('property'), t.stringLiteral(path.node.property.name))]));
}
}
};
return result;
});
function collectJsxComponentUsages(node) {
const names = new Set();
(0, _babelBundle.traverse)(node, {
enter: p => {
// Treat JSX-everything as component usages.
if (t.isJSXElement(p.node)) {
if (t.isJSXIdentifier(p.node.openingElement.name)) names.add(p.node.openingElement.name.name);
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);
}
}
});
return names;
}
function collectClassMountUsages(node) {
const names = new Set();
(0, _babelBundle.traverse)(node, {
enter: p => {
// Treat calls to mount and all identifiers in arguments as component usages e.g. mount(Component)
if (t.isCallExpression(p.node) && t.isIdentifier(p.node.callee) && p.node.callee.name === 'mount') {
p.traverse({
Identifier: p => {
names.add(p.node.name);
}
});
}
}
});
return names;
}
function importInfo(importNode, specifier, filename) {
const importSource = importNode.source.value;
const idPrefix = _path.default.join(filename, '..', importSource).replace(/[^\w_\d]/g, '_');
const result = {
id: idPrefix,
filename,
importSource,
remoteName: undefined
};
if (t.isImportDefaultSpecifier(specifier)) {} else if (t.isIdentifier(specifier.imported)) {
result.remoteName = specifier.imported.name;
} else {
result.remoteName = specifier.imported.value;
}
if (result.remoteName) result.id += '_' + result.remoteName;
return {
localName: specifier.local.name,
info: result
};
}
const artifactExtensions = new Set([
// Frameworks
'.vue', '.svelte',
// Images
'.jpg', '.jpeg', '.png', '.gif', '.svg', '.bmp', '.webp', '.ico',
// CSS
'.css',
// Fonts
'.woff', '.woff2', '.ttf', '.otf', '.eot']);
Loading

0 comments on commit 07fec13

Please sign in to comment.