playwright/test/jest/playwrightEnvironment.js

177 lines
4.8 KiB
JavaScript
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.
*/
const NodeEnvironment = require('jest-environment-node');
const path = require('path');
const playwright = require('../../index');
class PlaywrightEnvironment extends NodeEnvironment {
constructor(config, context) {
super(config, context);
this.fixturePool = new FixturePool();
this.global.registerFixture = (name, fn) => {
this.fixturePool.registerFixture(name, 'test', fn);
};
this.global.registerWorkerFixture = (name, fn) => {
this.fixturePool.registerFixture(name, 'worker', fn);
};
this.global.registerWorkerFixture('browser', async (test) => {
const browser = await playwright[process.env.BROWSER || 'chromium'].launch();
await test(browser);
await browser.close();
});
this.global.registerFixture('context', async (browser, test) => {
const context = await browser.newContext();
await test(context);
await context.close();
});
this.global.registerFixture('page', async (context, test) => {
const page = await context.newPage();
await test(page);
});
}
async setup() {
await super.setup();
}
async teardown() {
await this.fixturePool.teardownScope('worker');
await super.teardown();
}
runScript(script) {
return super.runScript(script);
}
async handleTestEvent(event, state) {
if (event.name === 'test_start') {
const fn = event.test.fn;
event.test.fn = async () => {
try {
return await this.fixturePool.resolveParametersAndRun(fn);
} finally {
await this.fixturePool.teardownScope('test');
}
};
}
}
}
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 = this.deps.map(n => this.pool.instances.get(n).value);
let setupFenceCallback;
const setupFence = new Promise(f => setupFenceCallback = f);
const teardownFence = new Promise(f => this._teardownFenceCallback = f);
this._tearDownComplete = this.fn(...params, async value => {
this.value = value;
setupFenceCallback();
await teardownFence;
});
await setupFence;
}
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();
}
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;
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);
await fn(...names.map(n => this.instances.get(n).value));
}
}
exports.getPlaywrightEnv = () => PlaywrightEnvironment;
exports.default = exports.getPlaywrightEnv();
function fixtureParameterNames(fn) {
const text = fn.toString();
const match = text.match(/async\ (.*) =>/);
if (!match)
return [];
let signature = match[1];
if (signature.startsWith('(') && signature.endsWith(')'))
signature = signature.substring(1, signature.length - 1);
if (!signature.trim())
return [];
const result = signature.split(',').map(t => t.trim());
return result.filter(s => s !== 'test');
}