chore: sort out base fixtures (#9809)

This commit is contained in:
Pavel Feldman 2021-10-27 07:28:53 -08:00 committed by GitHub
parent 1c88079755
commit 31faa7d9a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 375 additions and 254 deletions

View File

@ -17,7 +17,7 @@
const { start } = require('./node_modules/playwright-core/lib/outofprocess');
(async () => {
const playwright = await start();
const { playwright, stop } = await start();
console.log(`driver PID=${playwright.driverProcess.pid}`);
for (const browserType of ['chromium', 'firefox', 'webkit']) {
try {
@ -33,7 +33,7 @@ const { start } = require('./node_modules/playwright-core/lib/outofprocess');
process.exit(1);
}
}
await playwright.stop();
await stop();
console.log(`driver SUCCESS`);
})().catch(err => {
console.error(err);

View File

@ -20,12 +20,11 @@ import { Playwright } from './client/playwright';
import * as childProcess from 'child_process';
import * as path from 'path';
export async function start(env: any = {}) {
export async function start(env: any = {}): Promise<{ playwright: Playwright, stop: () => Promise<void> }> {
const client = new PlaywrightClient(env);
const playwright = await client._playwright;
(playwright as any).stop = () => client.stop();
(playwright as any).driverProcess = client._driverProcess;
return playwright;
return { playwright, stop: () => client.stop() };
}
class PlaywrightClient {

View File

@ -15,16 +15,17 @@
*/
import type { AndroidDevice, BrowserContext } from 'playwright-core';
import { CommonWorkerFixtures, baseTest } from '../config/baseTest';
import type { Fixtures } from '@playwright/test';
import type { Fixtures, PlaywrightWorkerOptions } from '@playwright/test';
import { PageTestFixtures } from '../page/pageTest';
import { TestModeWorkerFixtures } from '../config/testModeFixtures';
import { browserTest } from '../config/browserTest';
export { expect } from '@playwright/test';
type AndroidWorkerFixtures = {
androidDevice: AndroidDevice;
};
export const androidFixtures: Fixtures<PageTestFixtures, AndroidWorkerFixtures & { androidContext: BrowserContext }, {}, CommonWorkerFixtures> = {
export const androidFixtures: Fixtures<PageTestFixtures, AndroidWorkerFixtures & { androidContext: BrowserContext }, {}, PlaywrightWorkerOptions & TestModeWorkerFixtures> = {
androidDevice: [ async ({ playwright }, run) => {
const device = (await playwright._android.devices())[0];
await device.shell('am force-stop org.chromium.webview_shell');
@ -67,4 +68,4 @@ export const androidFixtures: Fixtures<PageTestFixtures, AndroidWorkerFixtures &
},
};
export const androidTest = baseTest.extend<PageTestFixtures, AndroidWorkerFixtures>(androidFixtures as any);
export const androidTest = browserTest.extend<PageTestFixtures, AndroidWorkerFixtures>(androidFixtures as any);

View File

@ -118,7 +118,7 @@ test('selenium grid 3.141.59 standalone chromium through driver', async ({ brows
});
await waitForPort(port);
const pw = await start({
const { playwright: pw, stop } = await start({
SELENIUM_REMOTE_URL: `http://localhost:${port}/wd/hub`,
});
const browser = await pw.chromium.launch(browserOptions);
@ -128,7 +128,7 @@ test('selenium grid 3.141.59 standalone chromium through driver', async ({ brows
await expect(page).toHaveTitle('Hello world');
// Note: it is important to stop the driver without explicitly closing the browser.
// It should terminate selenium session in this case.
await pw.stop();
await stop();
expect(grid.output).toContain('Starting ChromeDriver');
expect(grid.output).toContain('Started new session');

View File

@ -14,16 +14,16 @@
* limitations under the License.
*/
import type { Config } from '@playwright/test';
import type { Config, PlaywrightTestOptions, PlaywrightWorkerOptions } from '@playwright/test';
import * as path from 'path';
import { test as pageTest } from '../page/pageTest';
import { androidFixtures } from '../android/androidTest';
import { PlaywrightOptionsEx } from './browserTest';
import { CommonOptions } from './baseTest';
import { ServerWorkerOptions } from './serverFixtures';
import { playwrightFixtures } from './browserTest';
const outputDir = path.join(__dirname, '..', '..', 'test-results');
const testDir = path.join(__dirname, '..');
const config: Config<CommonOptions & PlaywrightOptionsEx> = {
const config: Config<ServerWorkerOptions & PlaywrightWorkerOptions & PlaywrightTestOptions> = {
testDir,
outputDir,
timeout: 120000,
@ -52,7 +52,6 @@ config.projects.push({
name: 'android',
use: {
loopback: '10.0.2.2',
mode: 'default',
browserName: 'chromium',
},
testDir: path.join(testDir, 'android'),
@ -63,11 +62,10 @@ config.projects.push({
name: 'android',
use: {
loopback: '10.0.2.2',
mode: 'default',
browserName: 'chromium',
},
testDir: path.join(testDir, 'page'),
define: { test: pageTest, fixtures: androidFixtures },
define: { test: pageTest, fixtures: { ...playwrightFixtures, ...androidFixtures } },
metadata,
});

View File

@ -14,118 +14,16 @@
* limitations under the License.
*/
import { Fixtures, VideoMode, _baseTest } from '@playwright/test';
import * as path from 'path';
import * as fs from 'fs';
import { installCoverageHooks } from './coverage';
import { start } from '../../packages/playwright-core/lib/outofprocess';
import { GridClient } from 'playwright-core/lib/grid/gridClient';
import type { LaunchOptions, ViewportSize } from 'playwright-core';
import { commonFixtures, CommonFixtures, serverFixtures, ServerFixtures, ServerOptions } from './commonFixtures';
import { _baseTest } from '@playwright/test';
import { commonFixtures, CommonFixtures } from './commonFixtures';
import { serverFixtures, ServerFixtures, ServerWorkerOptions } from './serverFixtures';
import { coverageFixtures, CoverageWorkerOptions } from './coverageFixtures';
import { platformFixtures, PlatformWorkerFixtures } from './platformFixtures';
import { testModeFixtures, TestModeWorkerFixtures } from './testModeFixtures';
export type BrowserName = 'chromium' | 'firefox' | 'webkit';
type Mode = 'default' | 'driver' | 'service';
type BaseOptions = {
mode: Mode;
browserName: BrowserName;
channel: LaunchOptions['channel'];
video: VideoMode | { mode: VideoMode, size: ViewportSize };
trace: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-trace';
headless: boolean | undefined;
};
type BaseFixtures = {
platform: 'win32' | 'darwin' | 'linux';
playwright: typeof import('playwright-core');
toImpl: (rpcObject: any) => any;
isWindows: boolean;
isMac: boolean;
isLinux: boolean;
};
class DriverMode {
private _playwrightObject: any;
async setup(workerIndex: number) {
this._playwrightObject = await start();
return this._playwrightObject;
}
async teardown() {
await this._playwrightObject.stop();
}
}
class ServiceMode {
private _gridClient: GridClient;
async setup(workerIndex: number) {
this._gridClient = await GridClient.connect('http://localhost:3333');
return this._gridClient.playwright();
}
async teardown() {
await this._gridClient.close();
}
}
class DefaultMode {
async setup(workerIndex: number) {
return require('playwright-core');
}
async teardown() {
}
}
const baseFixtures: Fixtures<{}, BaseOptions & BaseFixtures> = {
mode: [ 'default', { scope: 'worker' } ],
browserName: [ 'chromium' , { scope: 'worker' } ],
channel: [ undefined, { scope: 'worker' } ],
video: [ undefined, { scope: 'worker' } ],
trace: [ undefined, { scope: 'worker' } ],
headless: [ undefined, { scope: 'worker' } ],
platform: [ process.platform as 'win32' | 'darwin' | 'linux', { scope: 'worker' } ],
playwright: [ async ({ mode }, run, workerInfo) => {
const modeImpl = {
default: new DefaultMode(),
service: new ServiceMode(),
driver: new DriverMode(),
}[mode];
require('playwright-core/lib/utils/utils').setUnderTest();
const playwright = await modeImpl.setup(workerInfo.workerIndex);
await run(playwright);
await modeImpl.teardown();
}, { scope: 'worker' } ],
toImpl: [ async ({ playwright }, run) => run((playwright as any)._toImpl), { scope: 'worker' } ],
isWindows: [ process.platform === 'win32', { scope: 'worker' } ],
isMac: [ process.platform === 'darwin', { scope: 'worker' } ],
isLinux: [ process.platform === 'linux', { scope: 'worker' } ],
};
type CoverageOptions = {
coverageName?: string;
};
const coverageFixtures: Fixtures<{}, CoverageOptions & { __collectCoverage: void }> = {
coverageName: [ undefined, { scope: 'worker' } ],
__collectCoverage: [ async ({ coverageName }, run, workerInfo) => {
if (!coverageName) {
await run();
return;
}
const { coverage, uninstall } = installCoverageHooks(coverageName);
await run();
uninstall();
const coveragePath = path.join(__dirname, '..', 'coverage-report', workerInfo.workerIndex + '.json');
const coverageJSON = Array.from(coverage.keys()).filter(key => coverage.get(key));
await fs.promises.mkdir(path.dirname(coveragePath), { recursive: true });
await fs.promises.writeFile(coveragePath, JSON.stringify(coverageJSON, undefined, 2), 'utf8');
}, { scope: 'worker', auto: true } ],
};
export type CommonOptions = BaseOptions & ServerOptions & CoverageOptions;
export type CommonWorkerFixtures = CommonOptions & BaseFixtures;
export const baseTest = _baseTest.extend<CommonFixtures>(commonFixtures).extend<{}, CoverageOptions>(coverageFixtures).extend<ServerFixtures>(serverFixtures as any).extend<{}, BaseOptions & BaseFixtures>(baseFixtures);
export const baseTest = _baseTest
.extend<{}, CoverageWorkerOptions>(coverageFixtures)
.extend<{}, PlatformWorkerFixtures>(platformFixtures)
.extend<{}, TestModeWorkerFixtures>(testModeFixtures)
.extend<CommonFixtures>(commonFixtures)
.extend<ServerFixtures, ServerWorkerOptions>(serverFixtures as any);

View File

@ -22,11 +22,14 @@ import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
import { RemoteServer, RemoteServerOptions } from './remoteServer';
import { baseTest, CommonWorkerFixtures } from './baseTest';
import { baseTest } from './baseTest';
import { CommonFixtures } from './commonFixtures';
import type { ParsedStackTrace } from 'playwright-core/lib/utils/stackTrace';
import { DefaultTestMode, DriverTestMode, ServiceTestMode } from './testMode';
import { TestModeWorkerFixtures } from './testModeFixtures';
export type PlaywrightWorkerFixtures = {
playwright: typeof import('playwright-core');
_browserType: BrowserType;
_browserOptions: LaunchOptions;
browserType: BrowserType;
@ -34,6 +37,7 @@ export type PlaywrightWorkerFixtures = {
browser: Browser;
browserVersion: string;
_reuseBrowserContext: ReuseBrowserContextStorage;
toImpl: (rpcObject: any) => any;
};
type PlaywrightTestFixtures = {
@ -45,11 +49,24 @@ type PlaywrightTestFixtures = {
context: BrowserContext;
page: Page;
};
export type PlaywrightOptionsEx = PlaywrightWorkerOptions & PlaywrightTestOptions;
export const playwrightFixtures: Fixtures<PlaywrightTestOptions & PlaywrightTestFixtures, PlaywrightWorkerOptions & PlaywrightWorkerFixtures, CommonFixtures, CommonWorkerFixtures> = {
export const playwrightFixtures: Fixtures<PlaywrightTestOptions & PlaywrightTestFixtures, PlaywrightWorkerOptions & PlaywrightWorkerFixtures, CommonFixtures, TestModeWorkerFixtures> = {
hasTouch: undefined,
playwright: [ async ({ mode }, run) => {
const testMode = {
default: new DefaultTestMode(),
service: new ServiceTestMode(),
driver: new DriverTestMode(),
}[mode];
require('playwright-core/lib/utils/utils').setUnderTest();
const playwright = await testMode.setup();
await run(playwright);
await testMode.teardown();
}, { scope: 'worker' } ],
toImpl: [ async ({ playwright }, run) => run((playwright as any)._toImpl), { scope: 'worker' } ],
_browserType: [browserTypeWorkerFixture, { scope: 'worker' } ],
_browserOptions: [browserOptionsWorkerFixture, { scope: 'worker' } ],
@ -148,7 +165,7 @@ export const playwrightFixtures: Fixtures<PlaywrightTestOptions & PlaywrightTest
});
await Promise.all([...contexts.keys()].map(async context => {
const videos = context.pages().map(p => p.video()).filter(Boolean);
if (trace && !contexts.get(context)!.closed) {
if (trace === 'on' && !contexts.get(context)!.closed) {
const tracePath = testInfo.outputPath('trace.zip');
await context.tracing.stop({ path: tracePath });
testInfo.attachments.push({ name: 'trace', path: tracePath, contentType: 'application/zip' });
@ -181,6 +198,12 @@ export const playwrightFixtures: Fixtures<PlaywrightTestOptions & PlaywrightTest
}
await run(await context.newPage());
},
browserName: [ 'chromium' , { scope: 'worker' } ],
headless: [ undefined, { scope: 'worker' } ],
channel: [ undefined, { scope: 'worker' } ],
video: [ 'off', { scope: 'worker' } ],
trace: [ 'off', { scope: 'worker' } ],
};
const test = baseTest.extend<PlaywrightTestOptions & PlaywrightTestFixtures, PlaywrightWorkerOptions & PlaywrightWorkerFixtures>(playwrightFixtures);

View File

@ -17,10 +17,6 @@
import type { Fixtures } from '@playwright/test';
import { ChildProcess, execSync, spawn } from 'child_process';
import net from 'net';
import path from 'path';
import socks from 'socksv5';
import { TestServer } from '../../utils/testserver';
import { TestProxy } from './proxy';
type TestChildParams = {
command: string[],
@ -150,93 +146,3 @@ export const commonFixtures: Fixtures<CommonFixtures, {}> = {
token.canceled = true;
},
};
export type ServerOptions = {
loopback?: string;
};
export type ServerFixtures = {
server: TestServer;
httpsServer: TestServer;
socksPort: number;
proxyServer: TestProxy;
asset: (p: string) => string;
};
export type ServersInternal = ServerFixtures & { socksServer: socks.SocksServer };
export const serverFixtures: Fixtures<ServerFixtures, ServerOptions & { __servers: ServersInternal }> = {
loopback: [ undefined, { scope: 'worker' } ],
__servers: [ async ({ loopback }, run, workerInfo) => {
const assetsPath = path.join(__dirname, '..', 'assets');
const cachedPath = path.join(__dirname, '..', 'assets', 'cached');
const port = 8907 + workerInfo.workerIndex * 4;
const server = await TestServer.create(assetsPath, port, loopback);
server.enableHTTPCache(cachedPath);
const httpsPort = port + 1;
const httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort, loopback);
httpsServer.enableHTTPCache(cachedPath);
const socksServer = socks.createServer((info, accept, deny) => {
const socket = accept(true);
if (socket) {
// Catch and ignore ECONNRESET errors.
socket.on('error', () => {});
const body = '<html><title>Served by the SOCKS proxy</title></html>';
socket.end([
'HTTP/1.1 200 OK',
'Connection: close',
'Content-Type: text/html',
'Content-Length: ' + Buffer.byteLength(body),
'',
body
].join('\r\n'));
}
});
const socksPort = port + 2;
socksServer.listen(socksPort, 'localhost');
socksServer.useAuth(socks.auth.None());
const proxyPort = port + 3;
const proxyServer = await TestProxy.create(proxyPort);
await run({
asset: (p: string) => path.join(__dirname, '..', 'assets', ...p.split('/')),
server,
httpsServer,
socksPort,
proxyServer,
socksServer,
});
await Promise.all([
server.stop(),
httpsServer.stop(),
socksServer.close(),
proxyServer.stop(),
]);
}, { scope: 'worker' } ],
server: async ({ __servers }, run) => {
__servers.server.reset();
await run(__servers.server);
},
httpsServer: async ({ __servers }, run) => {
__servers.httpsServer.reset();
await run(__servers.httpsServer);
},
socksPort: async ({ __servers }, run) => {
await run(__servers.socksPort);
},
proxyServer: async ({ __servers }, run) => {
__servers.proxyServer.reset();
await run(__servers.proxyServer);
},
asset: async ({ __servers }, run) => {
await run(__servers.asset);
},
};

View File

@ -15,9 +15,6 @@
* limitations under the License.
*/
const path = require('path');
const fs = require('fs');
/**
* @param {Map<string, boolean>} apiCoverage
* @param {Object} events

View File

@ -0,0 +1,43 @@
/**
* 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 { Fixtures } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
import { installCoverageHooks } from './coverage';
export type CoverageWorkerOptions = {
coverageName?: string;
};
export const coverageFixtures: Fixtures<{}, CoverageWorkerOptions & { __collectCoverage: void }> = {
coverageName: [ undefined, { scope: 'worker' } ],
__collectCoverage: [ async ({ coverageName }, run, workerInfo) => {
if (!coverageName) {
await run();
return;
}
const { coverage, uninstall } = installCoverageHooks(coverageName);
await run();
uninstall();
const coveragePath = path.join(__dirname, '..', 'coverage-report', workerInfo.workerIndex + '.json');
const coverageJSON = Array.from(coverage.keys()).filter(key => coverage.get(key));
await fs.promises.mkdir(path.dirname(coveragePath), { recursive: true });
await fs.promises.writeFile(coveragePath, JSON.stringify(coverageJSON, undefined, 2), 'utf8');
}, { scope: 'worker', auto: true } ],
};

View File

@ -18,7 +18,10 @@ import type { Config, PlaywrightTestOptions, PlaywrightWorkerOptions } from '@pl
import * as path from 'path';
import { playwrightFixtures } from './browserTest';
import { test as pageTest } from '../page/pageTest';
import { BrowserName, CommonOptions } from './baseTest';
import { TestModeWorkerFixtures } from './testModeFixtures';
import { CoverageWorkerOptions } from './coverageFixtures';
type BrowserName = 'chromium' | 'firefox' | 'webkit';
const getExecutablePath = (browserName: BrowserName) => {
if (browserName === 'chromium' && process.env.CRPATH)
@ -46,7 +49,7 @@ const trace = !!process.env.PWTEST_TRACE;
const outputDir = path.join(__dirname, '..', '..', 'test-results');
const testDir = path.join(__dirname, '..');
const config: Config<CommonOptions & PlaywrightWorkerOptions & PlaywrightTestOptions > = {
const config: Config<CoverageWorkerOptions & PlaywrightWorkerOptions & PlaywrightTestOptions & TestModeWorkerFixtures> = {
testDir,
outputDir,
timeout: video ? 60000 : 30000,

View File

@ -14,16 +14,16 @@
* limitations under the License.
*/
import type { Config } from '@playwright/test';
import type { Config, PlaywrightTestOptions, PlaywrightWorkerOptions } from '@playwright/test';
import * as path from 'path';
import { electronFixtures } from '../electron/electronTest';
import { test as pageTest } from '../page/pageTest';
import { PlaywrightOptionsEx } from './browserTest';
import { CommonOptions } from './baseTest';
import { playwrightFixtures } from './browserTest';
import { CoverageWorkerOptions } from './coverageFixtures';
const outputDir = path.join(__dirname, '..', '..', 'test-results');
const testDir = path.join(__dirname, '..');
const config: Config<CommonOptions & PlaywrightOptionsEx> = {
const config: Config<CoverageWorkerOptions & PlaywrightWorkerOptions & PlaywrightTestOptions> = {
testDir,
outputDir,
timeout: 30000,
@ -51,7 +51,6 @@ const metadata = {
config.projects.push({
name: 'chromium', // We use 'chromium' here to share screenshots with chromium.
use: {
mode: 'default',
browserName: 'chromium',
coverageName: 'electron',
},
@ -62,12 +61,11 @@ config.projects.push({
config.projects.push({
name: 'chromium', // We use 'chromium' here to share screenshots with chromium.
use: {
mode: 'default',
browserName: 'chromium',
coverageName: 'electron',
},
testDir: path.join(testDir, 'page'),
define: { test: pageTest, fixtures: electronFixtures },
define: { test: pageTest, fixtures: { ...playwrightFixtures, ...electronFixtures } },
metadata,
});

View File

@ -0,0 +1,31 @@
/**
* 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 { Fixtures } from '@playwright/test';
export type PlatformWorkerFixtures = {
platform: 'win32' | 'darwin' | 'linux';
isWindows: boolean;
isMac: boolean;
isLinux: boolean;
};
export const platformFixtures: Fixtures<{}, PlatformWorkerFixtures> = {
platform: [ process.platform as 'win32' | 'darwin' | 'linux', { scope: 'worker' } ],
isWindows: [ process.platform === 'win32', { scope: 'worker' } ],
isMac: [ process.platform === 'darwin', { scope: 'worker' } ],
isLinux: [ process.platform === 'linux', { scope: 'worker' } ],
};

View File

@ -0,0 +1,112 @@
/**
* 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 { Fixtures } from '@playwright/test';
import path from 'path';
import socks from 'socksv5';
import { TestServer } from '../../utils/testserver';
import { TestProxy } from './proxy';
export type ServerWorkerOptions = {
loopback?: string;
};
export type ServerFixtures = {
server: TestServer;
httpsServer: TestServer;
socksPort: number;
proxyServer: TestProxy;
asset: (p: string) => string;
};
export type ServersInternal = ServerFixtures & { socksServer: socks.SocksServer };
export const serverFixtures: Fixtures<ServerFixtures, ServerWorkerOptions & { __servers: ServersInternal }> = {
loopback: [ undefined, { scope: 'worker' } ],
__servers: [ async ({ loopback }, run, workerInfo) => {
const assetsPath = path.join(__dirname, '..', 'assets');
const cachedPath = path.join(__dirname, '..', 'assets', 'cached');
const port = 8907 + workerInfo.workerIndex * 4;
const server = await TestServer.create(assetsPath, port, loopback);
server.enableHTTPCache(cachedPath);
const httpsPort = port + 1;
const httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort, loopback);
httpsServer.enableHTTPCache(cachedPath);
const socksServer = socks.createServer((info, accept, deny) => {
const socket = accept(true);
if (socket) {
// Catch and ignore ECONNRESET errors.
socket.on('error', () => {});
const body = '<html><title>Served by the SOCKS proxy</title></html>';
socket.end([
'HTTP/1.1 200 OK',
'Connection: close',
'Content-Type: text/html',
'Content-Length: ' + Buffer.byteLength(body),
'',
body
].join('\r\n'));
}
});
const socksPort = port + 2;
socksServer.listen(socksPort, 'localhost');
socksServer.useAuth(socks.auth.None());
const proxyPort = port + 3;
const proxyServer = await TestProxy.create(proxyPort);
await run({
asset: (p: string) => path.join(__dirname, '..', 'assets', ...p.split('/')),
server,
httpsServer,
socksPort,
proxyServer,
socksServer,
});
await Promise.all([
server.stop(),
httpsServer.stop(),
socksServer.close(),
proxyServer.stop(),
]);
}, { scope: 'worker' } ],
server: async ({ __servers }, run) => {
__servers.server.reset();
await run(__servers.server);
},
httpsServer: async ({ __servers }, run) => {
__servers.httpsServer.reset();
await run(__servers.httpsServer);
},
socksPort: async ({ __servers }, run) => {
await run(__servers.socksPort);
},
proxyServer: async ({ __servers }, run) => {
__servers.proxyServer.reset();
await run(__servers.proxyServer);
},
asset: async ({ __servers }, run) => {
await run(__servers.asset);
},
};

61
tests/config/testMode.ts Normal file
View File

@ -0,0 +1,61 @@
/**
* 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 { GridClient } from '../../packages/playwright-core/lib/grid/gridClient';
import { start } from '../../packages/playwright-core/lib/outofprocess';
import { Playwright } from '../../packages/playwright-core/lib/client/playwright';
export type TestModeName = 'default' | 'driver' | 'service';
interface TestMode {
setup(): Promise<Playwright>;
teardown(): Promise<void>;
}
export class DriverTestMode implements TestMode {
private _impl: { playwright: Playwright; stop: () => Promise<void>; };
async setup() {
this._impl = await start();
return this._impl.playwright;
}
async teardown() {
await this._impl.stop();
}
}
export class ServiceTestMode implements TestMode {
private _gridClient: GridClient;
async setup() {
this._gridClient = await GridClient.connect('http://localhost:3333');
return this._gridClient.playwright();
}
async teardown() {
await this._gridClient.close();
}
}
export class DefaultTestMode implements TestMode {
async setup() {
return require('playwright-core');
}
async teardown() {
}
}

View File

@ -0,0 +1,42 @@
/**
* 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 { Fixtures } from '@playwright/test';
import { DefaultTestMode, DriverTestMode, ServiceTestMode, TestModeName } from './testMode';
export type TestModeWorkerFixtures = {
mode: TestModeName;
playwright: typeof import('playwright-core');
toImpl: (rpcObject: any) => any;
};
export const testModeFixtures: Fixtures<{}, TestModeWorkerFixtures> = {
mode: [ 'default', { scope: 'worker' } ],
playwright: [ async ({ mode }, run) => {
const testMode = {
default: new DefaultTestMode(),
service: new ServiceTestMode(),
driver: new DriverTestMode(),
}[mode];
require('playwright-core/lib/utils/utils').setUnderTest();
const playwright = await testMode.setup();
await run(playwright);
await testMode.teardown();
}, { scope: 'worker' } ],
toImpl: [ async ({ playwright }, run) => run((playwright as any)._toImpl), { scope: 'worker' } ],
};

View File

@ -17,9 +17,8 @@
import type { BrowserWindow } from 'electron';
import path from 'path';
import { electronTest as test, expect } from './electronTest';
import { baseTest } from '../config/baseTest';
baseTest('should fire close event', async ({ playwright }) => {
test('should fire close event', async ({ playwright }) => {
const electronApp = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-app.js')],
});

View File

@ -14,11 +14,12 @@
* limitations under the License.
*/
import { baseTest, CommonWorkerFixtures } from '../config/baseTest';
import { ElectronApplication, Page } from 'playwright-core';
import type { Fixtures } from '@playwright/test';
import type { Fixtures, PlaywrightWorkerOptions } from '@playwright/test';
import * as path from 'path';
import { PageTestFixtures } from '../page/pageTest';
import { TestModeWorkerFixtures } from '../config/testModeFixtures';
import { browserTest } from '../config/browserTest';
export { expect } from '@playwright/test';
type ElectronTestFixtures = PageTestFixtures & {
@ -27,7 +28,7 @@ type ElectronTestFixtures = PageTestFixtures & {
};
const electronVersion = require('electron/package.json').version;
export const electronFixtures: Fixtures<ElectronTestFixtures, {}, {}, CommonWorkerFixtures> = {
export const electronFixtures: Fixtures<ElectronTestFixtures, {}, {}, PlaywrightWorkerOptions & TestModeWorkerFixtures> = {
browserVersion: electronVersion,
browserMajorVersion: Number(electronVersion.split('.')[0]),
isAndroid: false,
@ -66,10 +67,9 @@ export const electronFixtures: Fixtures<ElectronTestFixtures, {}, {}, CommonWork
await window.close();
},
page: async ({ newWindow }, run) => {
await run(await newWindow());
},
};
export const electronTest = baseTest.extend<ElectronTestFixtures>(electronFixtures);
export const electronTest = browserTest.extend<ElectronTestFixtures>(electronFixtures as any);

View File

@ -234,7 +234,7 @@ it('should give access to the intercepted response body', async ({ page, server
const routePromise = new Promise<Route>(f => routeCallback = f);
await page.route('**/simple.json', routeCallback);
const evalPromise = page.evaluate(url => fetch(url), server.PREFIX + '/simple.json').catch(console.log);
const evalPromise = page.evaluate(url => fetch(url), server.PREFIX + '/simple.json').catch(() => {});
const route = await routePromise;
// @ts-expect-error

View File

@ -15,7 +15,8 @@
*/
import { baseTest } from '../config/baseTest';
import type { Page } from 'playwright-core';
import type { Page, ViewportSize } from 'playwright-core';
import { VideoMode } from '@playwright/test';
export { expect } from '@playwright/test';
// Page test does not guarantee an isolated context, just a new page (because Android).
@ -27,4 +28,12 @@ export type PageTestFixtures = {
isElectron: boolean;
};
export const test = baseTest.declare<PageTestFixtures>();
export type PageWorkerFixtures = {
headless: boolean,
channel: string,
trace: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-trace';
video: VideoMode | { mode: VideoMode, size: ViewportSize };
browserName: 'chromium' | 'firefox' | 'webkit',
};
export const test = baseTest.declare<PageTestFixtures, PageWorkerFixtures>();

View File

@ -20,7 +20,8 @@ import * as os from 'os';
import * as path from 'path';
import rimraf from 'rimraf';
import { promisify } from 'util';
import { CommonFixtures, commonFixtures, serverFixtures, ServerFixtures } from '../config/commonFixtures';
import { CommonFixtures, commonFixtures } from '../config/commonFixtures';
import { serverFixtures, ServerFixtures, ServerWorkerOptions } from '../config/serverFixtures';
import { test as base, TestInfo } from './stable-test-runner';
const removeFolderAsync = promisify(rimraf);
@ -193,7 +194,7 @@ type Fixtures = {
};
const common = base.extend<CommonFixtures>(commonFixtures as any);
export const test = common.extend<ServerFixtures>(serverFixtures as any).extend<Fixtures>({
export const test = common.extend<ServerFixtures, ServerWorkerOptions>(serverFixtures as any).extend<Fixtures>({
writeFiles: async ({}, use, testInfo) => {
await use(files => writeFiles(testInfo, files));
},