mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: sort out base fixtures (#9809)
This commit is contained in:
parent
1c88079755
commit
31faa7d9a7
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
},
|
||||
};
|
||||
|
||||
@ -15,9 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* @param {Map<string, boolean>} apiCoverage
|
||||
* @param {Object} events
|
||||
|
||||
43
tests/config/coverageFixtures.ts
Normal file
43
tests/config/coverageFixtures.ts
Normal 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 } ],
|
||||
};
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
|
||||
31
tests/config/platformFixtures.ts
Normal file
31
tests/config/platformFixtures.ts
Normal 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' } ],
|
||||
};
|
||||
112
tests/config/serverFixtures.ts
Normal file
112
tests/config/serverFixtures.ts
Normal 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
61
tests/config/testMode.ts
Normal 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() {
|
||||
}
|
||||
}
|
||||
42
tests/config/testModeFixtures.ts
Normal file
42
tests/config/testModeFixtures.ts
Normal 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' } ],
|
||||
};
|
||||
@ -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')],
|
||||
});
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>();
|
||||
|
||||
@ -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));
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user