chore: remove PlaywrightClient and Docker factory (#13571)
This commit is contained in:
parent
02aa4fe617
commit
7ffce1da53
|
|
@ -19,8 +19,6 @@
|
|||
"./cli": "./cli.js",
|
||||
"./package.json": "./package.json",
|
||||
"./lib/grid/gridServer": "./lib/grid/gridServer.js",
|
||||
"./lib/grid/gridClient": "./lib/grid/gridClient.js",
|
||||
"./lib/grid/dockerGridFactory": "./lib/grid/dockerGridFactory.js",
|
||||
"./lib/outofprocess": "./lib/outofprocess.js",
|
||||
"./lib/utils": "./lib/utils/index.js",
|
||||
"./lib/utils/comparators": "./lib/utils/comparators.js",
|
||||
|
|
@ -35,7 +33,6 @@
|
|||
"./lib/utils/stackTrace": "./lib/utils/stackTrace.js",
|
||||
"./lib/utils/timeoutRunner": "./lib/utils/timeoutRunner.js",
|
||||
"./lib/remote/playwrightServer": "./lib/remote/playwrightServer.js",
|
||||
"./lib/remote/playwrightClient": "./lib/remote/playwrightClient.js",
|
||||
"./lib/server": "./lib/server/index.js",
|
||||
"./types/protocol": "./types/protocol.d.ts",
|
||||
"./types/structs": "./types/structs.d.ts"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import type * as channels from '../protocol/channels';
|
||||
import { TimeoutError } from '../common/errors';
|
||||
import * as socks from '../common/socksProxy';
|
||||
import type * as socks from '../common/socksProxy';
|
||||
import { Android } from './android';
|
||||
import { BrowserType } from './browserType';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
|
|
@ -87,23 +87,6 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
|
|||
this.selectors._addChannel(selectorsOwner);
|
||||
}
|
||||
|
||||
// TODO: remove this methods together with PlaywrightClient.
|
||||
_enablePortForwarding(redirectPortForTest?: number) {
|
||||
const socksSupport = this._initializer.socksSupport;
|
||||
if (!socksSupport)
|
||||
return;
|
||||
const handler = new socks.SocksProxyHandler(redirectPortForTest);
|
||||
this._socksProxyHandler = handler;
|
||||
handler.on(socks.SocksProxyHandler.Events.SocksConnected, (payload: socks.SocksSocketConnectedPayload) => socksSupport.socksConnected(payload).catch(() => {}));
|
||||
handler.on(socks.SocksProxyHandler.Events.SocksData, (payload: socks.SocksSocketDataPayload) => socksSupport.socksData({ uid: payload.uid, data: payload.data.toString('base64') }).catch(() => {}));
|
||||
handler.on(socks.SocksProxyHandler.Events.SocksError, (payload: socks.SocksSocketErrorPayload) => socksSupport.socksError(payload).catch(() => {}));
|
||||
handler.on(socks.SocksProxyHandler.Events.SocksFailed, (payload: socks.SocksSocketFailedPayload) => socksSupport.socksFailed(payload).catch(() => {}));
|
||||
handler.on(socks.SocksProxyHandler.Events.SocksEnd, (payload: socks.SocksSocketEndPayload) => socksSupport.socksEnd(payload).catch(() => {}));
|
||||
socksSupport.on('socksRequested', payload => handler.socketRequested(payload));
|
||||
socksSupport.on('socksClosed', payload => handler.socketClosed(payload));
|
||||
socksSupport.on('socksData', payload => handler.sendSocketData({ uid: payload.uid, data: Buffer.from(payload.data, 'base64') }));
|
||||
}
|
||||
|
||||
static from(channel: channels.PlaywrightChannel): Playwright {
|
||||
return (channel as any)._object;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,165 +0,0 @@
|
|||
/**
|
||||
* 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 http from 'http';
|
||||
import os from 'os';
|
||||
import type { GridAgentLaunchOptions, GridFactory } from './gridServer';
|
||||
import * as utils from '../utils';
|
||||
|
||||
const dockerFactory: GridFactory = {
|
||||
name: 'Agents launched inside Docker container',
|
||||
capacity: Infinity,
|
||||
launchTimeout: 30000,
|
||||
retireTimeout: Infinity,
|
||||
launch: async (options: GridAgentLaunchOptions) => {
|
||||
const { vncUrl } = await launchDockerGridAgent(options.agentId, options.gridURL);
|
||||
/* eslint-disable no-console */
|
||||
console.log(``);
|
||||
console.log(`✨ Running browsers inside docker container: ${vncUrl} ✨`);
|
||||
}
|
||||
};
|
||||
|
||||
export default dockerFactory;
|
||||
|
||||
interface DockerImage {
|
||||
Containers: number;
|
||||
Created: number;
|
||||
Id: string;
|
||||
Labels: null | Record<string, string>;
|
||||
ParentId: string;
|
||||
RepoDigests: null | string[];
|
||||
RepoTags: null | string[];
|
||||
SharedSize: number;
|
||||
Size: number;
|
||||
VirtualSize: number;
|
||||
}
|
||||
|
||||
async function launchDockerGridAgent(agentId: string, gridURL: string): Promise<{vncUrl: string }> {
|
||||
const gridPort = new URL(gridURL).port || '80';
|
||||
const images: DockerImage[] | null = await getJSON('/images/json');
|
||||
|
||||
if (!images) {
|
||||
throw new Error(`\n` + utils.wrapInASCIIBox([
|
||||
`Failed to list docker images`,
|
||||
`Please ensure docker is running.`,
|
||||
``,
|
||||
`<3 Playwright Team`,
|
||||
].join('\n'), 1));
|
||||
}
|
||||
|
||||
const imageName = process.env.PWTEST_IMAGE_NAME ?? `mcr.microsoft.com/playwright:v${require('../../package.json').version}-focal`;
|
||||
const pwImage = images.find(image => image.RepoTags?.includes(imageName));
|
||||
|
||||
if (!pwImage) {
|
||||
throw new Error(`\n` + utils.wrapInASCIIBox([
|
||||
`Failed to find ${imageName} docker image.`,
|
||||
`Please pull docker image with the following command:`,
|
||||
``,
|
||||
` npx playwright install docker-image`,
|
||||
``,
|
||||
`<3 Playwright Team`,
|
||||
].join('\n'), 1));
|
||||
}
|
||||
const Env = [
|
||||
'PW_SOCKS_PROXY_PORT=1', // Enable port forwarding over PlaywrightClient
|
||||
];
|
||||
const forwardIfDefined = (envName: string) => {
|
||||
if (process.env[envName])
|
||||
Env.push(`CI=${process.env[envName]}`);
|
||||
};
|
||||
forwardIfDefined('CI');
|
||||
forwardIfDefined('PWDEBUG');
|
||||
forwardIfDefined('DEBUG');
|
||||
forwardIfDefined('DEBUG_FILE');
|
||||
forwardIfDefined('SELENIUM_REMOTE_URL');
|
||||
|
||||
const container = await postJSON('/containers/create', {
|
||||
Env,
|
||||
WorkingDir: '/ms-playwright-agent',
|
||||
Cmd: [ 'bash', 'start_agent.sh', agentId, `http://host.docker.internal:${gridPort}` ],
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Image: pwImage.Id,
|
||||
ExposedPorts: {
|
||||
'7900/tcp': { }
|
||||
},
|
||||
HostConfig: {
|
||||
Init: true,
|
||||
AutoRemove: true,
|
||||
ShmSize: 2 * 1024 * 1024 * 1024,
|
||||
ExtraHosts: process.platform === 'linux' ? [
|
||||
'host.docker.internal:host-gateway', // Enable host.docker.internal on Linux.
|
||||
] : [],
|
||||
PortBindings: {
|
||||
'7900/tcp': [{ HostPort: '0' }]
|
||||
},
|
||||
},
|
||||
});
|
||||
await postJSON(`/containers/${container.Id}/start`);
|
||||
const info = await getJSON(`/containers/${container.Id}/json`);
|
||||
const vncPort = info?.NetworkSettings?.Ports['7900/tcp'];
|
||||
return {
|
||||
vncUrl: `http://localhost:${vncPort[0].HostPort}`,
|
||||
};
|
||||
}
|
||||
|
||||
async function getJSON(url: string): Promise<any> {
|
||||
const result = await callDockerAPI('get', url);
|
||||
if (!result)
|
||||
return result;
|
||||
return JSON.parse(result);
|
||||
}
|
||||
|
||||
async function postJSON(url: string, json: any = undefined) {
|
||||
const result = await callDockerAPI('post', url, json ? JSON.stringify(json) : undefined);
|
||||
if (!result)
|
||||
return result;
|
||||
return JSON.parse(result);
|
||||
}
|
||||
|
||||
function callDockerAPI(method: 'post'|'get', url: string, body: Buffer|string|undefined = undefined): Promise<string|null> {
|
||||
const dockerSocket = os.platform() === 'win32' ? '\\\\.\\pipe\\docker_engine' : '/var/run/docker.sock';
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = http.request({
|
||||
socketPath: dockerSocket,
|
||||
path: url,
|
||||
method,
|
||||
}, (response: http.IncomingMessage) => {
|
||||
let body = '';
|
||||
response.on('data', function(chunk){
|
||||
body += chunk;
|
||||
});
|
||||
response.on('end', function(){
|
||||
if (!response.statusCode || response.statusCode < 200 || response.statusCode >= 300) {
|
||||
console.error(`ERROR ${method} ${url}`, response.statusCode, body);
|
||||
resolve(null);
|
||||
} else {
|
||||
resolve(body);
|
||||
}
|
||||
});
|
||||
});
|
||||
request.on('error', function(e){
|
||||
console.error('Error fetching json: ' + e);
|
||||
resolve(null);
|
||||
});
|
||||
if (body) {
|
||||
request.setHeader('Content-Type', 'application/json');
|
||||
request.setHeader('Content-Length', body.length);
|
||||
request.write(body);
|
||||
}
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
|
|
@ -31,10 +31,15 @@ export function launchGridAgent(agentId: string, gridURL: string, runId: string
|
|||
ws.on('message', (message: string) => {
|
||||
log('worker requested ' + message);
|
||||
const { workerId, browserAlias } = JSON.parse(message);
|
||||
if (browserAlias)
|
||||
fork(require.resolve('./gridBrowserWorker.js'), [gridURL, agentId, workerId, browserAlias], { detached: true });
|
||||
else
|
||||
fork(require.resolve('./gridWorker.js'), [gridURL, agentId, workerId], { detached: true });
|
||||
if (!workerId) {
|
||||
log('workerId not specified');
|
||||
return;
|
||||
}
|
||||
if (!browserAlias) {
|
||||
log('browserAlias not specified');
|
||||
return;
|
||||
}
|
||||
fork(require.resolve('./gridBrowserWorker.js'), [gridURL, agentId, workerId, browserAlias], { detached: true });
|
||||
});
|
||||
ws.on('close', () => process.exit(0));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* 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 WebSocket from 'ws';
|
||||
import debug from 'debug';
|
||||
import { createPlaywright, PlaywrightDispatcher, DispatcherConnection, Root } from '../server';
|
||||
import { gracefullyCloseAll } from '../utils/processLauncher';
|
||||
import { SocksProxy } from '../common/socksProxy';
|
||||
|
||||
function launchGridWorker(gridURL: string, agentId: string, workerId: string) {
|
||||
const log = debug(`pw:grid:worker:${workerId}`);
|
||||
log('created');
|
||||
const ws = new WebSocket(gridURL.replace('http://', 'ws://') + `/registerWorker?agentId=${agentId}&workerId=${workerId}`);
|
||||
const dispatcherConnection = new DispatcherConnection();
|
||||
dispatcherConnection.onmessage = message => ws.send(JSON.stringify(message));
|
||||
ws.once('open', () => {
|
||||
new Root(dispatcherConnection, async rootScope => {
|
||||
const playwright = createPlaywright('javascript');
|
||||
const socksProxy = new SocksProxy();
|
||||
playwright.options.socksProxyPort = await socksProxy.listen(0);
|
||||
return new PlaywrightDispatcher(rootScope, playwright, socksProxy);
|
||||
});
|
||||
});
|
||||
ws.on('message', message => dispatcherConnection.dispatch(JSON.parse(message.toString())));
|
||||
ws.on('close', async () => {
|
||||
// Drop any messages during shutdown on the floor.
|
||||
dispatcherConnection.onmessage = () => {};
|
||||
setTimeout(() => process.exit(0), 30000);
|
||||
// Meanwhile, try to gracefully close all browsers.
|
||||
await gracefullyCloseAll();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
launchGridWorker(process.argv[2], process.argv[3], process.argv[4]);
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/**
|
||||
* 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 WebSocket from 'ws';
|
||||
import { Connection } from '../client/connection';
|
||||
import type { Playwright } from '../client/playwright';
|
||||
import { makeWaitForNextTask } from '../utils';
|
||||
|
||||
// TODO: this file should be removed because it uses the old protocol.
|
||||
|
||||
export type PlaywrightClientConnectOptions = {
|
||||
wsEndpoint: string;
|
||||
timeout?: number;
|
||||
followRedirects?: boolean;
|
||||
};
|
||||
|
||||
export class PlaywrightClient {
|
||||
private _playwright: Playwright;
|
||||
private _ws: WebSocket;
|
||||
private _closePromise: Promise<void>;
|
||||
|
||||
static async connect(options: PlaywrightClientConnectOptions): Promise<PlaywrightClient> {
|
||||
const { wsEndpoint, timeout = 30000, followRedirects = true } = options;
|
||||
const connection = new Connection();
|
||||
connection.markAsRemote();
|
||||
const ws = new WebSocket(wsEndpoint, { followRedirects });
|
||||
const waitForNextTask = makeWaitForNextTask();
|
||||
connection.onmessage = message => {
|
||||
if (ws.readyState === 2 /** CLOSING */ || ws.readyState === 3 /** CLOSED */)
|
||||
throw new Error('PlaywrightClient: writing to closed WebSocket connection');
|
||||
ws.send(JSON.stringify(message));
|
||||
};
|
||||
ws.on('message', message => waitForNextTask(() => connection.dispatch(JSON.parse(message.toString()))));
|
||||
const errorPromise = new Promise((_, reject) => ws.on('error', error => reject(error)));
|
||||
const closePromise = new Promise((_, reject) => ws.on('close', () => reject(new Error('Connection closed'))));
|
||||
const playwrightClientPromise = new Promise<PlaywrightClient>((resolve, reject) => {
|
||||
let playwright: Playwright;
|
||||
ws.on('open', async () => {
|
||||
playwright = await connection.initializePlaywright();
|
||||
resolve(new PlaywrightClient(playwright, ws));
|
||||
});
|
||||
ws.on('close', (code, reason) => connection.close(reason.toString()));
|
||||
});
|
||||
let timer: NodeJS.Timeout;
|
||||
try {
|
||||
await Promise.race([
|
||||
playwrightClientPromise,
|
||||
errorPromise,
|
||||
closePromise,
|
||||
new Promise((_, reject) => timer = setTimeout(() => reject(`Timeout of ${timeout}ms exceeded while connecting.`), timeout))
|
||||
]);
|
||||
return await playwrightClientPromise;
|
||||
} finally {
|
||||
clearTimeout(timer!);
|
||||
}
|
||||
}
|
||||
|
||||
constructor(playwright: Playwright, ws: WebSocket) {
|
||||
this._playwright = playwright;
|
||||
this._ws = ws;
|
||||
this._closePromise = new Promise(f => ws.on('close', f));
|
||||
}
|
||||
|
||||
playwright(): Playwright {
|
||||
return this._playwright;
|
||||
}
|
||||
|
||||
async close() {
|
||||
this._ws.close();
|
||||
await this._closePromise;
|
||||
}
|
||||
}
|
||||
|
|
@ -27,9 +27,6 @@ import { Runner, builtInReporters, kDefaultConfigFiles } from './runner';
|
|||
import { stopProfiling, startProfiling } from './profiler';
|
||||
import type { FilePatternFilter } from './util';
|
||||
import { showHTMLReport } from './reporters/html';
|
||||
import { GridServer } from 'playwright-core/lib/grid/gridServer';
|
||||
import dockerFactory from 'playwright-core/lib/grid/dockerGridFactory';
|
||||
import { createGuid } from 'playwright-core/lib/utils';
|
||||
import { hostPlatform } from 'playwright-core/lib/utils/hostPlatform';
|
||||
import { fileIsModule } from './loader';
|
||||
|
||||
|
|
@ -166,8 +163,6 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
|||
};
|
||||
});
|
||||
|
||||
if (process.env.PLAYWRIGHT_DOCKER)
|
||||
runner.addInternalGlobalSetup(launchDockerContainer);
|
||||
const result = await runner.runAllTests({
|
||||
listOnly: !!opts.list,
|
||||
filePatternFilter,
|
||||
|
|
@ -235,16 +230,6 @@ function resolveReporter(id: string) {
|
|||
return require.resolve(id, { paths: [ process.cwd() ] });
|
||||
}
|
||||
|
||||
async function launchDockerContainer(): Promise<() => Promise<void>> {
|
||||
const gridServer = new GridServer(dockerFactory, createGuid());
|
||||
await gridServer.start();
|
||||
// Start docker container in advance.
|
||||
const { error } = await gridServer.createAgent();
|
||||
if (error)
|
||||
throw error;
|
||||
return async () => await gridServer.stop();
|
||||
}
|
||||
|
||||
function restartWithExperimentalTsEsm(configFile: string | null): boolean {
|
||||
const nodeVersion = +process.versions.node.split('.')[0];
|
||||
// New experimental loader is only supported on Node 16+.
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
|||
});
|
||||
},
|
||||
|
||||
_snapshotSuffix: [process.env.PLAYWRIGHT_DOCKER ? 'docker' : process.platform, { scope: 'worker' }],
|
||||
_snapshotSuffix: [process.platform, { scope: 'worker' }],
|
||||
|
||||
_setupContextOptionsAndArtifacts: [async ({ playwright, _snapshotSuffix, _combinedContextOptions, _browserOptions, _artifactsDir, trace, screenshot, actionTimeout, navigationTimeout }, use, testInfo) => {
|
||||
testInfo.snapshotSuffix = _snapshotSuffix;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ if (mode === 'service') {
|
|||
config.projects = [{
|
||||
name: 'Chromium page tests',
|
||||
testMatch: /page\/.*spec.ts$/,
|
||||
testIgnore: 'screenshot',
|
||||
testIgnore: '**/*screenshot*',
|
||||
use: {
|
||||
browserName: 'chromium',
|
||||
mode
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ class OutOfProcessPlaywrightServer {
|
|||
stdio: 'pipe',
|
||||
detached: true,
|
||||
env: {
|
||||
...process.env,
|
||||
PW_SOCKS_PROXY_PORT: '1'
|
||||
...process.env
|
||||
}
|
||||
});
|
||||
this._driverProcess.unref();
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@ it('should type', async ({ page }) => {
|
|||
expect(await page.$eval('input', input => input.value)).toBe('hello');
|
||||
});
|
||||
|
||||
it('should take screenshot', async ({ page, server, browserName, headless, isAndroid }) => {
|
||||
it('should take screenshot', async ({ page, server, browserName, headless, isAndroid, mode }) => {
|
||||
it.skip(mode === 'service');
|
||||
it.skip(browserName === 'firefox' && !headless);
|
||||
it.skip(isAndroid, 'Different dpr. Remove after using 1x scale for screenshots.');
|
||||
await page.setViewportSize({ width: 500, height: 500 });
|
||||
|
|
|
|||
|
|
@ -76,7 +76,8 @@ it('should work with status code 422', async ({ page, server }) => {
|
|||
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
|
||||
});
|
||||
|
||||
it('should allow mocking binary responses', async ({ page, server, browserName, headless, asset, isAndroid }) => {
|
||||
it('should allow mocking binary responses', async ({ page, server, browserName, headless, asset, isAndroid, mode }) => {
|
||||
it.skip(mode === 'service');
|
||||
it.skip(browserName === 'firefox' && !headless, 'Firefox headed produces a different image.');
|
||||
it.skip(isAndroid);
|
||||
|
||||
|
|
@ -97,7 +98,8 @@ it('should allow mocking binary responses', async ({ page, server, browserName,
|
|||
expect(await img.screenshot()).toMatchSnapshot('mock-binary-response.png');
|
||||
});
|
||||
|
||||
it('should allow mocking svg with charset', async ({ page, server, browserName, headless, isAndroid }) => {
|
||||
it('should allow mocking svg with charset', async ({ page, server, browserName, headless, isAndroid, mode }) => {
|
||||
it.skip(mode === 'service');
|
||||
it.skip(browserName === 'firefox' && !headless, 'Firefox headed produces a different image.');
|
||||
it.skip(isAndroid);
|
||||
|
||||
|
|
@ -117,7 +119,8 @@ it('should allow mocking svg with charset', async ({ page, server, browserName,
|
|||
expect(await img.screenshot()).toMatchSnapshot('mock-svg.png');
|
||||
});
|
||||
|
||||
it('should work with file path', async ({ page, server, asset, isAndroid }) => {
|
||||
it('should work with file path', async ({ page, server, asset, isAndroid, mode }) => {
|
||||
it.skip(mode === 'service');
|
||||
it.skip(isAndroid);
|
||||
|
||||
await page.route('**/*', route => route.fulfill({ contentType: 'shouldBeIgnored', path: asset('pptr.png') }));
|
||||
|
|
|
|||
|
|
@ -141,7 +141,6 @@ async function runPlaywrightTest(childProcess: CommonFixtures['childProcess'], b
|
|||
GITHUB_SHA: undefined,
|
||||
// END: Reserved CI
|
||||
PW_TEST_HTML_REPORT_OPEN: undefined,
|
||||
PLAYWRIGHT_DOCKER: undefined,
|
||||
PW_TEST_REPORTER: undefined,
|
||||
PW_TEST_REPORTER_WS_ENDPOINT: undefined,
|
||||
PW_TEST_SOURCE_TRANSFORM: undefined,
|
||||
|
|
|
|||
Loading…
Reference in a new issue