playwright/test-runner/src/test.ts

213 lines
5 KiB
TypeScript
Raw Normal View History

/**
* 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 Mocha from 'mocha';
import { fixturesUI } from './fixturesUI';
import { EventEmitter } from 'events';
export type Configuration = { name: string, value: string }[];
export class Test {
suite: Suite;
title: string;
file: string;
only = false;
pending = false;
duration = 0;
timeout = 0;
fn: Function;
_ordinal: number;
_configurationObject: Configuration;
_configurationString: string;
_overriddenFn: Function;
_impl: any;
constructor(title: string, fn: Function) {
this.title = title;
this.fn = fn;
}
_materialize(overriddenFn: Function) {
this._impl = new Mocha.Test(this.title, overriddenFn);
this._impl.pending = this.pending;
}
clone(): Test {
const test = new Test(this.title, this.fn);
test.suite = this.suite;
test.only = this.only;
test.file = this.file;
test.pending = this.pending;
test.timeout = this.timeout;
test._overriddenFn = this._overriddenFn;
test._materialize(this._overriddenFn);
return test;
}
titlePath(): string[] {
return [...this.suite.titlePath(), this.title];
}
fullTitle(): string {
return this.titlePath().join(' ');
}
slow(): number {
return 10000;
}
}
export class Suite {
title: string;
parent?: Suite;
suites: Suite[] = [];
tests: Test[] = [];
only = false;
pending = false;
file: string;
_impl: any;
constructor(title: string, parent?: Suite) {
this.title = title;
this.parent = parent;
this._impl = new Mocha.Suite(title, new Mocha.Context());
this._impl.__nomocha = this;
}
titlePath(): string[] {
if (!this.parent)
return [this.title];
return [...this.parent.titlePath(), this.title];
}
total(): number {
let count = 0;
this.eachTest(fn => ++count);
return count;
}
isPending(): boolean {
return this.pending || (this.parent && this.parent.isPending());
}
addTest(test: Test) {
test.suite = this;
this.tests.push(test);
this._impl.addTest(test._impl);
}
addSuite(suite: Suite) {
suite.parent = this;
this.suites.push(suite);
this._impl.addSuite(suite._impl);
}
eachTest(fn: (test: Test) => void) {
for (const suite of this.suites)
suite.eachTest(fn);
for (const test of this.tests)
fn(test);
}
clone(): Suite {
const suite = new Suite(this.title);
suite.only = this.only;
suite.file = this.file;
suite.pending = this.pending;
suite._impl = this._impl.clone();
return suite;
}
}
class NullReporter {
stats = {
suites: 0,
tests: 0,
passes: 0,
pending: 0,
failures: 0
};
runner = null;
failures = [];
epilogue: () => {};
}
type NoMockaOptions = {
forbidOnly?: boolean;
timeout: number;
testWrapper: (test: Test, fn: Function) => Function;
hookWrapper: (hook: any, fn: Function) => Function;
};
class PatchedMocha extends Mocha {
suite: any;
static pendingSuite: Suite;
constructor(suite, options) {
PatchedMocha.pendingSuite = suite;
super(options);
}
grep(...args) {
this.suite = new Mocha.Suite('', new Mocha.Context());
this.suite.__nomocha = PatchedMocha.pendingSuite;
PatchedMocha.pendingSuite._impl = this.suite;
return super.grep(...args);
}
}
export class Runner extends EventEmitter {
private _mochaRunner: any;
constructor(mochaRunner: any) {
super();
const constants = Mocha.Runner.constants;
this._mochaRunner = mochaRunner;
this._mochaRunner.on(constants.EVENT_TEST_BEGIN, test => this.emit('test', test));
this._mochaRunner.on(constants.EVENT_TEST_PENDING, test => this.emit('pending', test));
this._mochaRunner.on(constants.EVENT_TEST_PASS, test => this.emit('pass', test));
this._mochaRunner.on(constants.EVENT_TEST_FAIL, (test, err) => this.emit('fail', test, err));
this._mochaRunner.on(constants.EVENT_RUN_END, () => this.emit('done'));
}
duration(): number {
return this._mochaRunner.stats.duration || 0;
}
}
export class NoMocha {
suite: Suite;
private _mocha: Mocha;
constructor(file: string, options: NoMockaOptions) {
this.suite = new Suite('');
this._mocha = new PatchedMocha(this.suite, {
forbidOnly: options.forbidOnly,
reporter: NullReporter,
timeout: options.timeout,
ui: fixturesUI.bind(null, options)
});
this._mocha.addFile(file);
(this._mocha as any).loadFiles();
}
run(cb: () => void): Runner {
return new Runner(this._mocha.run(cb));
}
}