chore: try serializing the config instead of requiring it in the worker (#13839)
This commit is contained in:
Родитель
4b682f9f13
Коммит
a1b10c3856
|
@ -16,12 +16,15 @@
|
|||
|
||||
import type { TestError } from '../types/testReporter';
|
||||
import type { ConfigCLIOverrides } from './runner';
|
||||
import type { TestStatus } from './types';
|
||||
import type { FullConfigInternal, TestStatus } from './types';
|
||||
|
||||
export type SerializedLoaderData = {
|
||||
overrides: ConfigCLIOverrides;
|
||||
configFile: { file: string } | { configDir: string };
|
||||
config: FullConfigInternal;
|
||||
configFile: string | undefined;
|
||||
configDir: string;
|
||||
overridesForLegacyConfigMode?: ConfigCLIOverrides;
|
||||
};
|
||||
|
||||
export type WorkerInitParams = {
|
||||
workerIndex: number;
|
||||
parallelIndex: number;
|
||||
|
|
|
@ -25,7 +25,6 @@ import * as path from 'path';
|
|||
import * as url from 'url';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { ProjectImpl } from './project';
|
||||
import type { BuiltInReporter, ConfigCLIOverrides } from './runner';
|
||||
import type { Reporter } from '../types/testReporter';
|
||||
import { builtInReporters } from './runner';
|
||||
|
@ -45,7 +44,6 @@ export class Loader {
|
|||
private _fullConfig: FullConfigInternal;
|
||||
private _configDir: string = '';
|
||||
private _configFile: string | undefined;
|
||||
private _projects: ProjectImpl[] = [];
|
||||
|
||||
constructor(configCLIOverrides?: ConfigCLIOverrides) {
|
||||
this._configCLIOverrides = configCLIOverrides || {};
|
||||
|
@ -53,12 +51,20 @@ export class Loader {
|
|||
}
|
||||
|
||||
static async deserialize(data: SerializedLoaderData): Promise<Loader> {
|
||||
const loader = new Loader(data.overrides);
|
||||
if ('file' in data.configFile)
|
||||
await loader.loadConfigFile(data.configFile.file);
|
||||
else
|
||||
await loader.loadEmptyConfig(data.configFile.configDir);
|
||||
return loader;
|
||||
if (process.env.PLAYWRIGHT_LEGACY_CONFIG_MODE) {
|
||||
const loader = new Loader(data.overridesForLegacyConfigMode);
|
||||
if (data.configFile)
|
||||
await loader.loadConfigFile(data.configFile);
|
||||
else
|
||||
await loader.loadEmptyConfig(data.configDir);
|
||||
return loader;
|
||||
} else {
|
||||
const loader = new Loader();
|
||||
loader._configFile = data.configFile;
|
||||
loader._configDir = data.configDir;
|
||||
loader._fullConfig = data.config;
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
|
||||
async loadConfigFile(file: string): Promise<FullConfigInternal> {
|
||||
|
@ -107,6 +113,8 @@ export class Loader {
|
|||
config.projects = takeFirst(this._configCLIOverrides.projects, config.projects as any);
|
||||
config.workers = takeFirst(this._configCLIOverrides.workers, config.workers);
|
||||
config.use = mergeObjects(config.use, this._configCLIOverrides.use);
|
||||
for (const project of config.projects || [])
|
||||
this._applyCLIOverridesToProject(project);
|
||||
|
||||
// 3. Run configure plugins phase.
|
||||
for (const plugin of config.plugins || [])
|
||||
|
@ -155,11 +163,7 @@ export class Loader {
|
|||
this._fullConfig.workers = takeFirst(config.workers, baseFullConfig.workers);
|
||||
this._fullConfig.webServer = takeFirst(config.webServer, baseFullConfig.webServer);
|
||||
this._fullConfig._plugins = takeFirst(config.plugins, baseFullConfig._plugins);
|
||||
|
||||
const projects: Project[] = this._configCLIOverrides.projects || config.projects || [config];
|
||||
for (const project of projects)
|
||||
this._addProject(config, project, throwawayArtifactsPath);
|
||||
this._fullConfig.projects = this._projects.map(p => p.config);
|
||||
this._fullConfig.projects = (config.projects || [config]).map(p => this._resolveProject(config, p, throwawayArtifactsPath));
|
||||
}
|
||||
|
||||
async loadTestFile(file: string, environment: 'runner' | 'worker') {
|
||||
|
@ -228,18 +232,28 @@ export class Loader {
|
|||
return this._fullConfig;
|
||||
}
|
||||
|
||||
projects() {
|
||||
return this._projects;
|
||||
}
|
||||
|
||||
serialize(): SerializedLoaderData {
|
||||
return {
|
||||
configFile: this._configFile ? { file: this._configFile } : { configDir: this._configDir },
|
||||
overrides: this._configCLIOverrides,
|
||||
const result: SerializedLoaderData = {
|
||||
configFile: this._configFile,
|
||||
configDir: this._configDir,
|
||||
config: this._fullConfig,
|
||||
};
|
||||
if (process.env.PLAYWRIGHT_LEGACY_CONFIG_MODE)
|
||||
result.overridesForLegacyConfigMode = this._configCLIOverrides;
|
||||
return result;
|
||||
}
|
||||
|
||||
private _addProject(config: Config, projectConfig: Project, throwawayArtifactsPath: string) {
|
||||
private _applyCLIOverridesToProject(projectConfig: Project) {
|
||||
projectConfig.fullyParallel = takeFirst(this._configCLIOverrides.fullyParallel, projectConfig.fullyParallel);
|
||||
projectConfig.grep = takeFirst(this._configCLIOverrides.grep, projectConfig.grep);
|
||||
projectConfig.grepInvert = takeFirst(this._configCLIOverrides.grepInvert, projectConfig.grepInvert);
|
||||
projectConfig.outputDir = takeFirst(this._configCLIOverrides.outputDir, projectConfig.outputDir);
|
||||
projectConfig.repeatEach = takeFirst(this._configCLIOverrides.repeatEach, projectConfig.repeatEach);
|
||||
projectConfig.retries = takeFirst(this._configCLIOverrides.retries, projectConfig.retries);
|
||||
projectConfig.timeout = takeFirst(this._configCLIOverrides.timeout, projectConfig.timeout);
|
||||
}
|
||||
|
||||
private _resolveProject(config: Config, projectConfig: Project, throwawayArtifactsPath: string): FullProjectInternal {
|
||||
// Resolve all config dirs relative to configDir.
|
||||
if (projectConfig.testDir !== undefined)
|
||||
projectConfig.testDir = path.resolve(this._configDir, projectConfig.testDir);
|
||||
|
@ -250,21 +264,13 @@ export class Loader {
|
|||
if (projectConfig.snapshotDir !== undefined)
|
||||
projectConfig.snapshotDir = path.resolve(this._configDir, projectConfig.snapshotDir);
|
||||
|
||||
projectConfig.fullyParallel = takeFirst(this._configCLIOverrides.fullyParallel, projectConfig.fullyParallel);
|
||||
projectConfig.grep = takeFirst(this._configCLIOverrides.grep, projectConfig.grep);
|
||||
projectConfig.grepInvert = takeFirst(this._configCLIOverrides.grepInvert, projectConfig.grepInvert);
|
||||
projectConfig.outputDir = takeFirst(this._configCLIOverrides.outputDir, projectConfig.outputDir);
|
||||
projectConfig.repeatEach = takeFirst(this._configCLIOverrides.repeatEach, projectConfig.repeatEach);
|
||||
projectConfig.retries = takeFirst(this._configCLIOverrides.retries, projectConfig.retries);
|
||||
projectConfig.timeout = takeFirst(this._configCLIOverrides.timeout, projectConfig.timeout);
|
||||
|
||||
const testDir = takeFirst(projectConfig.testDir, config.testDir, this._configDir);
|
||||
|
||||
const outputDir = takeFirst(projectConfig.outputDir, config.outputDir, path.join(throwawayArtifactsPath, 'test-results'));
|
||||
const snapshotDir = takeFirst(projectConfig.snapshotDir, config.snapshotDir, testDir);
|
||||
const name = takeFirst(projectConfig.name, config.name, '');
|
||||
const screenshotsDir = takeFirst((projectConfig as any).screenshotsDir, (config as any).screenshotsDir, path.join(testDir, '__screenshots__', process.platform, name));
|
||||
const fullProject: FullProjectInternal = {
|
||||
return {
|
||||
_fullyParallel: takeFirst(projectConfig.fullyParallel, config.fullyParallel, undefined),
|
||||
_expect: takeFirst(projectConfig.expect, config.expect, undefined),
|
||||
grep: takeFirst(projectConfig.grep, config.grep, baseFullConfig.grep),
|
||||
|
@ -282,7 +288,6 @@ export class Loader {
|
|||
timeout: takeFirst(projectConfig.timeout, config.timeout, defaultTimeout),
|
||||
use: mergeObjects(config.use, projectConfig.use),
|
||||
};
|
||||
this._projects.push(new ProjectImpl(fullProject, this._projects.length));
|
||||
}
|
||||
|
||||
private async _requireOrImport(file: string) {
|
||||
|
|
|
@ -32,9 +32,9 @@ export class ProjectImpl {
|
|||
this.index = index;
|
||||
}
|
||||
|
||||
private buildTestTypePool(testType: TestTypeImpl): FixturePool {
|
||||
private _buildTestTypePool(testType: TestTypeImpl): FixturePool {
|
||||
if (!this.testTypePools.has(testType)) {
|
||||
const fixtures = this.resolveFixtures(testType, this.config.use);
|
||||
const fixtures = this._resolveFixtures(testType, this.config.use);
|
||||
const pool = new FixturePool(fixtures);
|
||||
this.testTypePools.set(testType, pool);
|
||||
}
|
||||
|
@ -42,9 +42,9 @@ export class ProjectImpl {
|
|||
}
|
||||
|
||||
// TODO: we can optimize this function by building the pool inline in cloneSuite
|
||||
private buildPool(test: TestCase): FixturePool {
|
||||
private _buildPool(test: TestCase): FixturePool {
|
||||
if (!this.testPools.has(test)) {
|
||||
let pool = this.buildTestTypePool(test._testType);
|
||||
let pool = this._buildTestTypePool(test._testType);
|
||||
|
||||
const parents: Suite[] = [];
|
||||
for (let parent: Suite | undefined = test.parent; parent; parent = parent.parent)
|
||||
|
@ -88,7 +88,7 @@ export class ProjectImpl {
|
|||
to._entries.pop();
|
||||
to.tests.pop();
|
||||
} else {
|
||||
const pool = this.buildPool(entry);
|
||||
const pool = this._buildPool(entry);
|
||||
test._workerHash = `run${this.index}-${pool.digest}-repeat${repeatEachIndex}`;
|
||||
test._pool = pool;
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ export class ProjectImpl {
|
|||
return this._cloneEntries(suite, result, repeatEachIndex, filter, '') ? result : undefined;
|
||||
}
|
||||
|
||||
private resolveFixtures(testType: TestTypeImpl, configUse: Fixtures): FixturesWithLocation[] {
|
||||
private _resolveFixtures(testType: TestTypeImpl, configUse: Fixtures): FixturesWithLocation[] {
|
||||
return testType.fixtures.map(f => {
|
||||
const configKeys = new Set(Object.keys(configUse || {}));
|
||||
const resolved = { ...f.fixtures };
|
||||
|
|
|
@ -37,8 +37,8 @@ import JSONReporter from './reporters/json';
|
|||
import JUnitReporter from './reporters/junit';
|
||||
import EmptyReporter from './reporters/empty';
|
||||
import HtmlReporter from './reporters/html';
|
||||
import type { ProjectImpl } from './project';
|
||||
import type { Config } from './types';
|
||||
import { ProjectImpl } from './project';
|
||||
import type { Config, FullProjectInternal } from './types';
|
||||
import type { FullConfigInternal } from './types';
|
||||
import { raceAgainstTimeout } from 'playwright-core/lib/utils/timeoutRunner';
|
||||
import { SigIntWatcher } from './sigIntWatcher';
|
||||
|
@ -194,8 +194,8 @@ export class Runner {
|
|||
};
|
||||
for (const [project, files] of filesByProject) {
|
||||
report.projects.push({
|
||||
name: project.config.name,
|
||||
testDir: path.resolve(configFile, project.config.testDir),
|
||||
name: project.name,
|
||||
testDir: path.resolve(configFile, project.testDir),
|
||||
files: files
|
||||
});
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ export class Runner {
|
|||
return await this._runFiles(list, filesByProject, testFileReFilters);
|
||||
}
|
||||
|
||||
private async _collectFiles(testFileReFilters: FilePatternFilter[], projectNames?: string[]): Promise<Map<ProjectImpl, string[]>> {
|
||||
private async _collectFiles(testFileReFilters: FilePatternFilter[], projectNames?: string[]): Promise<Map<FullProjectInternal, string[]>> {
|
||||
const testFileFilter = testFileReFilters.length ? createFileMatcher(testFileReFilters.map(e => e.re)) : () => true;
|
||||
let projectsToFind: Set<string> | undefined;
|
||||
let unknownProjects: Map<string, string> | undefined;
|
||||
|
@ -220,26 +220,27 @@ export class Runner {
|
|||
unknownProjects!.set(name, n);
|
||||
});
|
||||
}
|
||||
const projects = this._loader.projects().filter(project => {
|
||||
const fullConfig = this._loader.fullConfig();
|
||||
const projects = fullConfig.projects.filter(project => {
|
||||
if (!projectsToFind)
|
||||
return true;
|
||||
const name = project.config.name.toLocaleLowerCase();
|
||||
const name = project.name.toLocaleLowerCase();
|
||||
unknownProjects!.delete(name);
|
||||
return projectsToFind.has(name);
|
||||
});
|
||||
if (unknownProjects && unknownProjects.size) {
|
||||
const names = this._loader.projects().map(p => p.config.name).filter(name => !!name);
|
||||
const names = fullConfig.projects.map(p => p.name).filter(name => !!name);
|
||||
if (!names.length)
|
||||
throw new Error(`No named projects are specified in the configuration file`);
|
||||
const unknownProjectNames = Array.from(unknownProjects.values()).map(n => `"${n}"`).join(', ');
|
||||
throw new Error(`Project(s) ${unknownProjectNames} not found. Available named projects: ${names.map(name => `"${name}"`).join(', ')}`);
|
||||
}
|
||||
|
||||
const files = new Map<ProjectImpl, string[]>();
|
||||
const files = new Map<FullProjectInternal, string[]>();
|
||||
for (const project of projects) {
|
||||
const allFiles = await collectFiles(project.config.testDir);
|
||||
const testMatch = createFileMatcher(project.config.testMatch);
|
||||
const testIgnore = createFileMatcher(project.config.testIgnore);
|
||||
const allFiles = await collectFiles(project.testDir);
|
||||
const testMatch = createFileMatcher(project.testMatch);
|
||||
const testIgnore = createFileMatcher(project.testIgnore);
|
||||
const extensions = ['.js', '.ts', '.mjs', '.tsx', '.jsx'];
|
||||
const testFileExtension = (file: string) => extensions.includes(path.extname(file));
|
||||
const testFiles = allFiles.filter(file => !testIgnore(file) && testMatch(file) && testFileFilter(file) && testFileExtension(file));
|
||||
|
@ -248,7 +249,7 @@ export class Runner {
|
|||
return files;
|
||||
}
|
||||
|
||||
private async _runFiles(list: boolean, filesByProject: Map<ProjectImpl, string[]>, testFileReFilters: FilePatternFilter[]): Promise<FullResult> {
|
||||
private async _runFiles(list: boolean, filesByProject: Map<FullProjectInternal, string[]>, testFileReFilters: FilePatternFilter[]): Promise<FullResult> {
|
||||
const allTestFiles = new Set<string>();
|
||||
for (const files of filesByProject.values())
|
||||
files.forEach(file => allTestFiles.add(file));
|
||||
|
@ -293,19 +294,20 @@ export class Runner {
|
|||
const outputDirs = new Set<string>();
|
||||
const rootSuite = new Suite('');
|
||||
for (const [project, files] of filesByProject) {
|
||||
const grepMatcher = createTitleMatcher(project.config.grep);
|
||||
const grepInvertMatcher = project.config.grepInvert ? createTitleMatcher(project.config.grepInvert) : null;
|
||||
const projectSuite = new Suite(project.config.name);
|
||||
projectSuite._projectConfig = project.config;
|
||||
if (project.config._fullyParallel)
|
||||
const projectImpl = new ProjectImpl(project, config.projects.indexOf(project));
|
||||
const grepMatcher = createTitleMatcher(project.grep);
|
||||
const grepInvertMatcher = project.grepInvert ? createTitleMatcher(project.grepInvert) : null;
|
||||
const projectSuite = new Suite(project.name);
|
||||
projectSuite._projectConfig = project;
|
||||
if (project._fullyParallel)
|
||||
projectSuite._parallelMode = 'parallel';
|
||||
rootSuite._addSuite(projectSuite);
|
||||
for (const file of files) {
|
||||
const fileSuite = fileSuites.get(file);
|
||||
if (!fileSuite)
|
||||
continue;
|
||||
for (let repeatEachIndex = 0; repeatEachIndex < project.config.repeatEach; repeatEachIndex++) {
|
||||
const cloned = project.cloneFileSuite(fileSuite, repeatEachIndex, test => {
|
||||
for (let repeatEachIndex = 0; repeatEachIndex < project.repeatEach; repeatEachIndex++) {
|
||||
const cloned = projectImpl.cloneFileSuite(fileSuite, repeatEachIndex, test => {
|
||||
const grepTitle = test.titlePath().join(' ');
|
||||
if (grepInvertMatcher?.(grepTitle))
|
||||
return false;
|
||||
|
@ -315,7 +317,7 @@ export class Runner {
|
|||
projectSuite._addSuite(cloned);
|
||||
}
|
||||
}
|
||||
outputDirs.add(project.config.outputDir);
|
||||
outputDirs.add(project.outputDir);
|
||||
}
|
||||
|
||||
// 7. Fail when no tests.
|
||||
|
|
|
@ -85,12 +85,13 @@ export class TestInfoImpl implements TestInfo {
|
|||
|
||||
constructor(
|
||||
loader: Loader,
|
||||
projectImpl: ProjectImpl,
|
||||
workerParams: WorkerInitParams,
|
||||
test: TestCase,
|
||||
retry: number,
|
||||
addStepImpl: (data: Omit<TestStepInternal, 'complete'>) => TestStepInternal,
|
||||
) {
|
||||
this._projectImpl = loader.projects()[workerParams.projectIndex];
|
||||
this._projectImpl = projectImpl;
|
||||
this._test = test;
|
||||
this._addStepImpl = addStepImpl;
|
||||
this._startTime = monotonicTime();
|
||||
|
@ -113,10 +114,10 @@ export class TestInfoImpl implements TestInfo {
|
|||
this._timeoutManager = new TimeoutManager(this.project.timeout);
|
||||
|
||||
this.outputDir = (() => {
|
||||
const sameName = loader.projects().filter(project => project.config.name === this.project.name);
|
||||
const sameName = loader.fullConfig().projects.filter(project => project.name === this.project.name);
|
||||
let uniqueProjectNamePathSegment: string;
|
||||
if (sameName.length > 1)
|
||||
uniqueProjectNamePathSegment = this.project.name + (sameName.indexOf(this._projectImpl) + 1);
|
||||
uniqueProjectNamePathSegment = this.project.name + (sameName.indexOf(this._projectImpl.config) + 1);
|
||||
else
|
||||
uniqueProjectNamePathSegment = this.project.name;
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import { setCurrentTestInfo } from './globals';
|
|||
import { Loader } from './loader';
|
||||
import type { Suite, TestCase } from './test';
|
||||
import type { Annotation, TestError, TestStepInternal } from './types';
|
||||
import type { ProjectImpl } from './project';
|
||||
import { ProjectImpl } from './project';
|
||||
import { FixtureRunner } from './fixtures';
|
||||
import { ManualPromise } from 'playwright-core/lib/utils/manualPromise';
|
||||
import { TestInfoImpl } from './testInfo';
|
||||
|
@ -151,7 +151,7 @@ export class WorkerRunner extends EventEmitter {
|
|||
return;
|
||||
|
||||
this._loader = await Loader.deserialize(this._params.loader);
|
||||
this._project = this._loader.projects()[this._params.projectIndex];
|
||||
this._project = new ProjectImpl(this._loader.fullConfig().projects[this._params.projectIndex], this._params.projectIndex);
|
||||
}
|
||||
|
||||
async runTestGroup(runPayload: RunPayload) {
|
||||
|
@ -207,7 +207,7 @@ export class WorkerRunner extends EventEmitter {
|
|||
|
||||
private async _runTest(test: TestCase, retry: number, nextTest: TestCase | undefined) {
|
||||
let lastStepId = 0;
|
||||
const testInfo = new TestInfoImpl(this._loader, this._params, test, retry, data => {
|
||||
const testInfo = new TestInfoImpl(this._loader, this._project, this._params, test, retry, data => {
|
||||
const stepId = `${data.category}@${data.title}@${++lastStepId}`;
|
||||
let callbackHandled = false;
|
||||
const step: TestStepInternal = {
|
||||
|
|
|
@ -215,6 +215,7 @@ type RunOptions = {
|
|||
cwd?: string,
|
||||
};
|
||||
type Fixtures = {
|
||||
legacyConfigLoader: boolean;
|
||||
writeFiles: (files: Files) => Promise<string>;
|
||||
runInlineTest: (files: Files, params?: Params, env?: Env, options?: RunOptions, beforeRunPlaywrightTest?: ({ baseDir }: { baseDir: string }) => Promise<void>) => Promise<RunResult>;
|
||||
runTSC: (files: Files) => Promise<TSCResult>;
|
||||
|
@ -224,15 +225,19 @@ export const test = base
|
|||
.extend<CommonFixtures>(commonFixtures)
|
||||
.extend<ServerFixtures, ServerWorkerOptions>(serverFixtures)
|
||||
.extend<Fixtures>({
|
||||
legacyConfigLoader: [false, { option: true }],
|
||||
|
||||
writeFiles: async ({}, use, testInfo) => {
|
||||
await use(files => writeFiles(testInfo, files));
|
||||
},
|
||||
|
||||
runInlineTest: async ({ childProcess }, use, testInfo: TestInfo) => {
|
||||
runInlineTest: async ({ childProcess, legacyConfigLoader }, use, testInfo: TestInfo) => {
|
||||
await use(async (files: Files, params: Params = {}, env: Env = {}, options: RunOptions = {}, beforeRunPlaywrightTest?: ({ baseDir: string }) => Promise<void>) => {
|
||||
const baseDir = await writeFiles(testInfo, files);
|
||||
if (beforeRunPlaywrightTest)
|
||||
await beforeRunPlaywrightTest({ baseDir });
|
||||
if (legacyConfigLoader)
|
||||
env = { ...env, PLAYWRIGHT_LEGACY_CONFIG_MODE: '1' };
|
||||
return await runPlaywrightTest(childProcess, baseDir, params, env, options);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -29,7 +29,13 @@ const config: Config = {
|
|||
workers: process.env.CI ? 1 : undefined,
|
||||
preserveOutput: process.env.CI ? 'failures-only' : 'always',
|
||||
projects: [
|
||||
{ name: 'playwright-test' },
|
||||
{
|
||||
name: 'playwright-test'
|
||||
},
|
||||
{
|
||||
name: 'playwright-test-legacy-config',
|
||||
use: { legacyConfigLoader: true },
|
||||
} as any,
|
||||
],
|
||||
reporter: process.env.CI ? [
|
||||
['dot'],
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
|
||||
import { test, expect } from './playwright-test-fixtures';
|
||||
|
||||
test('should work with connectOptions', async ({ runInlineTest }) => {
|
||||
test('should work with connectOptions (legacy)', async ({ runInlineTest, legacyConfigLoader }) => {
|
||||
test.skip(!legacyConfigLoader, 'Not supported in the new mode');
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = {
|
||||
|
@ -49,6 +50,42 @@ test('should work with connectOptions', async ({ runInlineTest }) => {
|
|||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('should work with connectOptions', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = { plugins: [require('./plugin')] };
|
||||
`,
|
||||
'plugin.js': `
|
||||
let server;
|
||||
module.exports = {
|
||||
configure: async (config) => {
|
||||
server = await pwt.chromium.launchServer();
|
||||
config.use = {
|
||||
connectOptions: {
|
||||
wsEndpoint: server.wsEndpoint()
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
teardown: async () => {
|
||||
await server.close();
|
||||
}
|
||||
};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test.use({ locale: 'fr-CH' });
|
||||
test('pass', async ({ page }) => {
|
||||
await page.setContent('<div>PASS</div>');
|
||||
await expect(page.locator('div')).toHaveText('PASS');
|
||||
expect(await page.evaluate(() => navigator.language)).toBe('fr-CH');
|
||||
});
|
||||
`,
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('should throw with bad connectOptions', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
import fs from 'fs';
|
||||
import { test, expect } from './playwright-test-fixtures';
|
||||
|
||||
test('event order', async ({ runInlineTest }, testInfo) => {
|
||||
test('event order', async ({ runInlineTest, legacyConfigLoader }, testInfo) => {
|
||||
test.skip(legacyConfigLoader);
|
||||
const log = testInfo.outputPath('logs.txt');
|
||||
const result = await runInlineTest({
|
||||
'log.ts': `
|
||||
|
@ -87,8 +88,6 @@ test('event order', async ({ runInlineTest }, testInfo) => {
|
|||
'a setup',
|
||||
'b setup',
|
||||
'globalSetup',
|
||||
'a configure',
|
||||
'b configure',
|
||||
'baseURL a | b | ',
|
||||
'globalTeardown',
|
||||
'b teardown',
|
||||
|
|
Загрузка…
Ссылка в новой задаче