chore: use plugins for component testing again (#13977)

This commit is contained in:
Pavel Feldman 2022-05-05 13:26:56 -08:00 коммит произвёл GitHub
Родитель 65d025d82c
Коммит a2f9f15e3e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
68 изменённых файлов: 516 добавлений и 349 удалений

Просмотреть файл

@ -62,7 +62,7 @@ npm i @playwright/experimental-ct-react
### Create a test `src/App.spec.tsx`
```js
import { test, expect } from '@playwright/experimental-ct-react';
import { test, expect } from '@playwright/test';
import App from './App';
test.use({ viewport: { width: 500, height: 500 } });

Просмотреть файл

@ -1,10 +1,12 @@
import { type PlaywrightTestConfig, devices } from '@playwright/experimental-ct-vue';
import { type PlaywrightTestConfig, devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-vue';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct()],
use: {
trace: 'on-first-retry',
},

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import Counter from './Counter.vue'

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import Counter from './Counter.vue'

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import HelloWorld from './HelloWorld.vue'

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import HelloWorld from './HelloWorld.vue'

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import NamedSlots from './NamedSlots.vue'

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import NamedSlots from './NamedSlots.vue'

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import DocumentationIcon from './icons/IconDocumentation.vue'
import WelcomeItem from './WelcomeItem.vue'

Просмотреть файл

@ -0,0 +1,8 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"noEmit": true,
"jsx": "react-jsx",
},
"include": ["src", "playwright.d.ts", "vue.d.ts"],
}

Просмотреть файл

@ -15,16 +15,17 @@
*/
import path from 'path';
import type { PlaywrightTestConfig } from '@playwright/experimental-ct-react';
import { devices } from '@playwright/experimental-ct-react';
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-react';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct({ vitePort: 3101 })],
use: {
vitePort: 3101,
trace: 'on-first-retry',
},
projects: [ ],

17
packages/html-reporter/playwright.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/**
* 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.
*/
import '@playwright/experimental-ct-react';

Просмотреть файл

@ -15,7 +15,7 @@
*/
import React from 'react';
import { expect, test } from '@playwright/experimental-ct-react';
import { expect, test } from '@playwright/test';
import { AutoChip, Chip as LocalChip } from './chip';
test.use({ viewport: { width: 500, height: 500 } });

Просмотреть файл

@ -15,7 +15,7 @@
*/
import React from 'react';
import { test, expect } from '@playwright/experimental-ct-react';
import { test, expect } from '@playwright/test';
import { HeaderView } from './headerView';
test.use({ viewport: { width: 720, height: 200 } });

Просмотреть файл

@ -15,7 +15,7 @@
*/
import React from 'react';
import { test, expect } from '@playwright/experimental-ct-react';
import { test, expect } from '@playwright/test';
import type { ImageDiff } from './imageDiffView';
import { ImageDiffView } from './imageDiffView';

Просмотреть файл

@ -15,7 +15,7 @@
*/
import React from 'react';
import { test, expect } from '@playwright/experimental-ct-react';
import { test, expect } from '@playwright/test';
import { TestCaseView } from './testCaseView';
import type { TestCase, TestResult } from '../../playwright-test/src/reporters/html';

Просмотреть файл

@ -24,6 +24,6 @@
"playwright-test/lib/*": ["../playwright-test/src/*"],
}
},
"include": ["src"],
"include": ["src", "playwright.d.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}

28
packages/playwright-ct-react/index.d.ts поставляемый
Просмотреть файл

@ -14,27 +14,15 @@
* limitations under the License.
*/
import type {
TestType,
PlaywrightTestArgs,
PlaywrightTestConfig as BasePlaywrightTestConfig,
PlaywrightTestOptions,
PlaywrightWorkerArgs,
PlaywrightWorkerOptions,
Locator,
} from '@playwright/test';
import type { Locator, TestPlugin } from '@playwright/test';
import type { InlineConfig } from 'vite';
export type PlaywrightTestConfig = Omit<BasePlaywrightTestConfig, 'use'> & {
use?: BasePlaywrightTestConfig['use'] & { vitePort?: number, viteConfig?: InlineConfig }
};
interface ComponentFixtures {
mount(component: JSX.Element): Promise<Locator>;
declare global {
export namespace PlaywrightTest {
export interface TestArgs {
mount(component: JSX.Element): Promise<Locator>;
}
}
}
export const test: TestType<
PlaywrightTestArgs & PlaywrightTestOptions & ComponentFixtures,
PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
export { expect, devices } from '@playwright/test';
export default function(options?: { vitePort?: number, viteConfig?: InlineConfig }): TestPlugin;

Просмотреть файл

@ -14,42 +14,14 @@
* limitations under the License.
*/
const { test: baseTest, expect, devices, _addRunnerPlugin } = require('@playwright/test');
const { mount } = require('@playwright/test/lib/mount');
const path = require('path');
_addRunnerPlugin(() => {
// Only fetch upon request to avoid resolution in workers.
const { createPlugin } = require('@playwright/test/lib/plugins/vitePlugin');
return createPlugin(
module.exports = ({ viteConfig, vitePort } = {}) => {
const { vitePlugin } = require('@playwright/test/lib/plugins/vitePlugin');
return vitePlugin(
'playwright:experimental-ct-react',
path.join(__dirname, 'registerSource.mjs'),
() => require('@vitejs/plugin-react')());
});
const test = baseTest.extend({
_workerPage: [async ({ browser }, use) => {
const page = await browser._wrapApiCall(async () => {
const page = await browser.newPage();
await page.addInitScript('navigator.serviceWorker.register = () => {}');
return page;
});
await use(page);
}, { scope: 'worker' }],
context: async ({ page }, use) => {
await use(page.context());
},
page: async ({ _workerPage }, use) => {
await use(_workerPage);
},
mount: async ({ page, baseURL, viewport }, use) => {
await use(async (component, options) => {
const selector = await mount(page, component, options, baseURL, viewport);
return page.locator(selector);
});
},
});
module.exports = { test, expect, devices };
() => require('@vitejs/plugin-react')(),
viteConfig,
vitePort);
};

36
packages/playwright-ct-svelte/index.d.ts поставляемый
Просмотреть файл

@ -14,31 +14,19 @@
* limitations under the License.
*/
import type {
TestType,
PlaywrightTestArgs,
PlaywrightTestConfig as BasePlaywrightTestConfig,
PlaywrightTestOptions,
PlaywrightWorkerArgs,
PlaywrightWorkerOptions,
Locator,
} from '@playwright/test';
import type { Locator, TestPlugin } from '@playwright/test';
import type { InlineConfig } from 'vite';
export type PlaywrightTestConfig = Omit<BasePlaywrightTestConfig, 'use'> & {
use?: BasePlaywrightTestConfig['use'] & { vitePort?: number, viteConfig?: InlineConfig }
};
interface ComponentFixtures {
mount(component: any, options?: {
props?: { [key: string]: any },
slots?: { [key: string]: any },
on?: { [key: string]: Function },
}): Promise<Locator>;
declare global {
export namespace PlaywrightTest {
export interface TestArgs {
mount(component: any, options?: {
props?: { [key: string]: any },
slots?: { [key: string]: any },
on?: { [key: string]: Function },
}): Promise<Locator>;
}
}
}
export const test: TestType<
PlaywrightTestArgs & PlaywrightTestOptions & ComponentFixtures,
PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
export { expect, devices } from '@playwright/test';
export default function(options?: { vitePort?: number, viteConfig?: InlineConfig }): TestPlugin;

Просмотреть файл

@ -14,42 +14,14 @@
* limitations under the License.
*/
const { test: baseTest, expect, devices, _addRunnerPlugin } = require('@playwright/test');
const { mount } = require('@playwright/test/lib/mount');
const path = require('path');
_addRunnerPlugin(() => {
// Only fetch upon request to avoid resolution in workers.
const { createPlugin } = require('@playwright/test/lib/plugins/vitePlugin');
return createPlugin(
module.exports = ({ viteConfig, vitePort } = {}) => {
const { vitePlugin } = require('@playwright/test/lib/plugins/vitePlugin');
return vitePlugin(
'playwright:experimental-ct-svelte',
path.join(__dirname, 'registerSource.mjs'),
() => require('@sveltejs/vite-plugin-svelte').svelte());
});
const test = baseTest.extend({
_workerPage: [async ({ browser }, use) => {
const page = await browser._wrapApiCall(async () => {
const page = await browser.newPage();
await page.addInitScript('navigator.serviceWorker.register = () => {}');
return page;
});
await use(page);
}, { scope: 'worker' }],
context: async ({ page }, use) => {
await use(page.context());
},
page: async ({ _workerPage }, use) => {
await use(_workerPage);
},
mount: async ({ page, baseURL, viewport }, use) => {
await use(async (component, options) => {
const selector = await mount(page, component, options, baseURL, viewport);
return page.locator(selector);
});
},
});
module.exports = { test, expect, devices };
() => require('@sveltejs/vite-plugin-svelte').svelte(),
viteConfig,
vitePort);
};

38
packages/playwright-ct-vue/index.d.ts поставляемый
Просмотреть файл

@ -14,32 +14,20 @@
* limitations under the License.
*/
import type {
TestType,
PlaywrightTestArgs,
PlaywrightTestConfig as BasePlaywrightTestConfig,
PlaywrightTestOptions,
PlaywrightWorkerArgs,
PlaywrightWorkerOptions,
Locator,
} from '@playwright/test';
import type { Locator, TestPlugin } from '@playwright/test';
import type { InlineConfig } from 'vite';
export type PlaywrightTestConfig = Omit<BasePlaywrightTestConfig, 'use'> & {
use?: BasePlaywrightTestConfig['use'] & { vitePort?: number, viteConfig?: InlineConfig }
};
interface ComponentFixtures {
mount(component: JSX.Element): Promise<Locator>;
mount(component: any, options?: {
props?: { [key: string]: any },
slots?: { [key: string]: any },
on?: { [key: string]: Function },
}): Promise<Locator>;
declare global {
export namespace PlaywrightTest {
export interface TestArgs {
mount(component: JSX.Element): Promise<Locator>;
mount(component: any, options?: {
props?: { [key: string]: any },
slots?: { [key: string]: any },
on?: { [key: string]: Function },
}): Promise<Locator>;
}
}
}
export const test: TestType<
PlaywrightTestArgs & PlaywrightTestOptions & ComponentFixtures,
PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
export { expect, devices } from '@playwright/test';
export default function(options?: { vitePort?: number, viteConfig?: InlineConfig }): TestPlugin;

Просмотреть файл

@ -14,42 +14,14 @@
* limitations under the License.
*/
const { test: baseTest, expect, devices, _addRunnerPlugin } = require('@playwright/test');
const { mount } = require('@playwright/test/lib/mount');
const path = require('path');
_addRunnerPlugin(() => {
// Only fetch upon request to avoid resolution in workers.
const { createPlugin } = require('@playwright/test/lib/plugins/vitePlugin');
return createPlugin(
module.exports = ({ viteConfig, vitePort } = {}) => {
const { vitePlugin } = require('@playwright/test/lib/plugins/vitePlugin');
return vitePlugin(
'playwright:experimental-ct-vue',
path.join(__dirname, 'registerSource.mjs'),
() => require('@vitejs/plugin-vue')());
});
const test = baseTest.extend({
_workerPage: [async ({ browser }, use) => {
const page = await browser._wrapApiCall(async () => {
const page = await browser.newPage();
await page.addInitScript('navigator.serviceWorker.register = () => {}');
return page;
});
await use(page);
}, { scope: 'worker' }],
context: async ({ page }, use) => {
await use(page.context());
},
page: async ({ _workerPage }, use) => {
await use(_workerPage);
},
mount: async ({ page, baseURL, viewport }, use) => {
await use(async (component, options) => {
const selector = await mount(page, component, options, baseURL, viewport);
return page.locator(selector);
});
},
});
module.exports = { test, expect, devices };
() => require('@vitejs/plugin-vue')(),
viteConfig,
vitePort);
};

Просмотреть файл

@ -18,7 +18,6 @@
"./lib/ci": "./lib/ci.js",
"./lib/cli": "./lib/cli.js",
"./lib/experimentalLoader": "./lib/experimentalLoader.js",
"./lib/mount": "./lib/mount.js",
"./lib/plugins": "./lib/plugins/index.js",
"./lib/plugins/vitePlugin": "./lib/plugins/vitePlugin.js",
"./reporter": "./reporter.js"

Просмотреть файл

@ -123,13 +123,17 @@ export class Loader {
if (config.snapshotDir !== undefined)
config.snapshotDir = path.resolve(configDir, config.snapshotDir);
config.plugins = await Promise.all((config.plugins || []).map(async plugin => {
const resolvedPlugins = await Promise.all((config.plugins || []).map(async plugin => {
if (typeof plugin === 'string')
return (await this._requireOrImportDefaultObject(resolveScript(plugin, configDir))) as TestPlugin;
if (Array.isArray(plugin)) {
const func = await this._requireOrImportDefaultFunction(resolveScript(plugin[0], configDir), false);
plugin = func(plugin[1]) as TestPlugin;
}
return plugin;
}));
for (const plugin of config.plugins || []) {
for (const plugin of resolvedPlugins) {
if (!plugin.fixtures)
continue;
if (typeof plugin.fixtures === 'string')
@ -155,7 +159,7 @@ export class Loader {
this._fullConfig.updateSnapshots = takeFirst(config.updateSnapshots, baseFullConfig.updateSnapshots);
this._fullConfig.workers = takeFirst(config.workers, baseFullConfig.workers);
this._fullConfig.webServer = takeFirst(config.webServer, baseFullConfig.webServer);
this._fullConfig._plugins = takeFirst(config.plugins, baseFullConfig._plugins);
this._fullConfig._plugins = takeFirst(resolvedPlugins, baseFullConfig._plugins);
this._fullConfig.metadata = takeFirst(config.metadata, baseFullConfig.metadata);
this._fullConfig.projects = (config.projects || [config]).map(p => this._resolveProject(config, this._fullConfig, p, throwawayArtifactsPath));
}

Просмотреть файл

@ -14,131 +14,57 @@
* limitations under the License.
*/
import fs from 'fs';
import type { InlineConfig, Plugin } from 'vite';
import type { Suite } from '../../types/testReporter';
import path from 'path';
import type { InlineConfig, Plugin, PreviewServer } from 'vite';
import type { TestRunnerPlugin } from '.';
import { parse, traverse, types as t } from '../babelBundle';
import type { ComponentInfo } from '../tsxTransform';
import { collectComponentUsages, componentInfo } from '../tsxTransform';
import type { FullConfig } from '../types';
import type { Fixtures, FullConfig, Locator, Page, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, TestPlugin } from '../types';
let previewServer: PreviewServer;
import { mount } from '../mount';
export function createPlugin(
export function vitePlugin(
name: string,
registerSourceFile: string,
frameworkPluginFactory: () => Plugin): TestRunnerPlugin {
let configDir: string;
frameworkPluginFactory: () => Plugin,
viteConfig: InlineConfig = {},
vitePort: number = 3100): TestPlugin {
let teardownVite: () => Promise<void>;
return {
name: 'playwright-vite-plugin',
name,
setup: async (config: FullConfig, configDirectory: string, suite: Suite) => {
const use = config.projects[0].use as any;
const viteConfig: InlineConfig = use.viteConfig || {};
const port = use.vitePort || 3100;
configDir = configDirectory;
process.env.PLAYWRIGHT_TEST_BASE_URL = `http://localhost:${port}/playwright/index.html`;
viteConfig.root = viteConfig.root || configDir;
viteConfig.plugins = viteConfig.plugins || [
frameworkPluginFactory()
];
const files = new Set<string>();
for (const project of suite.suites) {
for (const file of project.suites)
files.add(file.location!.file);
}
const registerSource = await fs.promises.readFile(registerSourceFile, 'utf-8');
viteConfig.plugins.push(vitePlugin(registerSource, [...files]));
viteConfig.configFile = viteConfig.configFile || false;
viteConfig.define = viteConfig.define || {};
viteConfig.define.__VUE_PROD_DEVTOOLS__ = true;
viteConfig.css = viteConfig.css || {};
viteConfig.css.devSourcemap = true;
viteConfig.preview = { port };
viteConfig.build = {
target: 'esnext',
minify: false,
rollupOptions: {
treeshake: false,
input: {
index: path.join(viteConfig.root, 'playwright', 'index.html')
},
},
sourcemap: true,
outDir: viteConfig?.build?.outDir || path.join(viteConfig.root, './dist-pw/')
};
const { build, preview } = require('vite');
await build(viteConfig);
previewServer = await preview(viteConfig);
teardownVite = await require('./vitePluginSetup').setup(registerSourceFile, frameworkPluginFactory, configDirectory, suite, viteConfig, vitePort);
},
teardown: async () => {
await new Promise<void>((f, r) => previewServer.httpServer.close(err => {
if (err)
r(err);
else
f();
}));
await teardownVite();
},
fixtures
};
}
const imports: Map<string, ComponentInfo> = new Map();
const fixtures: Fixtures<PlaywrightTestArgs & PlaywrightTestOptions & { mount: (component: any, options: any) => Promise<Locator> }, PlaywrightWorkerArgs & { _workerPage: Page }> = {
_workerPage: [async ({ browser }, use) => {
const page = await (browser as any)._wrapApiCall(async () => {
const page = await browser.newPage();
await page.addInitScript('navigator.serviceWorker.register = () => {}');
return page;
});
await use(page);
}, { scope: 'worker' }],
function vitePlugin(registerSource: string, files: string[]): Plugin {
return {
name: 'playwright:component-index',
context: async ({ page }, use) => {
await use(page.context());
},
configResolved: async config => {
page: async ({ _workerPage }, use) => {
await use(_workerPage);
},
for (const file of files) {
const text = await fs.promises.readFile(file, 'utf-8');
const ast = parse(text, { errorRecovery: true, plugins: ['typescript', 'jsx'], sourceType: 'module' });
const components = collectComponentUsages(ast);
traverse(ast, {
enter: p => {
if (t.isImportDeclaration(p.node)) {
const importNode = p.node;
if (!t.isStringLiteral(importNode.source))
return;
for (const specifier of importNode.specifiers) {
if (!components.names.has(specifier.local.name))
continue;
if (t.isImportNamespaceSpecifier(specifier))
continue;
const info = componentInfo(specifier, importNode.source.value, file);
imports.set(info.fullName, info);
}
}
}
});
}
},
transform: async (content, id) => {
if (!id.endsWith('playwright/index.ts') && !id.endsWith('playwright/index.tsx') && !id.endsWith('playwright/index.js'))
return;
const folder = path.dirname(id);
const lines = [content, ''];
lines.push(registerSource);
for (const [alias, value] of imports) {
const importPath = value.isModuleOrAlias ? value.importPath : './' + path.relative(folder, value.importPath).replace(/\\/g, '/');
if (value.importedName)
lines.push(`import { ${value.importedName} as ${alias} } from '${importPath}';`);
else
lines.push(`import ${alias} from '${importPath}';`);
}
lines.push(`register({ ${[...imports.keys()].join(',\n ')} });`);
return lines.join('\n');
},
};
}
mount: async ({ page, viewport }, use) => {
await use(async (component, options) => {
const selector = await mount(page, component, options, process.env.PLAYWRIGHT_VITE_PLUGIN_GALLERY!, viewport || { width: 1280, height: 720 });
return page.locator(selector);
});
},
};

Просмотреть файл

@ -0,0 +1,129 @@
/**
* 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.
*/
import type { Suite } from '../../types/testReporter';
import type { InlineConfig, Plugin } from 'vite';
import type { ComponentInfo } from '../tsxTransform';
import fs from 'fs';
import path from 'path';
import { parse, traverse, types as t } from '../babelBundle';
import { collectComponentUsages, componentInfo } from '../tsxTransform';
const { build, preview } = require('vite') as typeof import('vite');
export const setup = async (registerSourceFile: string, frameworkPluginFactory: () => Plugin, configDirectory: string, suite: Suite, viteConfig: InlineConfig, vitePort: number) => {
process.env.PLAYWRIGHT_VITE_PLUGIN_GALLERY = `http://localhost:${vitePort}/playwright/index.html`;
viteConfig.root = viteConfig.root || configDirectory;
viteConfig.plugins = viteConfig.plugins || [
frameworkPluginFactory()
];
const files = new Set<string>();
for (const project of suite.suites) {
for (const file of project.suites)
files.add(file.location!.file);
}
const registerSource = await fs.promises.readFile(registerSourceFile, 'utf-8');
viteConfig.plugins.push(generateGalleryPlugin(registerSource, [...files]));
viteConfig.configFile = viteConfig.configFile || false;
viteConfig.define = viteConfig.define || {};
viteConfig.define.__VUE_PROD_DEVTOOLS__ = true;
viteConfig.css = viteConfig.css || {};
viteConfig.css.devSourcemap = true;
viteConfig.preview = { port: vitePort };
viteConfig.build = {
target: 'esnext',
minify: false,
rollupOptions: {
treeshake: false,
input: {
index: path.join(viteConfig.root, 'playwright', 'index.html')
},
},
sourcemap: true,
outDir: viteConfig?.build?.outDir || path.join(viteConfig.root, './dist-pw/')
};
await build(viteConfig);
const previewServer = await preview(viteConfig);
const teardown = async () => {
await new Promise<void>((f, r) => previewServer.httpServer.close(err => {
if (err)
r(err);
else
f();
}));
};
return teardown;
};
const imports: Map<string, ComponentInfo> = new Map();
function generateGalleryPlugin(registerSource: string, files: string[]): Plugin {
return {
name: 'playwright:component-index',
configResolved: async config => {
for (const file of files) {
const text = await fs.promises.readFile(file, 'utf-8');
const ast = parse(text, { errorRecovery: true, plugins: ['typescript', 'jsx'], sourceType: 'module' });
const components = collectComponentUsages(ast);
traverse(ast, {
enter: p => {
if (t.isImportDeclaration(p.node)) {
const importNode = p.node;
if (!t.isStringLiteral(importNode.source))
return;
for (const specifier of importNode.specifiers) {
if (!components.names.has(specifier.local.name))
continue;
if (t.isImportNamespaceSpecifier(specifier))
continue;
const info = componentInfo(specifier, importNode.source.value, file);
imports.set(info.fullName, info);
}
}
}
});
}
},
transform: async (content, id) => {
if (!id.endsWith('playwright/index.ts') && !id.endsWith('playwright/index.tsx') && !id.endsWith('playwright/index.js'))
return;
const folder = path.dirname(id);
const lines = [content, ''];
lines.push(registerSource);
for (const [alias, value] of imports) {
const importPath = value.isModuleOrAlias ? value.importPath : './' + path.relative(folder, value.importPath).replace(/\\/g, '/');
if (value.importedName)
lines.push(`import { ${value.importedName} as ${alias} } from '${importPath}';`);
else
lines.push(`import ${alias} from '${importPath}';`);
}
lines.push(`register({ ${[...imports.keys()].join(',\n ')} });`);
return lines.join('\n');
},
};
}

15
packages/playwright-test/types/test.d.ts поставляемый
Просмотреть файл

@ -370,9 +370,8 @@ type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never });
*
*/
export interface TestPlugin {
fixtures?: Fixtures;
name: string;
fixtures?: Fixtures;
/**
* @param config
* @param configDir
@ -475,7 +474,7 @@ interface TestConfig {
*
*/
webServer?: TestConfigWebServer;
plugins?: TestPlugin[],
plugins?: (TestPlugin | string | [string, any])[],
/**
* Configuration for the `expect` assertion library. Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts).
*
@ -2620,7 +2619,7 @@ export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
* ```
*
*/
export interface PlaywrightTestOptions {
export interface PlaywrightTestOptions extends PlaywrightTest.TestOptions {
/**
* Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted.
*/
@ -2808,7 +2807,7 @@ export interface PlaywrightWorkerArgs {
* [fixtures.context](https://playwright.dev/docs/api/class-fixtures#fixtures-context) and
* [fixtures.page](https://playwright.dev/docs/api/class-fixtures#fixtures-page).
*/
export interface PlaywrightTestArgs {
export interface PlaywrightTestArgs extends PlaywrightTest.TestArgs {
/**
* Isolated [BrowserContext] instance, created for each test. Since contexts are isolated between each other, every test
* gets a fresh environment, even when multiple tests run in a single [Browser] for maximum efficiency.
@ -2926,6 +2925,12 @@ declare global {
export namespace PlaywrightTest {
export interface Matchers<R, T = unknown> {
}
export interface TestArgs {
}
export interface TestOptions {
}
}
}
// --- ENDGLOBAL ---

Просмотреть файл

@ -14,16 +14,17 @@
* limitations under the License.
*/
import type { PlaywrightTestConfig } from '@playwright/experimental-ct-react';
import { devices } from '@playwright/experimental-ct-react';
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-react';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct({ vitePort: 3102 })],
use: {
vitePort: 3102,
trace: 'on-first-retry',
},
projects: [

17
packages/web/playwright.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/**
* 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.
*/
import '@playwright/experimental-ct-react';

Просмотреть файл

@ -15,7 +15,7 @@
*/
import React from 'react';
import { expect, test } from '@playwright/experimental-ct-react';
import { expect, test } from '@playwright/test';
import { Expandable } from './expandable';
test.use({ viewport: { width: 500, height: 500 } });

Просмотреть файл

@ -15,7 +15,7 @@
*/
import React from 'react';
import { expect, test } from '@playwright/experimental-ct-react';
import { expect, test } from '@playwright/test';
import { Source } from './source';
test.use({ viewport: { width: 500, height: 500 } });

Просмотреть файл

@ -15,7 +15,7 @@
*/
import React from 'react';
import { expect, test } from '@playwright/experimental-ct-react';
import { expect, test } from '@playwright/test';
import { SplitView } from './splitView';
test.use({ viewport: { width: 500, height: 500 } });

Просмотреть файл

@ -0,0 +1,8 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"noEmit": true,
"jsx": "react-jsx",
},
"include": ["src", "playwright.d.ts"],
}

3
tests/components/.gitignore поставляемый
Просмотреть файл

@ -1 +1,2 @@
package-lock.json
package-lock.json
dist-pw

Просмотреть файл

@ -14,13 +14,15 @@
* limitations under the License.
*/
import { type PlaywrightTestConfig, devices } from '@playwright/experimental-ct-react';
import { type PlaywrightTestConfig, devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-react';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct()],
use: {
trace: 'on-first-retry',
},

17
tests/components/ct-react-vite/playwright.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/**
* 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.
*/
import '@playwright/experimental-ct-react';

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-react';
import { test, expect } from '@playwright/test';
import App from './App';
test.use({ viewport: { width: 500, height: 500 } });

Просмотреть файл

@ -16,6 +16,6 @@
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"include": ["src", "playwright.d.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}

Просмотреть файл

@ -14,13 +14,15 @@
* limitations under the License.
*/
import { type PlaywrightTestConfig, devices } from '@playwright/experimental-ct-react';
import { type PlaywrightTestConfig, devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-react';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct()],
use: {
trace: 'on-first-retry',
},

17
tests/components/ct-react/playwright.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/**
* 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.
*/
import '@playwright/experimental-ct-react';

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-react';
import { test, expect } from '@playwright/test';
import App from './App';
test.use({ viewport: { width: 500, height: 500 } });

Просмотреть файл

@ -21,6 +21,7 @@
"jsx": "react-jsx"
},
"include": [
"src"
"src",
"playwright.d.ts"
]
}

Просмотреть файл

@ -14,14 +14,16 @@
* limitations under the License.
*/
import type { PlaywrightTestConfig } from '@playwright/experimental-ct-svelte';
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-svelte';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct()],
use: {
trace: 'on-first-retry',
},

17
tests/components/ct-svelte-kit/playwright.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/**
* 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.
*/
import '@playwright/experimental-ct-svelte';

Просмотреть файл

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { test, expect } from '@playwright/experimental-ct-svelte';
import { test, expect } from '@playwright/test';
import Counter from './Counter.svelte';
test.use({ viewport: { width: 500, height: 500 } });

4
tests/components/ct-svelte-kit/svelte.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,4 @@
declare module '*.svelte' {
const value: any; // Add better type definitions here if desired.
export default value;
}

Просмотреть файл

@ -0,0 +1,8 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"noEmit": true,
"jsx": "react-jsx",
},
"include": ["src", "playwright.d.ts", "selte.d.ts"],
}

Просмотреть файл

@ -14,14 +14,16 @@
* limitations under the License.
*/
import type { PlaywrightTestConfig } from '@playwright/experimental-ct-svelte';
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-svelte';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct()],
use: {
trace: 'on-first-retry',
},

17
tests/components/ct-svelte-vite/playwright.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/**
* 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.
*/
import '@playwright/experimental-ct-svelte';

Просмотреть файл

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { test, expect } from '@playwright/experimental-ct-svelte';
import { test, expect } from '@playwright/test';
import Counter from './Counter.svelte';
test.use({ viewport: { width: 500, height: 500 } });

Просмотреть файл

@ -15,6 +15,6 @@
"allowJs": true,
"checkJs": true
},
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte", "playwright.d.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}

Просмотреть файл

@ -14,14 +14,16 @@
* limitations under the License.
*/
import type { PlaywrightTestConfig } from '@playwright/experimental-ct-svelte';
import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-svelte';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct()],
use: {
trace: 'on-first-retry',
},

17
tests/components/ct-svelte/playwright.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/**
* 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.
*/
import '@playwright/experimental-ct-svelte';

Просмотреть файл

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { test, expect } from '@playwright/experimental-ct-svelte';
import { test, expect } from '@playwright/test';
import App from './App.svelte';
test.use({ viewport: { width: 500, height: 500 } });

Просмотреть файл

@ -0,0 +1,8 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"noEmit": true,
"jsx": "react-jsx",
},
"include": ["src", "playwright.d.ts", "svelte.d.ts"],
}

Просмотреть файл

@ -14,13 +14,15 @@
* limitations under the License.
*/
import { type PlaywrightTestConfig, devices } from '@playwright/experimental-ct-vue';
import { type PlaywrightTestConfig, devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-vue';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct()],
use: {
trace: 'on-first-retry',
},

17
tests/components/ct-vue-cli/playwright.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/**
* 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.
*/
import '@playwright/experimental-ct-vue';

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import Button from './components/Button.vue'
import DefaultSlot from './components/DefaultSlot.vue'
import NamedSlots from './components/NamedSlots.vue'

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import Button from './components/Button.vue'
import DefaultSlot from './components/DefaultSlot.vue'

Просмотреть файл

@ -14,13 +14,15 @@
* limitations under the License.
*/
import { type PlaywrightTestConfig, devices } from '@playwright/experimental-ct-vue';
import { type PlaywrightTestConfig, devices } from '@playwright/test';
import ct from '@playwright/experimental-ct-vue';
const config: PlaywrightTestConfig = {
testDir: 'src',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'html',
plugins: [ct()],
use: {
trace: 'on-first-retry',
},

17
tests/components/ct-vue-vite/playwright.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/**
* 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.
*/
import '@playwright/experimental-ct-vue';

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import Button from './components/Button.vue'
import DefaultSlot from './components/DefaultSlot.vue'
import NamedSlots from './components/NamedSlots.vue'

Просмотреть файл

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/experimental-ct-vue'
import { test, expect } from '@playwright/test'
import Button from './components/Button.vue'
import DefaultSlot from './components/DefaultSlot.vue'

Просмотреть файл

@ -0,0 +1,8 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"noEmit": true,
"jsx": "react-jsx",
},
"include": ["src", "vue.d.ts", "playwright.d.ts"],
}

9
tests/config/experimental.d.ts поставляемый
Просмотреть файл

@ -17030,9 +17030,8 @@ type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never });
*
*/
export interface TestPlugin {
fixtures?: Fixtures;
name: string;
fixtures?: Fixtures;
/**
* @param config
* @param configDir
@ -17135,7 +17134,7 @@ interface TestConfig {
*
*/
webServer?: TestConfigWebServer;
plugins?: TestPlugin[],
plugins?: (TestPlugin | string | [string, any])[],
/**
* Configuration for the `expect` assertion library. Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts).
*
@ -19365,7 +19364,7 @@ export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
* ```
*
*/
export interface PlaywrightTestOptions {
export interface PlaywrightTestOptions extends PlaywrightTest.TestOptions {
/**
* Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted.
*/
@ -19553,7 +19552,7 @@ export interface PlaywrightWorkerArgs {
* [fixtures.context](https://playwright.dev/docs/api/class-fixtures#fixtures-context) and
* [fixtures.page](https://playwright.dev/docs/api/class-fixtures#fixtures-page).
*/
export interface PlaywrightTestArgs {
export interface PlaywrightTestArgs extends PlaywrightTest.TestArgs {
/**
* Isolated [BrowserContext] instance, created for each test. Since contexts are isolated between each other, every test
* gets a fresh environment, even when multiple tests run in a single [Browser] for maximum efficiency.

Просмотреть файл

@ -93,7 +93,7 @@ class TypesGenerator {
let overrides = await parseOverrides(overridesFile, className => {
const docClass = this.docClassForName(className);
if (!docClass)
if (!docClass || !this.shouldGenerate(className))
return '';
handledClasses.add(className);
return this.writeComment(docClass.comment) + '\n';
@ -573,6 +573,7 @@ class TypesGenerator {
'PlaywrightWorkerOptions.defaultBrowserType',
'PlaywrightWorkerArgs.playwright',
'Matchers',
'TestArgs',
]),
doNotExportClassNames: new Set([...assertionClasses, 'TestProject']),
includeExperimental,

13
utils/generate_types/overrides-test.d.ts поставляемый
Просмотреть файл

@ -57,13 +57,14 @@ export interface FullProject<TestArgs = {}, WorkerArgs = {}> {
type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never });
export interface TestPlugin {
name: string;
fixtures?: Fixtures;
}
interface TestConfig {
reporter?: LiteralUnion<'list'|'dot'|'line'|'github'|'json'|'junit'|'null'|'html', string> | ReporterDescription[];
webServer?: TestConfigWebServer;
plugins?: TestPlugin[],
plugins?: (TestPlugin | string | [string, any])[],
}
export interface Config<TestArgs = {}, WorkerArgs = {}> extends TestConfig {
@ -212,7 +213,7 @@ export interface PlaywrightWorkerOptions {
export type TraceMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
export interface PlaywrightTestOptions {
export interface PlaywrightTestOptions extends PlaywrightTest.TestOptions {
acceptDownloads: boolean | undefined;
bypassCSP: boolean | undefined;
colorScheme: ColorScheme | undefined;
@ -244,7 +245,7 @@ export interface PlaywrightWorkerArgs {
browser: Browser;
}
export interface PlaywrightTestArgs {
export interface PlaywrightTestArgs extends PlaywrightTest.TestArgs {
context: BrowserContext;
page: Page;
request: APIRequestContext;
@ -317,6 +318,12 @@ declare global {
export namespace PlaywrightTest {
export interface Matchers<R, T = unknown> {
}
export interface TestArgs {
}
export interface TestOptions {
}
}
}
// --- ENDGLOBAL ---