mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
test: do not inherit from the Node environment (#3348)
This commit is contained in:
parent
b3091deb78
commit
f6d321fb6a
124
test/jest/fixturePool.js
Normal file
124
test/jest/fixturePool.js
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* 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 debug = require('debug');
|
||||||
|
|
||||||
|
const registrations = new Map();
|
||||||
|
|
||||||
|
class Fixture {
|
||||||
|
constructor(pool, name, scope, fn) {
|
||||||
|
this.pool = pool;
|
||||||
|
this.name = name;
|
||||||
|
this.scope = scope;
|
||||||
|
this.fn = fn;
|
||||||
|
this.deps = fixtureParameterNames(this.fn);
|
||||||
|
this.usages = new Set();
|
||||||
|
this.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setup() {
|
||||||
|
for (const name of this.deps) {
|
||||||
|
await this.pool.setupFixture(name);
|
||||||
|
this.pool.instances.get(name).usages.add(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {};
|
||||||
|
for (const n of this.deps)
|
||||||
|
params[n] = this.pool.instances.get(n).value;
|
||||||
|
let setupFenceFulfill;
|
||||||
|
let setupFenceReject;
|
||||||
|
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.value = value;
|
||||||
|
setupFenceFulfill();
|
||||||
|
await teardownFence;
|
||||||
|
}).catch(e => setupFenceReject(e));
|
||||||
|
await setupFence;
|
||||||
|
this._setup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async teardown() {
|
||||||
|
if (this._teardown)
|
||||||
|
return;
|
||||||
|
this._teardown = true;
|
||||||
|
for (const name of this.usages) {
|
||||||
|
const fixture = this.pool.instances.get(name);
|
||||||
|
if (!fixture)
|
||||||
|
continue;
|
||||||
|
await fixture.teardown();
|
||||||
|
}
|
||||||
|
if (this._setup) {
|
||||||
|
debug('pw:test:hook')(`teardown "${this.name}"`);
|
||||||
|
this._teardownFenceCallback();
|
||||||
|
}
|
||||||
|
await this._tearDownComplete;
|
||||||
|
this.pool.instances.delete(this.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FixturePool {
|
||||||
|
constructor() {
|
||||||
|
this.instances = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
async setupFixture(name) {
|
||||||
|
let fixture = this.instances.get(name);
|
||||||
|
if (fixture)
|
||||||
|
return fixture;
|
||||||
|
|
||||||
|
if (!registrations.has(name))
|
||||||
|
throw new Error('Unknown fixture: ' + name);
|
||||||
|
const { scope, fn } = registrations.get(name);
|
||||||
|
fixture = new Fixture(this, name, scope, fn);
|
||||||
|
this.instances.set(name, fixture);
|
||||||
|
await fixture.setup();
|
||||||
|
return fixture;
|
||||||
|
}
|
||||||
|
|
||||||
|
async teardownScope(scope) {
|
||||||
|
for (const [name, fixture] of this.instances) {
|
||||||
|
if (fixture.scope === scope)
|
||||||
|
await fixture.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async resolveParametersAndRun(fn) {
|
||||||
|
const names = fixtureParameterNames(fn);
|
||||||
|
for (const name of names)
|
||||||
|
await this.setupFixture(name);
|
||||||
|
const params = {};
|
||||||
|
for (const n of names)
|
||||||
|
params[n] = this.instances.get(n).value;
|
||||||
|
return fn(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixtureParameterNames(fn) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerFixture(name, scope, fn) {
|
||||||
|
registrations.set(name, { scope, fn });
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { FixturePool, registerFixture };
|
||||||
@ -14,8 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const NodeEnvironment = require('jest-environment-node');
|
|
||||||
const registerFixtures = require('./fixtures');
|
const registerFixtures = require('./fixtures');
|
||||||
|
const { FixturePool, registerFixture } = require('./fixturePool');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
@ -26,52 +26,62 @@ const GoldenUtils = require('../../utils/testrunner/GoldenUtils');
|
|||||||
const {installCoverageHooks} = require('./coverage');
|
const {installCoverageHooks} = require('./coverage');
|
||||||
const browserName = process.env.BROWSER || 'chromium';
|
const browserName = process.env.BROWSER || 'chromium';
|
||||||
const reportOnly = !!process.env.REPORT_ONLY_PLATFORM;
|
const reportOnly = !!process.env.REPORT_ONLY_PLATFORM;
|
||||||
|
const { ModuleMocker } = require('jest-mock');
|
||||||
|
|
||||||
class PlaywrightEnvironment extends NodeEnvironment {
|
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) {
|
constructor(config, context) {
|
||||||
super(config, context);
|
this.moduleMocker = new ModuleMocker(global);
|
||||||
this.fixturePool = new FixturePool();
|
this.fixturePool = new FixturePool();
|
||||||
const testOptions = {};
|
this.global = global;
|
||||||
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);
|
|
||||||
this.global.testOptions = testOptions;
|
this.global.testOptions = testOptions;
|
||||||
this.testPath = context.testPath;
|
this.testPath = context.testPath;
|
||||||
|
|
||||||
this.global.registerFixture = (name, fn) => {
|
|
||||||
this.fixturePool.registerFixture(name, 'test', fn);
|
|
||||||
};
|
|
||||||
this.global.registerWorkerFixture = (name, fn) => {
|
|
||||||
this.fixturePool.registerFixture(name, 'worker', fn);
|
|
||||||
};
|
|
||||||
registerFixtures(this.global);
|
|
||||||
|
|
||||||
process.on('SIGINT', async () => {
|
|
||||||
await this.fixturePool.teardownScope('test');
|
|
||||||
await this.fixturePool.teardownScope('worker');
|
|
||||||
process.exit(130);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setup() {
|
async setup() {
|
||||||
await super.setup();
|
|
||||||
const {coverage, uninstall} = installCoverageHooks(browserName);
|
const {coverage, uninstall} = installCoverageHooks(browserName);
|
||||||
this.coverage = coverage;
|
this.coverage = coverage;
|
||||||
this.uninstallCoverage = uninstall;
|
this.uninstallCoverage = uninstall;
|
||||||
|
currentFixturePool = this.fixturePool;
|
||||||
}
|
}
|
||||||
|
|
||||||
async teardown() {
|
async teardown() {
|
||||||
|
currentFixturePool = null;
|
||||||
await this.fixturePool.teardownScope('worker');
|
await this.fixturePool.teardownScope('worker');
|
||||||
await super.teardown();
|
|
||||||
// If the setup throws an error, we don't want to override it
|
// If the setup throws an error, we don't want to override it
|
||||||
// with a useless error about this.coverage not existing.
|
// with a useless error about this.coverage not existing.
|
||||||
if (!this.coverage)
|
if (!this.coverage)
|
||||||
@ -88,7 +98,7 @@ class PlaywrightEnvironment extends NodeEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runScript(script) {
|
runScript(script) {
|
||||||
return super.runScript(script);
|
return script.runInThisContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
patchToEnableFixtures(object, name) {
|
patchToEnableFixtures(object, name) {
|
||||||
@ -200,113 +210,6 @@ class PlaywrightEnvironment extends NodeEnvironment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Fixture {
|
|
||||||
constructor(pool, name, scope, fn) {
|
|
||||||
this.pool = pool;
|
|
||||||
this.name = name;
|
|
||||||
this.scope = scope;
|
|
||||||
this.fn = fn;
|
|
||||||
this.deps = fixtureParameterNames(this.fn);
|
|
||||||
this.usages = new Set();
|
|
||||||
this.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setup() {
|
|
||||||
for (const name of this.deps) {
|
|
||||||
await this.pool.setupFixture(name);
|
|
||||||
this.pool.instances.get(name).usages.add(this.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {};
|
|
||||||
for (const n of this.deps)
|
|
||||||
params[n] = this.pool.instances.get(n).value;
|
|
||||||
let setupFenceFulfill;
|
|
||||||
let setupFenceReject;
|
|
||||||
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.value = value;
|
|
||||||
setupFenceFulfill();
|
|
||||||
await teardownFence;
|
|
||||||
}).catch(e => setupFenceReject(e));
|
|
||||||
await setupFence;
|
|
||||||
this._setup = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async teardown() {
|
|
||||||
if (this._teardown)
|
|
||||||
return;
|
|
||||||
this._teardown = true;
|
|
||||||
for (const name of this.usages) {
|
|
||||||
const fixture = this.pool.instances.get(name);
|
|
||||||
if (!fixture)
|
|
||||||
continue;
|
|
||||||
await fixture.teardown();
|
|
||||||
}
|
|
||||||
if (this._setup) {
|
|
||||||
debug('pw:test:hook')(`teardown "${this.name}"`);
|
|
||||||
this._teardownFenceCallback();
|
|
||||||
}
|
|
||||||
await this._tearDownComplete;
|
|
||||||
this.pool.instances.delete(this.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FixturePool {
|
|
||||||
constructor() {
|
|
||||||
this.registrations = new Map();
|
|
||||||
this.instances = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
registerFixture(name, scope, fn) {
|
|
||||||
this.registrations.set(name, { scope, fn });
|
|
||||||
}
|
|
||||||
|
|
||||||
async setupFixture(name) {
|
|
||||||
let fixture = this.instances.get(name);
|
|
||||||
if (fixture)
|
|
||||||
return fixture;
|
|
||||||
|
|
||||||
if (!this.registrations.has(name))
|
|
||||||
throw new Error('Unknown fixture: ' + name);
|
|
||||||
const { scope, fn } = this.registrations.get(name);
|
|
||||||
fixture = new Fixture(this, name, scope, fn);
|
|
||||||
this.instances.set(name, fixture);
|
|
||||||
await fixture.setup();
|
|
||||||
return fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
async teardownScope(scope) {
|
|
||||||
for (const [name, fixture] of this.instances) {
|
|
||||||
if (fixture.scope === scope)
|
|
||||||
await fixture.teardown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async resolveParametersAndRun(fn) {
|
|
||||||
const names = fixtureParameterNames(fn);
|
|
||||||
for (const name of names)
|
|
||||||
await this.setupFixture(name);
|
|
||||||
const params = {};
|
|
||||||
for (const n of names)
|
|
||||||
params[n] = this.instances.get(n).value;
|
|
||||||
return fn(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getPlaywrightEnv = () => PlaywrightEnvironment;
|
|
||||||
exports.default = exports.getPlaywrightEnv();
|
|
||||||
|
|
||||||
function fixtureParameterNames(fn) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
function valueFromEnv(name, defaultValue) {
|
function valueFromEnv(name, defaultValue) {
|
||||||
if (!(name in process.env))
|
if (!(name in process.env))
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
@ -321,3 +224,6 @@ function testOrSuiteName(o) {
|
|||||||
name += ' ';
|
name += ' ';
|
||||||
return name + o.name;
|
return name + o.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.getPlaywrightEnv = () => PlaywrightEnvironment;
|
||||||
|
exports.default = exports.getPlaywrightEnv();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user