mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
230 lines
7.4 KiB
JavaScript
230 lines
7.4 KiB
JavaScript
/**
|
|
* 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 registerFixtures = require('./fixtures');
|
|
const { FixturePool, registerFixture } = require('./fixturePool');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const debug = require('debug');
|
|
const util = require('util');
|
|
const platform = process.env.REPORT_ONLY_PLATFORM || os.platform();
|
|
const GoldenUtils = require('../../utils/testrunner/GoldenUtils');
|
|
const {installCoverageHooks} = require('./coverage');
|
|
const browserName = process.env.BROWSER || 'chromium';
|
|
const reportOnly = !!process.env.REPORT_ONLY_PLATFORM;
|
|
const { ModuleMocker } = require('jest-mock');
|
|
|
|
const testOptions = {};
|
|
testOptions.MAC = platform === 'darwin';
|
|
testOptions.LINUX = platform === 'linux';
|
|
testOptions.WIN = platform === 'win32';
|
|
testOptions.CHROMIUM = browserName === 'chromium';
|
|
testOptions.FFOX = browserName === 'firefox';
|
|
testOptions.WEBKIT = browserName === 'webkit';
|
|
testOptions.USES_HOOKS = process.env.PWCHANNEL === 'wire';
|
|
testOptions.CHANNEL = !!process.env.PWCHANNEL;
|
|
testOptions.HEADLESS = !!valueFromEnv('HEADLESS', true);
|
|
testOptions.ASSETS_DIR = path.join(__dirname, '..', 'assets');
|
|
testOptions.GOLDEN_DIR = path.join(__dirname, '..', 'golden-' + browserName);
|
|
testOptions.OUTPUT_DIR = path.join(__dirname, '..', 'output-' + browserName);
|
|
global.testOptions = testOptions;
|
|
|
|
global.registerFixture = (name, fn) => {
|
|
registerFixture(name, 'test', fn);
|
|
};
|
|
|
|
global.registerWorkerFixture = (name, fn) => {
|
|
registerFixture(name, 'worker', fn);
|
|
};
|
|
|
|
registerFixtures(global);
|
|
|
|
let currentFixturePool = null;
|
|
|
|
process.on('SIGINT', async () => {
|
|
if (currentFixturePool) {
|
|
await currentFixturePool.teardownScope('test');
|
|
await currentFixturePool.teardownScope('worker');
|
|
}
|
|
process.exit(130);
|
|
});
|
|
|
|
class PlaywrightEnvironment {
|
|
constructor(config, context) {
|
|
this.moduleMocker = new ModuleMocker(global);
|
|
this.fixturePool = new FixturePool();
|
|
this.global = global;
|
|
this.global.testOptions = testOptions;
|
|
this.testPath = context.testPath;
|
|
}
|
|
|
|
async setup() {
|
|
const {coverage, uninstall} = installCoverageHooks(browserName);
|
|
this.coverage = coverage;
|
|
this.uninstallCoverage = uninstall;
|
|
currentFixturePool = this.fixturePool;
|
|
}
|
|
|
|
async teardown() {
|
|
currentFixturePool = null;
|
|
await this.fixturePool.teardownScope('worker');
|
|
// If the setup throws an error, we don't want to override it
|
|
// with a useless error about this.coverage not existing.
|
|
if (!this.coverage)
|
|
return;
|
|
this.uninstallCoverage();
|
|
const testRoot = path.join(__dirname, '..');
|
|
const relativeTestPath = path.relative(testRoot, this.testPath);
|
|
const coveragePath = path.join(this.global.testOptions.OUTPUT_DIR, 'coverage', relativeTestPath + '.json');
|
|
const coverageJSON = [...this.coverage.keys()].filter(key => this.coverage.get(key));
|
|
await fs.promises.mkdir(path.dirname(coveragePath), { recursive: true });
|
|
await fs.promises.writeFile(coveragePath, JSON.stringify(coverageJSON, undefined, 2), 'utf8');
|
|
delete this.coverage;
|
|
delete this.uninstallCoverage;
|
|
}
|
|
|
|
runScript(script) {
|
|
return script.runInThisContext();
|
|
}
|
|
|
|
patchToEnableFixtures(object, name) {
|
|
const original = object[name];
|
|
object[name] = fn => {
|
|
return original(async () => {
|
|
return await this.fixturePool.resolveParametersAndRun(fn);
|
|
});
|
|
}
|
|
}
|
|
|
|
async handleTestEvent(event, state) {
|
|
if (event.name === 'setup') {
|
|
this.patchToEnableFixtures(this.global, 'beforeEach');
|
|
this.patchToEnableFixtures(this.global, 'afterEach');
|
|
|
|
const describeSkip = this.global.describe.skip;
|
|
this.global.describe.skip = (...args) => {
|
|
if (args.length === 1)
|
|
return args[0] ? describeSkip : this.global.describe;
|
|
return describeSkip(...args);
|
|
};
|
|
|
|
function addSlow(f) {
|
|
f.slow = () => {
|
|
return (...args) => f(...args, 90000);
|
|
};
|
|
return f;
|
|
}
|
|
|
|
const itSkip = this.global.it.skip;
|
|
addSlow(itSkip);
|
|
addSlow(this.global.it);
|
|
this.global.it.skip = (...args) => {
|
|
if (args.length === 1)
|
|
return args[0] ? itSkip : this.global.it;
|
|
return itSkip(...args);
|
|
};
|
|
if (reportOnly) {
|
|
this.global.it.fail = condition => {
|
|
return addSlow((...inner) => {
|
|
inner[1].__fail = !!condition;
|
|
return this.global.it(...inner);
|
|
});
|
|
};
|
|
} else {
|
|
this.global.it.fail = this.global.it.skip;
|
|
}
|
|
|
|
const testOptions = this.global.testOptions;
|
|
function toBeGolden(received, goldenName) {
|
|
const {snapshotState} = this;
|
|
const updateSnapshot = snapshotState._updateSnapshot;
|
|
const expectedPath = path.join(testOptions.GOLDEN_DIR, goldenName);
|
|
const fileExists = fs.existsSync(expectedPath);
|
|
if (updateSnapshot === 'all' || (updateSnapshot === 'new' && !fileExists)) {
|
|
fs.writeFileSync(expectedPath, received);
|
|
if (fileExists)
|
|
snapshotState.updated++;
|
|
else
|
|
snapshotState.added++;
|
|
return {
|
|
pass: true
|
|
}
|
|
};
|
|
|
|
const {pass, message} = GoldenUtils.compare(received, {
|
|
goldenPath: testOptions.GOLDEN_DIR,
|
|
outputPath: testOptions.OUTPUT_DIR,
|
|
goldenName
|
|
});
|
|
if (pass)
|
|
snapshotState.matched++;
|
|
else
|
|
snapshotState.unmatched++;
|
|
return {pass, message: () => message};
|
|
};
|
|
this.global.expect.extend({ toBeGolden });
|
|
}
|
|
|
|
if (event.name === 'test_start') {
|
|
const fn = event.test.fn;
|
|
this._lastTest = event.test;
|
|
event.test.fn = async () => {
|
|
if (reportOnly) {
|
|
if (fn.__fail)
|
|
throw new Error('fail');
|
|
return;
|
|
}
|
|
debug('pw:test')(`start "${testOrSuiteName(event.test)}"`);
|
|
try {
|
|
await this.fixturePool.resolveParametersAndRun(fn);
|
|
} catch(e) {
|
|
debug('pw:test')(`error "${testOrSuiteName(event.test)}"`, util.inspect(e));
|
|
throw e;
|
|
} finally {
|
|
await this.fixturePool.teardownScope('test');
|
|
debug('pw:test')(`finish "${testOrSuiteName(event.test)}"`);
|
|
}
|
|
};
|
|
}
|
|
|
|
if (event.name === 'error')
|
|
debug('pw:test')(`error "${testOrSuiteName(this._lastTest)}"`, util.inspect(event.error));
|
|
|
|
if (event.name === 'test_fn_failure') {
|
|
await this.fixturePool.teardownScope('worker');
|
|
}
|
|
}
|
|
}
|
|
|
|
function valueFromEnv(name, defaultValue) {
|
|
if (!(name in process.env))
|
|
return defaultValue;
|
|
return JSON.parse(process.env[name]);
|
|
}
|
|
|
|
function testOrSuiteName(o) {
|
|
if (o.name === 'ROOT_DESCRIBE_BLOCK')
|
|
return '';
|
|
let name = o.parent ? testOrSuiteName(o.parent) : '';
|
|
if (name && o.name)
|
|
name += ' ';
|
|
return name + o.name;
|
|
}
|
|
|
|
exports.getPlaywrightEnv = () => PlaywrightEnvironment;
|
|
exports.default = exports.getPlaywrightEnv();
|