chore(test-runner): move into its own folder and typescript project (#3548)

This commit is contained in:
Joel Einbinder 2020-08-20 16:04:27 -07:00 committed by GitHub
parent 4c5635434a
commit 012f9425bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 261 additions and 170 deletions

View File

@ -15,3 +15,4 @@ src/webkit/protocol.ts
utils/generate_types/overrides.d.ts
utils/generate_types/test/test.ts
test/
test-runner/

View File

@ -27,7 +27,7 @@ jobs:
# XVFB-RUN merges both STDOUT and STDERR, whereas we need only STDERR
# Wrap `npm run` in a subshell to redirect STDERR to file.
# Enable core dumps in the subshell.
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && node test/runner/cli test/ --jobs=1 --forbid-only --timeout=30000"
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && node test-runner/cli test/ --jobs=1 --forbid-only --timeout=30000"
env:
BROWSER: ${{ matrix.browser }}
DEBUG: "pw:*,-pw:wrapped*,-pw:test*"

View File

@ -37,7 +37,7 @@ jobs:
# XVFB-RUN merges both STDOUT and STDERR, whereas we need only STDERR
# Wrap `npm run` in a subshell to redirect STDERR to file.
# Enable core dumps in the subshell.
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && node test/runner/cli test/ --jobs=1 --forbid-only --timeout=30000 && npm run coverage"
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && node test-runner/cli test/ --jobs=1 --forbid-only --timeout=30000 && npm run coverage"
env:
BROWSER: ${{ matrix.browser }}
DEBUG: "pw:*,-pw:wrapped*,-pw:test*"
@ -68,7 +68,7 @@ jobs:
- uses: microsoft/playwright-github-action@v1
- run: npm ci
- run: npm run build
- run: node test/runner/cli test/ --jobs=1 --forbid-only --timeout=30000
- run: node test-runner/cli test/ --jobs=1 --forbid-only --timeout=30000
env:
BROWSER: ${{ matrix.browser }}
DEBUG: "pw:*,-pw:wrapped*,-pw:test*"
@ -102,7 +102,7 @@ jobs:
- uses: microsoft/playwright-github-action@v1
- run: npm ci
- run: npm run build
- run: node test/runner/cli test/ --jobs=1 --forbid-only --timeout=30000
- run: node test-runner/cli test/ --jobs=1 --forbid-only --timeout=30000
shell: bash
env:
BROWSER: ${{ matrix.browser }}
@ -159,7 +159,7 @@ jobs:
# XVFB-RUN merges both STDOUT and STDERR, whereas we need only STDERR
# Wrap `npm run` in a subshell to redirect STDERR to file.
# Enable core dumps in the subshell.
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && node test/runner/cli test/ --jobs=1 --forbid-only --timeout=30000"
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && node test-runner/cli test/ --jobs=1 --forbid-only --timeout=30000"
if: ${{ always() }}
env:
BROWSER: ${{ matrix.browser }}
@ -197,7 +197,7 @@ jobs:
# XVFB-RUN merges both STDOUT and STDERR, whereas we need only STDERR
# Wrap `npm run` in a subshell to redirect STDERR to file.
# Enable core dumps in the subshell.
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && node test/runner/cli test/ --jobs=1 --forbid-only --timeout=30000"
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && node test-runner/cli test/ --jobs=1 --forbid-only --timeout=30000"
env:
BROWSER: ${{ matrix.browser }}
DEBUG: "pw:*,-pw:wrapped*,-pw:test*"

View File

@ -9,19 +9,19 @@
"node": ">=10.15.0"
},
"scripts": {
"ctest": "cross-env BROWSER=chromium node test/runner/cli test/",
"ftest": "cross-env BROWSER=firefox node test/runner/cli test/",
"wtest": "cross-env BROWSER=webkit node test/runner/cli test/",
"test": "node test/runner/cli test/",
"ctest": "cross-env BROWSER=chromium node test-runner/cli test/",
"ftest": "cross-env BROWSER=firefox node test-runner/cli test/",
"wtest": "cross-env BROWSER=webkit node test-runner/cli test/",
"test": "node test-runner/cli test/",
"eslint": "[ \"$CI\" = true ] && eslint --quiet -f codeframe --ext js,ts ./src || eslint --ext js,ts ./src",
"tsc": "tsc -p .",
"tsc-installer": "tsc -p ./src/install/tsconfig.json",
"doc": "node utils/doclint/cli.js",
"test-infra": "node test/runner/cli utils/doclint/check_public_api/test/test.js && node test/runner/cli utils/doclint/preprocessor/test.js",
"test-infra": "node test-runner/cli utils/doclint/check_public_api/test/test.js && node test-runner/cli utils/doclint/preprocessor/test.js",
"lint": "npm run eslint && npm run tsc && npm run doc && npm run check-deps && npm run generate-channels && npm run test-types && npm run test-infra",
"clean": "rimraf lib && rimraf types",
"prepare": "node install-from-github.js",
"build": "node utils/runWebpack.js --mode='development' && tsc -p . && npm run generate-types",
"build": "node utils/runWebpack.js --mode='development' && tsc -p . && npm run build-testrunner && npm run generate-types",
"watch": "node utils/watch.js",
"test-types": "npm run generate-types && npx -p typescript@3.7.5 tsc -p utils/generate_types/test/tsconfig.json && npm run typecheck-tests",
"generate-types": "node utils/generate_types/",
@ -30,7 +30,8 @@
"roll-browser": "node utils/roll_browser.js",
"coverage": "node test/checkCoverage.js",
"check-deps": "node utils/check_deps.js",
"build-driver": "pkg --public --targets node12-linux-x64,node12-macos-x64,node12-win-x64 --out-path=drivers packages/playwright-driver/main.js"
"build-driver": "pkg --public --targets node12-linux-x64,node12-macos-x64,node12-win-x64 --out-path=drivers packages/playwright-driver/main.js",
"build-testrunner": "tsc -p test-runner"
},
"author": {
"name": "Microsoft Corporation"

2
test-runner/cli.js Normal file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env node
module.exports = require('./lib/cli')

8
test-runner/package.json Normal file
View File

@ -0,0 +1,8 @@
{
"name": "test-runner",
"version": "0.0.7",
"bin": {
"test-runner": "./cli.js"
},
"main": "./lib/index.js"
}

View File

@ -42,7 +42,7 @@ const GoldenComparators = {
* @param {?Object} actualBuffer
* @param {!Buffer} expectedBuffer
* @param {!string} mimeType
* @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}}
* @return {?{diff?: Object, errorMessage?: string}}
*/
function compareImages(actualBuffer, expectedBuffer, mimeType, config = {}) {
if (!actualBuffer || !(actualBuffer instanceof Buffer))
@ -63,7 +63,7 @@ function compareImages(actualBuffer, expectedBuffer, mimeType, config = {}) {
/**
* @param {?Object} actual
* @param {!Buffer} expectedBuffer
* @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}}
* @return {?{diff?: Object, errorMessage?: string, diffExtension?: string}}
*/
function compareText(actual, expectedBuffer) {
if (typeof actual !== 'string')
@ -85,8 +85,8 @@ function compareText(actual, expectedBuffer) {
/**
* @param {?Object} actual
* @param {string} path
* @return {!{pass: boolean, message: (undefined|string)}}
* @param {string} name
* @return {!{pass: boolean, message?: string}}
*/
function compare(actual, name, options) {
const { relativeTestFile, snapshotDir, outputDir, updateSnapshots } = options;

View File

@ -0,0 +1,72 @@
/**
* Copyright Microsoft Corporation. All rights reserved.
*
* 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 os from 'os';
import path from 'path';
import {promisify} from 'util';
import fs from 'fs';
import rimraf from 'rimraf';
import {registerFixture} from './fixtures';
declare global {
type DescribeFunction = ((name: string, inner: () => void) => void) & {
fail(condition: boolean): DescribeFunction;
skip(condition: boolean): DescribeFunction;
slow(): DescribeFunction;
repeat(n: number): DescribeFunction;
};
type ItFunction<STATE> = ((name: string, inner: (state: STATE) => Promise<void>) => void) & {
fail(condition: boolean): ItFunction<STATE>;
skip(condition: boolean): ItFunction<STATE>;
slow(): ItFunction<STATE>;
repeat(n: number): ItFunction<STATE>;
};
const describe: DescribeFunction;
const fdescribe: DescribeFunction;
const xdescribe: DescribeFunction;
const it: ItFunction<TestState & WorkerState & FixtureParameters>;
const fit: ItFunction<TestState & WorkerState & FixtureParameters>;
const dit: ItFunction<TestState & WorkerState & FixtureParameters>;
const xit: ItFunction<TestState & WorkerState & FixtureParameters>;
const beforeEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise<void>) => void;
const afterEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise<void>) => void;
const beforeAll: (inner: (state: WorkerState & FixtureParameters) => Promise<void>) => void;
const afterAll: (inner: (state: WorkerState & FixtureParameters) => Promise<void>) => void;
}
const mkdtempAsync = promisify(fs.mkdtemp);
const removeFolderAsync = promisify(rimraf);
declare global {
interface FixtureParameters {
parallelIndex: number;
}
interface TestState {
tmpDir: string;
}
}
export {parameters, registerFixture, registerWorkerFixture, registerParameter} from './fixtures';
registerFixture('tmpDir', async ({}, test) => {
const tmpDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
await test(tmpDir);
await removeFolderAsync(tmpDir).catch(e => {});
});

View File

@ -25,21 +25,21 @@ let beforeFunction;
let afterFunction;
let matrix = {};
global.before = (fn => beforeFunction = fn);
global.after = (fn => afterFunction = fn);
global.matrix = (m => matrix = m);
global['before'] = (fn => beforeFunction = fn);
global['after'] = (fn => afterFunction = fn);
global['matrix'] = (m => matrix = m);
program
.version('Version ' + require('../../package.json').version)
.version('Version ' + /** @type {any} */ (require)('../package.json').version)
.option('--forbid-only', 'Fail if exclusive test(s) encountered', false)
.option('-g, --grep <grep>', 'Only run tests matching this string or regexp', '.*')
.option('-j, --jobs <jobs>', 'Number of concurrent jobs for --parallel; use 1 to run in serial, default: (number of CPU cores / 2)', Math.ceil(require('os').cpus().length / 2))
.option('-j, --jobs <jobs>', 'Number of concurrent jobs for --parallel; use 1 to run in serial, default: (number of CPU cores / 2)', Math.ceil(require('os').cpus().length / 2).toString())
.option('--reporter <reporter>', 'Specify reporter to use', '')
.option('--trial-run', 'Only collect the matching tests and report them as passing')
.option('--quiet', 'Suppress stdio', false)
.option('--debug', 'Run tests in-process for debugging', false)
.option('--output <outputDir>', 'Folder for output artifacts, default: test-results', path.join(process.cwd(), 'test-results'))
.option('--timeout <timeout>', 'Specify test timeout threshold (in milliseconds), default: 10000', 10000)
.option('--timeout <timeout>', 'Specify test timeout threshold (in milliseconds), default: 10000', '10000')
.option('-u, --update-snapshots', 'Use this flag to re-record every snapshot that fails during this test run')
.action(async (command) => {
// Collect files]
@ -101,7 +101,7 @@ program
try {
if (beforeFunction)
await beforeFunction();
await runner.run(files);
await runner.run();
await runner.stop();
} finally {
if (afterFunction)

View File

@ -69,7 +69,7 @@ class DotReporter extends Base {
this.failures.forEach((failure, index) => {
const relativePath = path.relative(process.cwd(), failure.file);
const header = ` ${index +1}. ${terminalLink(relativePath, `file://${os.hostname()}${failure.file}`)} ${failure.title}`;
console.log(colors.bold.red(header));
console.log(colors.bold(colors.red(header)));
const stack = failure.err.stack;
if (stack) {
console.log('');

View File

@ -14,21 +14,45 @@
* limitations under the License.
*/
const debug = require('debug');
import debug from 'debug';
declare global {
interface WorkerState {
}
interface TestState {
}
interface FixtureParameters {
}
}
const registrations = new Map();
const registrationsByFile = new Map();
let parameters = {};
const parameterRegistrations = new Map();
export let parameters: FixtureParameters = {} as FixtureParameters;
export const parameterRegistrations = new Map();
function setParameters(params) {
export function setParameters(params: any) {
parameters = Object.assign(parameters, params);
for (const name of Object.keys(params))
registerWorkerFixture(name, async ({}, test) => await test(parameters[name]));
registerWorkerFixture(name as keyof WorkerState, async ({}, test) => await test(parameters[name] as never));
}
class Fixture {
constructor(pool, name, scope, fn) {
pool: FixturePool;
name: string;
scope: any;
fn: any;
deps: any;
usages: Set<unknown>;
hasGeneratorValue: boolean;
value: any;
_teardownFenceCallback: (value?: unknown) => void;
_tearDownComplete: any;
_setup: boolean;
_teardown: any;
constructor(pool: FixturePool, name: string, scope: any, fn: any) {
this.pool = pool;
this.name = name;
this.scope = scope;
@ -50,16 +74,16 @@ class Fixture {
const params = {};
for (const n of this.deps)
params[n] = this.pool.instances.get(n).value;
let setupFenceFulfill;
let setupFenceReject;
let setupFenceFulfill: { (): void; (value?: unknown): void; };
let setupFenceReject: { (arg0: any): any; (reason?: any): void; };
const setupFence = new Promise((f, r) => { setupFenceFulfill = f; setupFenceReject = r; });
const teardownFence = new Promise(f => this._teardownFenceCallback = f);
debug('pw:test:hook')(`setup "${this.name}"`);
this._tearDownComplete = this.fn(params, async value => {
this._tearDownComplete = this.fn(params, async (value: any) => {
this.value = value;
setupFenceFulfill();
await teardownFence;
}).catch(e => setupFenceReject(e));
}).catch((e: any) => setupFenceReject(e));
await setupFence;
this._setup = true;
}
@ -85,12 +109,13 @@ class Fixture {
}
}
class FixturePool {
export class FixturePool {
instances: Map<any, any>;
constructor() {
this.instances = new Map();
}
async setupFixture(name) {
async setupFixture(name: string) {
let fixture = this.instances.get(name);
if (fixture)
return fixture;
@ -104,14 +129,14 @@ class FixturePool {
return fixture;
}
async teardownScope(scope) {
async teardownScope(scope: string) {
for (const [name, fixture] of this.instances) {
if (fixture.scope === scope)
await fixture.teardown();
}
}
async resolveParametersAndRun(fn) {
async resolveParametersAndRun(fn: (arg0: {}) => any) {
const names = fixtureParameterNames(fn);
for (const name of names)
await this.setupFixture(name);
@ -121,7 +146,7 @@ class FixturePool {
return fn(params);
}
wrapTestCallback(callback) {
wrapTestCallback(callback: any) {
if (!callback)
return callback;
return async() => {
@ -134,9 +159,9 @@ class FixturePool {
}
}
function fixturesForCallback(callback) {
const names = new Set();
const visit = (callback) => {
export function fixturesForCallback(callback: any): string[] {
const names = new Set<string>();
const visit = (callback: any) => {
for (const name of fixtureParameterNames(callback)) {
if (name in names)
continue;
@ -154,26 +179,28 @@ function fixturesForCallback(callback) {
return result;
}
function fixtureParameterNames(fn) {
function fixtureParameterNames(fn: { toString: () => any; }) {
const text = fn.toString();
const match = text.match(/async(?:\s+function)?\s*\(\s*{\s*([^}]*)\s*}/);
if (!match || !match[1].trim())
return [];
let signature = match[1];
return signature.split(',').map(t => t.trim());
return signature.split(',').map((t: string) => t.trim());
}
function optionParameterNames(fn) {
function optionParameterNames(fn: { toString: () => any; }) {
const text = fn.toString();
const match = text.match(/(?:\s+function)?\s*\(\s*{\s*([^}]*)\s*}/);
if (!match || !match[1].trim())
return [];
let signature = match[1];
return signature.split(',').map(t => t.trim());
return signature.split(',').map((t: string) => t.trim());
}
function innerRegisterFixture(name, scope, fn) {
const stackFrame = new Error().stack.split('\n').slice(1).filter(line => !line.includes(__filename))[0];
function innerRegisterFixture(name: any, scope: string, fn: any, caller: Function) {
const obj = {stack: ''};
Error.captureStackTrace(obj, caller);
const stackFrame = obj.stack.split('\n')[1];
const location = stackFrame.replace(/.*at Object.<anonymous> \((.*)\)/, '$1');
const file = location.replace(/^(.+):\d+:\d+$/, '$1');
const registration = { name, scope, fn, file, location };
@ -183,32 +210,32 @@ function innerRegisterFixture(name, scope, fn) {
registrationsByFile.get(file).push(registration);
};
function registerFixture(name, fn) {
innerRegisterFixture(name, 'test', fn);
export function registerFixture<T extends keyof TestState>(name: T, fn: (params: FixtureParameters & WorkerState & TestState, test: (arg: TestState[T]) => Promise<void>) => Promise<void>) {
innerRegisterFixture(name, 'test', fn, registerFixture);
};
function registerWorkerFixture(name, fn) {
innerRegisterFixture(name, 'worker', fn);
export function registerWorkerFixture<T extends keyof (WorkerState & FixtureParameters)>(name: T, fn: (params: FixtureParameters & WorkerState, test: (arg: (WorkerState & FixtureParameters)[T]) => Promise<void>) => Promise<void>) {
innerRegisterFixture(name, 'worker', fn, registerWorkerFixture);
};
function registerParameter(name, fn) {
registerWorkerFixture(name, async ({}, test) => await test(parameters[name]));
export function registerParameter<T extends keyof WorkerState>(name: T, fn: () => WorkerState[T][]) {
registerWorkerFixture(name, async ({}: any, test: (arg0: any) => any) => await test(parameters[name]));
parameterRegistrations.set(name, fn);
}
function collectRequires(file, result) {
function collectRequires(file: string | number, result: Set<unknown>) {
if (result.has(file))
return;
result.add(file);
const cache = require.cache[file];
if (!cache)
return;
const deps = cache.children.map(m => m.id).slice().reverse();
const deps = cache.children.map((m: { id: any; }) => m.id).slice().reverse();
for (const dep of deps)
collectRequires(dep, result);
}
function lookupRegistrations(file, scope) {
export function lookupRegistrations(file: any, scope: any) {
const deps = new Set();
collectRequires(file, deps);
const allDeps = [...deps].reverse();
@ -226,11 +253,9 @@ function lookupRegistrations(file, scope) {
return result;
}
function rerunRegistrations(file, scope) {
export function rerunRegistrations(file: any, scope: any) {
// When we are running several tests in the same worker, we should re-run registrations before
// each file. That way we erase potential fixture overrides from the previous test runs.
for (const registration of lookupRegistrations(file, scope).values())
registrations.set(registration.name, registration);
}
module.exports = { FixturePool, registerFixture, registerWorkerFixture, rerunRegistrations, lookupRegistrations, fixturesForCallback, registerParameter, setParameters, parameterRegistrations, parameters };

18
test-runner/src/index.ts Normal file
View File

@ -0,0 +1,18 @@
/**
* Copyright 2019 Google Inc. All rights reserved.
* Modifications 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 './builtin.fixtures';
export {registerFixture, registerWorkerFixture, registerParameter, parameters} from './fixtures';

View File

@ -200,8 +200,8 @@ class OopWorker extends EventEmitter {
this.process = child_process.fork(path.join(__dirname, 'worker.js'), {
detached: false,
env: {
FORCE_COLOR: process.stdout.isTTY ? 1 : 0,
DEBUG_COLORS: process.stdout.isTTY ? 1 : 0,
FORCE_COLOR: process.stdout.isTTY ? '1' : '0',
DEBUG_COLORS: process.stdout.isTTY ? '1' : '0',
...process.env
},
// Can't pipe since piping slows down termination for some reason.

View File

@ -25,6 +25,7 @@ class TestCollector {
constructor(files, matrix, options) {
this._matrix = matrix;
for (const name of Object.keys(matrix))
//@ts-ignore
registerWorkerFixture(name, async ({}, test) => test());
this._options = options;
this.suite = new Mocha.Suite('', new Mocha.Context(), true);

View File

@ -14,19 +14,42 @@
* limitations under the License.
*/
const path = require('path');
const Mocha = require('mocha');
const { FixturePool, registerWorkerFixture, rerunRegistrations, setParameters } = require('./fixtures');
const { fixturesUI } = require('./fixturesUI');
const { EventEmitter } = require('events');
import path from 'path';
import Mocha from 'mocha';
import { FixturePool, registerWorkerFixture, rerunRegistrations, setParameters } from './fixtures';
import { fixturesUI } from './fixturesUI';
import { EventEmitter } from 'events';
const fixturePool = new FixturePool();
export const fixturePool = new FixturePool();
declare global {
namespace NodeJS {
interface Global {
expect: typeof import('expect')
}
}
}
global.expect = require('expect');
const GoldenUtils = require('./GoldenUtils');
class NullReporter {}
class TestRunner extends EventEmitter {
export class TestRunner extends EventEmitter {
mocha: any;
_currentOrdinal: number;
_failedWithError: boolean;
_file: any;
_ordinals: Set<unknown>;
_remaining: Set<unknown>;
_trialRun: any;
_passes: number;
_failures: number;
_pending: number;
_configuredFile: any;
_configurationObject: any;
_configurationString: any;
_parsedGeneratorConfiguration: {};
_relativeTestFile: string;
_runner: any;
constructor(entry, options, workerId) {
super();
this.mocha = new Mocha({
@ -53,6 +76,7 @@ class TestRunner extends EventEmitter {
this._parsedGeneratorConfiguration = {};
for (const {name, value} of this._configurationObject) {
this._parsedGeneratorConfiguration[name] = value;
// @ts-ignore
registerWorkerFixture(name, async ({}, test) => await test(value));
}
this._parsedGeneratorConfiguration['parallelIndex'] = workerId;
@ -130,7 +154,7 @@ class TestRunner extends EventEmitter {
await result;
}
_shouldRunTest(hook) {
_shouldRunTest(hook = false) {
if (this._trialRun || this._failedWithError)
return false;
if (hook) {
@ -206,12 +230,10 @@ function serializeError(error) {
let relativeTestFile;
function initializeImageMatcher(options) {
export function initializeImageMatcher(options) {
function toMatchImage(received, name, config) {
const { pass, message } = GoldenUtils.compare(received, name, { ...options, relativeTestFile, config });
return { pass, message: () => message };
};
global.expect.extend({ toMatchImage });
}
module.exports = { TestRunner, initializeImageMatcher, fixturePool };

View File

@ -32,10 +32,12 @@ function chunkToParams(chunk) {
process.stdout.write = chunk => {
sendMessageToParent('stdout', chunkToParams(chunk));
return true;
};
process.stderr.write = chunk => {
sendMessageToParent('stderr', chunkToParams(chunk));
return true;
};
process.on('disconnect', gracefullyCloseAndExit);

20
test-runner/tsconfig.json Normal file
View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"moduleResolution": "node",
"target": "ESNext",
"module": "commonjs",
"lib": ["esnext", "dom", "DOM.Iterable"],
"sourceMap": true,
"rootDir": "./src",
"outDir": "./lib",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"declaration": true,
"allowJs": true,
"checkJs": true,
"declarationMap": true
},
"compileOnSave": true,
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -15,7 +15,7 @@
*/
import { options } from '../playwright.fixtures';
import { registerWorkerFixture } from '../runner';
import { registerWorkerFixture } from '../../test-runner';
registerWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, test) => {
const browser = await browserType.launch({

View File

@ -15,7 +15,7 @@
*/
import { options } from '../playwright.fixtures';
import { registerFixture } from '../runner';
import { registerFixture } from '../../test-runner';
import fs from 'fs';
import path from 'path';

View File

@ -16,7 +16,7 @@
*/
import { options } from './playwright.fixtures';
import { registerFixture } from './runner';
import { registerFixture } from '../test-runner';
import fs from 'fs';
import utils from './utils';
import { BrowserType, Browser, BrowserContext, Page } from '..';

View File

@ -17,7 +17,7 @@
import './playwright.fixtures';
import { registerFixture } from './runner';
import { registerFixture } from '../test-runner';
import path from 'path';
import fs from 'fs';

View File

@ -15,7 +15,7 @@
*/
import { options } from '../playwright.fixtures';
import { registerFixture } from '../runner';
import { registerFixture } from '../../test-runner';
import {ElectronApplication, ElectronLauncher, ElectronPage} from '../../electron-types';
import path from 'path';

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { options } from './playwright.fixtures';
import { registerFixture } from './runner';
import { registerFixture } from '../test-runner';
import path from 'path';
import {spawn, execSync} from 'child_process';

View File

@ -24,7 +24,7 @@ import { Connection } from '../lib/rpc/client/connection';
import { Transport } from '../lib/rpc/transport';
import { setUnderTest } from '../lib/helper';
import { installCoverageHooks } from './coverage';
import { parameters, registerFixture, registerWorkerFixture } from './runner';
import { parameters, registerFixture, registerWorkerFixture } from '../test-runner';
import {mkdtempAsync, removeFolderAsync} from './utils';
@ -40,6 +40,7 @@ declare global {
playwright: typeof import('../index');
browserType: BrowserType<Browser>;
browser: Browser;
httpService: {server: TestServer, httpsServer: TestServer}
}
interface TestState {
toImpl: (rpcObject: any) => any;
@ -53,7 +54,7 @@ declare global {
browserName: string;
headless: boolean;
wire: boolean;
slowMo: boolean;
slowMo: number;
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { parameters } from './runner';
import { parameters } from '../test-runner';
import { options } from './playwright.fixtures';
import socks from 'socksv5';

View File

@ -1,46 +0,0 @@
/**
* Copyright Microsoft Corporation. All rights reserved.
*
* 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.
*/
const os = require('os');
const path = require('path');
const { mkdtempAsync, removeFolderAsync } = require('../utils');
const {
parameters: jsParameters,
registerFixture: jsRegisterFixture,
registerWorkerFixture: jsRegisterWorkerFixture,
registerParameter: jsRegisterParameter
} = require('./fixtures');
declare global {
interface FixtureParameters {
parallelIndex: number;
}
interface WorkerState {
tmpDir: string;
}
}
export const parameters: FixtureParameters = jsParameters;
export const registerFixture = jsRegisterFixture;
export const registerWorkerFixture = jsRegisterWorkerFixture
export const registerParameter = jsRegisterParameter;
registerFixture('tmpDir', async ({}, test) => {
const tmpDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
await test(tmpDir);
await removeFolderAsync(tmpDir).catch(e => {});
});

View File

@ -15,7 +15,7 @@
*/
import { options } from './playwright.fixtures';
import { registerFixture } from './runner';
import { registerFixture } from '../test-runner';
import { Page } from '..';
import fs from 'fs';

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { registerFixture } from './runner';
import { registerFixture } from '../test-runner';
declare global {
interface TestState {

View File

@ -15,7 +15,7 @@
*/
import './test-runner-helper';
import { registerFixture } from './runner';
import { registerFixture } from '../test-runner';
registerFixture('helperFixture', async ({}, test) => {
await test('helperFixture - overridden');

37
test/types.d.ts vendored
View File

@ -17,29 +17,6 @@
type ServerResponse = import('http').ServerResponse;
type IncomingMessage = import('http').IncomingMessage;
type DescribeFunction = ((name: string, inner: () => void) => void) & {
fail(condition: boolean): DescribeFunction;
skip(condition: boolean): DescribeFunction;
slow(): DescribeFunction;
repeat(n: number): DescribeFunction;
};
type ItFunction<STATE> = ((name: string, inner: (state: STATE) => Promise<void>) => void) & {
fail(condition: boolean): ItFunction<STATE>;
skip(condition: boolean): ItFunction<STATE>;
slow(): ItFunction<STATE>;
repeat(n: number): ItFunction<STATE>;
};
interface FixtureParameters {
}
interface WorkerState {
}
interface TestState {
}
declare module '' {
module 'expect/build/types' {
interface Matchers<R> {
@ -49,20 +26,6 @@ declare module '' {
}
declare const expect: typeof import('expect');
declare const describe: DescribeFunction;
declare const fdescribe: DescribeFunction;
declare const xdescribe: DescribeFunction;
declare const it: ItFunction<TestState & WorkerState & FixtureParameters>;
declare const fit: ItFunction<TestState & WorkerState & FixtureParameters>;
declare const dit: ItFunction<TestState & WorkerState & FixtureParameters>;
declare const xit: ItFunction<TestState & WorkerState & FixtureParameters>;
declare const beforeEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise<void>) => void;
declare const afterEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise<void>) => void;
declare const beforeAll: (inner: (state: WorkerState) => Promise<void>) => void;
declare const afterAll: (inner: (state: WorkerState) => Promise<void>) => void;
declare const browserType: import('../index').BrowserType<import('../index').Browser>;
declare var MAC: boolean;

View File

@ -20,7 +20,7 @@ const checkPublicAPI = require('..');
const Source = require('../../Source');
const mdBuilder = require('../MDBuilder');
const jsBuilder = require('../JSBuilder');
const { registerWorkerFixture } = require('../../../../test/runner');
const { registerWorkerFixture } = require('../../../../test-runner');
registerWorkerFixture('page', async({}, test) => {
const browser = await playwright.chromium.launch();

View File

@ -21,6 +21,7 @@ const fs = require('fs');
const spawns = [
child_process.spawn('node', [path.join(__dirname, 'runWebpack.js'), '--mode="development"', '--watch', '--silent'], { stdio: 'inherit', shell: true }),
child_process.spawn('npx', ['tsc', '-w', '--preserveWatchOutput', '-p', path.join(__dirname, '..')], { stdio: 'inherit', shell: true }),
child_process.spawn('npx', ['tsc', '-w', '--preserveWatchOutput', '-p', path.join(__dirname, '..', 'test-runner')], { stdio: 'inherit', shell: true }),
];
process.on('exit', () => spawns.forEach(s => s.kill()));