mirror of
https://github.com/microsoft/playwright.git
synced 2024-09-11 20:37:54 +03:00
chore(test-runner): move into its own folder and typescript project (#3548)
This commit is contained in:
parent
4c5635434a
commit
012f9425bf
@ -15,3 +15,4 @@ src/webkit/protocol.ts
|
||||
utils/generate_types/overrides.d.ts
|
||||
utils/generate_types/test/test.ts
|
||||
test/
|
||||
test-runner/
|
2
.github/workflows/auto_roll.yml
vendored
2
.github/workflows/auto_roll.yml
vendored
@ -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*"
|
||||
|
10
.github/workflows/tests.yml
vendored
10
.github/workflows/tests.yml
vendored
@ -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*"
|
||||
|
15
package.json
15
package.json
@ -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
2
test-runner/cli.js
Normal file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
module.exports = require('./lib/cli')
|
8
test-runner/package.json
Normal file
8
test-runner/package.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "test-runner",
|
||||
"version": "0.0.7",
|
||||
"bin": {
|
||||
"test-runner": "./cli.js"
|
||||
},
|
||||
"main": "./lib/index.js"
|
||||
}
|
@ -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;
|
72
test-runner/src/builtin.fixtures.ts
Normal file
72
test-runner/src/builtin.fixtures.ts
Normal 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 => {});
|
||||
});
|
@ -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)
|
@ -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('');
|
@ -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
18
test-runner/src/index.ts
Normal 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';
|
@ -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.
|
@ -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);
|
@ -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 };
|
@ -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
20
test-runner/tsconfig.json
Normal 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"]
|
||||
}
|
@ -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({
|
||||
|
@ -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';
|
||||
|
@ -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 '..';
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
import './playwright.fixtures';
|
||||
|
||||
import { registerFixture } from './runner';
|
||||
import { registerFixture } from '../test-runner';
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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 => {});
|
||||
});
|
@ -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';
|
||||
|
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { registerFixture } from './runner';
|
||||
import { registerFixture } from '../test-runner';
|
||||
|
||||
declare global {
|
||||
interface TestState {
|
||||
|
@ -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
37
test/types.d.ts
vendored
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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()));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user