reland: testrunner: make environment a simple class (#2812)
This re-lands PR https://github.com/microsoft/playwright/pull/2769 It was reverted before in https://github.com/microsoft/playwright/pull/2790 because it was breaking the new CHANNEL bot.
This commit is contained in:
parent
024cb1ddc1
commit
05b019f1ba
247
test/environments.js
Normal file
247
test/environments.js
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const utils = require('./utils');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const rm = require('rimraf').sync;
|
||||
const {TestServer} = require('../utils/testserver/');
|
||||
const { DispatcherConnection } = require('../lib/rpc/server/dispatcher');
|
||||
const { Connection } = require('../lib/rpc/client/connection');
|
||||
const { BrowserTypeDispatcher } = require('../lib/rpc/server/browserTypeDispatcher');
|
||||
|
||||
class ServerEnvironment {
|
||||
async beforeAll(state) {
|
||||
const assetsPath = path.join(__dirname, 'assets');
|
||||
const cachedPath = path.join(__dirname, 'assets', 'cached');
|
||||
|
||||
const port = 8907 + state.parallelIndex * 2;
|
||||
state.server = await TestServer.create(assetsPath, port);
|
||||
state.server.enableHTTPCache(cachedPath);
|
||||
state.server.PORT = port;
|
||||
state.server.PREFIX = `http://localhost:${port}`;
|
||||
state.server.CROSS_PROCESS_PREFIX = `http://127.0.0.1:${port}`;
|
||||
state.server.EMPTY_PAGE = `http://localhost:${port}/empty.html`;
|
||||
|
||||
const httpsPort = port + 1;
|
||||
state.httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort);
|
||||
state.httpsServer.enableHTTPCache(cachedPath);
|
||||
state.httpsServer.PORT = httpsPort;
|
||||
state.httpsServer.PREFIX = `https://localhost:${httpsPort}`;
|
||||
state.httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`;
|
||||
state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`;
|
||||
}
|
||||
|
||||
async afterAll({server, httpsServer}) {
|
||||
await Promise.all([
|
||||
server.stop(),
|
||||
httpsServer.stop(),
|
||||
]);
|
||||
}
|
||||
|
||||
async beforeEach(state) {
|
||||
state.server.reset();
|
||||
state.httpsServer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultBrowserOptionsEnvironment {
|
||||
constructor(defaultBrowserOptions, dumpLogOnFailure, playwrightPath) {
|
||||
this._defaultBrowserOptions = defaultBrowserOptions;
|
||||
this._dumpLogOnFailure = dumpLogOnFailure;
|
||||
this._playwrightPath = playwrightPath;
|
||||
this._loggerSymbol = Symbol('DefaultBrowserOptionsEnvironment.logger');
|
||||
}
|
||||
|
||||
async beforeAll(state) {
|
||||
state[this._loggerSymbol] = utils.createTestLogger(this._dumpLogOnFailure, null, 'extra');
|
||||
state.defaultBrowserOptions = {
|
||||
...this._defaultBrowserOptions,
|
||||
logger: state[this._loggerSymbol],
|
||||
};
|
||||
state.playwrightPath = this._playwrightPath;
|
||||
}
|
||||
|
||||
async beforeEach(state, testRun) {
|
||||
state[this._loggerSymbol].setTestRun(testRun);
|
||||
}
|
||||
|
||||
async afterEach(state) {
|
||||
state[this._loggerSymbol].setTestRun(null);
|
||||
}
|
||||
}
|
||||
|
||||
// simulate globalSetup per browserType that happens only once regardless of TestWorker.
|
||||
const hasBeenCleaned = new Set();
|
||||
|
||||
class GoldenEnvironment {
|
||||
async beforeAll(state) {
|
||||
const { OUTPUT_DIR, GOLDEN_DIR } = utils.testOptions(state.browserType);
|
||||
if (!hasBeenCleaned.has(state.browserType)) {
|
||||
hasBeenCleaned.add(state.browserType);
|
||||
if (fs.existsSync(OUTPUT_DIR))
|
||||
rm(OUTPUT_DIR);
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
state.golden = goldenName => ({ goldenPath: GOLDEN_DIR, outputPath: OUTPUT_DIR, goldenName });
|
||||
}
|
||||
|
||||
async afterAll(state) {
|
||||
delete state.golden;
|
||||
}
|
||||
|
||||
async afterEach(state, testRun) {
|
||||
if (state.browser && state.browser.contexts().length !== 0) {
|
||||
if (testRun.ok())
|
||||
console.warn(`\nWARNING: test "${testRun.test().fullName()}" (${testRun.test().location()}) did not close all created contexts!\n`);
|
||||
await Promise.all(state.browser.contexts().map(context => context.close()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TraceTestEnvironment {
|
||||
static enableForTest(test) {
|
||||
test.setTimeout(100000000);
|
||||
test.addEnvironment(new TraceTestEnvironment());
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._session = null;
|
||||
}
|
||||
|
||||
async beforeEach() {
|
||||
const inspector = require('inspector');
|
||||
const fs = require('fs');
|
||||
const util = require('util');
|
||||
const url = require('url');
|
||||
const readFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||
this._session = new inspector.Session();
|
||||
this._session.connect();
|
||||
const postAsync = util.promisify(this._session.post.bind(this._session));
|
||||
await postAsync('Debugger.enable');
|
||||
const setBreakpointCommands = [];
|
||||
const N = t.body().toString().split('\n').length;
|
||||
const location = t.location();
|
||||
const lines = (await readFileAsync(location.filePath(), 'utf8')).split('\n');
|
||||
for (let line = 0; line < N; ++line) {
|
||||
const lineNumber = line + location.lineNumber();
|
||||
setBreakpointCommands.push(postAsync('Debugger.setBreakpointByUrl', {
|
||||
url: url.pathToFileURL(location.filePath()),
|
||||
lineNumber,
|
||||
condition: `console.log('${String(lineNumber + 1).padStart(6, ' ')} | ' + ${JSON.stringify(lines[lineNumber])})`,
|
||||
}).catch(e => {}));
|
||||
}
|
||||
await Promise.all(setBreakpointCommands);
|
||||
}
|
||||
|
||||
async afterEach() {
|
||||
this._session.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
class PlaywrightEnvironment {
|
||||
constructor(playwright) {
|
||||
this._playwright = playwright;
|
||||
}
|
||||
|
||||
name() { return 'Playwright'; };
|
||||
beforeAll(state) { state.playwright = this._playwright; }
|
||||
afterAll(state) { delete state.playwright; }
|
||||
}
|
||||
|
||||
class BrowserTypeEnvironment {
|
||||
constructor(browserType) {
|
||||
this._browserType = browserType;
|
||||
}
|
||||
|
||||
async beforeAll(state) {
|
||||
// Channel substitute
|
||||
let overridenBrowserType = this._browserType;
|
||||
if (process.env.PWCHANNEL) {
|
||||
const dispatcherConnection = new DispatcherConnection();
|
||||
const connection = new Connection();
|
||||
dispatcherConnection.onmessage = async message => {
|
||||
setImmediate(() => connection.dispatch(message));
|
||||
};
|
||||
connection.onmessage = async message => {
|
||||
const result = await dispatcherConnection.dispatch(message);
|
||||
await new Promise(f => setImmediate(f));
|
||||
return result;
|
||||
};
|
||||
new BrowserTypeDispatcher(dispatcherConnection.rootScope(), this._browserType);
|
||||
overridenBrowserType = await connection.waitForObjectWithKnownName(this._browserType.name());
|
||||
}
|
||||
state.browserType = overridenBrowserType;
|
||||
}
|
||||
|
||||
async afterAll(state) {
|
||||
delete state.browserType;
|
||||
}
|
||||
}
|
||||
|
||||
class BrowserEnvironment {
|
||||
constructor(launchOptions, dumpLogOnFailure) {
|
||||
this._launchOptions = launchOptions;
|
||||
this._dumpLogOnFailure = dumpLogOnFailure;
|
||||
this._loggerSymbol = Symbol('BrowserEnvironment.logger');
|
||||
}
|
||||
|
||||
async beforeAll(state) {
|
||||
state[this._loggerSymbol] = utils.createTestLogger(this._dumpLogOnFailure);
|
||||
state.browser = await state.browserType.launch({
|
||||
...this._launchOptions,
|
||||
logger: state[this._loggerSymbol],
|
||||
});
|
||||
}
|
||||
|
||||
async afterAll(state) {
|
||||
await state.browser.close();
|
||||
delete state.browser;
|
||||
}
|
||||
|
||||
async beforeEach(state, testRun) {
|
||||
state[this._loggerSymbol].setTestRun(testRun);
|
||||
}
|
||||
|
||||
async afterEach(state, testRun) {
|
||||
state[this._loggerSymbol].setTestRun(null);
|
||||
}
|
||||
}
|
||||
|
||||
class PageEnvironment {
|
||||
async beforeEach(state) {
|
||||
state.context = await state.browser.newContext();
|
||||
state.page = await state.context.newPage();
|
||||
}
|
||||
|
||||
async afterEach(state) {
|
||||
await state.context.close();
|
||||
state.context = null;
|
||||
state.page = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ServerEnvironment,
|
||||
GoldenEnvironment,
|
||||
TraceTestEnvironment,
|
||||
DefaultBrowserOptionsEnvironment,
|
||||
PlaywrightEnvironment,
|
||||
BrowserTypeEnvironment,
|
||||
BrowserEnvironment,
|
||||
PageEnvironment,
|
||||
};
|
||||
|
|
@ -1305,3 +1305,9 @@ describe('Page api coverage', function() {
|
|||
expect(await frame.evaluate(() => document.querySelector('textarea').value)).toBe('a');
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(!CHANNEL)('Page channel', function() {
|
||||
it('page should be client stub', async({page, server}) => {
|
||||
expect(!!page._channel).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,85 +15,21 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const rm = require('rimraf').sync;
|
||||
const utils = require('./utils');
|
||||
const {TestServer} = require('../utils/testserver/');
|
||||
const {Environment} = require('../utils/testrunner/Test');
|
||||
const {DefaultBrowserOptionsEnvironment, ServerEnvironment, GoldenEnvironment, TraceTestEnvironment} = require('./environments.js');
|
||||
|
||||
const playwrightPath = path.join(__dirname, '..');
|
||||
|
||||
const serverEnvironment = new Environment('TestServer');
|
||||
serverEnvironment.beforeAll(async state => {
|
||||
const assetsPath = path.join(__dirname, 'assets');
|
||||
const cachedPath = path.join(__dirname, 'assets', 'cached');
|
||||
const dumpLogOnFailure = valueFromEnv('DEBUGP', false);
|
||||
const defaultBrowserOptionsEnvironment = new DefaultBrowserOptionsEnvironment({
|
||||
handleSIGINT: false,
|
||||
slowMo: valueFromEnv('SLOW_MO', 0),
|
||||
headless: !!valueFromEnv('HEADLESS', true),
|
||||
}, dumpLogOnFailure, playwrightPath);
|
||||
|
||||
const port = 8907 + state.parallelIndex * 2;
|
||||
state.server = await TestServer.create(assetsPath, port);
|
||||
state.server.enableHTTPCache(cachedPath);
|
||||
state.server.PORT = port;
|
||||
state.server.PREFIX = `http://localhost:${port}`;
|
||||
state.server.CROSS_PROCESS_PREFIX = `http://127.0.0.1:${port}`;
|
||||
state.server.EMPTY_PAGE = `http://localhost:${port}/empty.html`;
|
||||
|
||||
const httpsPort = port + 1;
|
||||
state.httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort);
|
||||
state.httpsServer.enableHTTPCache(cachedPath);
|
||||
state.httpsServer.PORT = httpsPort;
|
||||
state.httpsServer.PREFIX = `https://localhost:${httpsPort}`;
|
||||
state.httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`;
|
||||
state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`;
|
||||
|
||||
state._extraLogger = utils.createTestLogger(valueFromEnv('DEBUGP', false), null, 'extra');
|
||||
state.defaultBrowserOptions = {
|
||||
handleSIGINT: false,
|
||||
slowMo: valueFromEnv('SLOW_MO', 0),
|
||||
headless: !!valueFromEnv('HEADLESS', true),
|
||||
logger: state._extraLogger,
|
||||
};
|
||||
state.playwrightPath = playwrightPath;
|
||||
});
|
||||
serverEnvironment.afterAll(async({server, httpsServer}) => {
|
||||
await Promise.all([
|
||||
server.stop(),
|
||||
httpsServer.stop(),
|
||||
]);
|
||||
});
|
||||
serverEnvironment.beforeEach(async(state, testRun) => {
|
||||
state.server.reset();
|
||||
state.httpsServer.reset();
|
||||
state._extraLogger.setTestRun(testRun);
|
||||
});
|
||||
serverEnvironment.afterEach(async(state) => {
|
||||
state._extraLogger.setTestRun(null);
|
||||
});
|
||||
|
||||
const customEnvironment = new Environment('Golden+CheckContexts');
|
||||
|
||||
// simulate globalSetup per browserType that happens only once regardless of TestWorker.
|
||||
const hasBeenCleaned = new Set();
|
||||
|
||||
customEnvironment.beforeAll(async state => {
|
||||
const { OUTPUT_DIR, GOLDEN_DIR } = require('./utils').testOptions(state.browserType);
|
||||
if (!hasBeenCleaned.has(state.browserType)) {
|
||||
hasBeenCleaned.add(state.browserType);
|
||||
if (fs.existsSync(OUTPUT_DIR))
|
||||
rm(OUTPUT_DIR);
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
state.golden = goldenName => ({ goldenPath: GOLDEN_DIR, outputPath: OUTPUT_DIR, goldenName });
|
||||
});
|
||||
customEnvironment.afterAll(async state => {
|
||||
delete state.golden;
|
||||
});
|
||||
customEnvironment.afterEach(async (state, testRun) => {
|
||||
if (state.browser && state.browser.contexts().length !== 0) {
|
||||
if (testRun.ok())
|
||||
console.warn(`\nWARNING: test "${testRun.test().fullName()}" (${testRun.test().location()}) did not close all created contexts!\n`);
|
||||
await Promise.all(state.browser.contexts().map(context => context.close()));
|
||||
}
|
||||
});
|
||||
const serverEnvironment = new ServerEnvironment();
|
||||
const customEnvironment = new GoldenEnvironment();
|
||||
|
||||
function valueFromEnv(name, defaultValue) {
|
||||
if (!(name in process.env))
|
||||
|
|
@ -108,39 +44,7 @@ function setupTestRunner(testRunner) {
|
|||
collector.addTestModifier('fail', (t, condition) => condition && t.setExpectation(t.Expectations.Fail));
|
||||
collector.addSuiteModifier('fail', (s, condition) => condition && s.setExpectation(s.Expectations.Fail));
|
||||
collector.addTestModifier('slow', t => t.setTimeout(t.timeout() * 3));
|
||||
collector.addTestAttribute('debug', t => {
|
||||
t.setTimeout(100000000);
|
||||
|
||||
let session;
|
||||
t.environment().beforeEach(async () => {
|
||||
const inspector = require('inspector');
|
||||
const fs = require('fs');
|
||||
const util = require('util');
|
||||
const url = require('url');
|
||||
const readFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||
session = new inspector.Session();
|
||||
session.connect();
|
||||
const postAsync = util.promisify(session.post.bind(session));
|
||||
await postAsync('Debugger.enable');
|
||||
const setBreakpointCommands = [];
|
||||
const N = t.body().toString().split('\n').length;
|
||||
const location = t.location();
|
||||
const lines = (await readFileAsync(location.filePath(), 'utf8')).split('\n');
|
||||
for (let line = 0; line < N; ++line) {
|
||||
const lineNumber = line + location.lineNumber();
|
||||
setBreakpointCommands.push(postAsync('Debugger.setBreakpointByUrl', {
|
||||
url: url.pathToFileURL(location.filePath()),
|
||||
lineNumber,
|
||||
condition: `console.log('${String(lineNumber + 1).padStart(6, ' ')} | ' + ${JSON.stringify(lines[lineNumber])})`,
|
||||
}).catch(e => {}));
|
||||
}
|
||||
await Promise.all(setBreakpointCommands);
|
||||
});
|
||||
|
||||
t.environment().afterEach(async () => {
|
||||
session.disconnect();
|
||||
});
|
||||
});
|
||||
collector.addTestAttribute('debug', t => TraceTestEnvironment.enableForTest(t));
|
||||
testRunner.api().fdescribe = testRunner.api().describe.only;
|
||||
testRunner.api().xdescribe = testRunner.api().describe.skip(true);
|
||||
testRunner.api().fit = testRunner.api().it.only;
|
||||
|
|
@ -161,7 +65,7 @@ module.exports = {
|
|||
headless: !!valueFromEnv('HEADLESS', true),
|
||||
},
|
||||
|
||||
globalEnvironments: [serverEnvironment],
|
||||
globalEnvironments: [defaultBrowserOptionsEnvironment, serverEnvironment],
|
||||
setupTestRunner,
|
||||
|
||||
specs: [
|
||||
|
|
|
|||
69
test/test.js
69
test/test.js
|
|
@ -18,10 +18,7 @@
|
|||
const fs = require('fs');
|
||||
const utils = require('./utils');
|
||||
const TestRunner = require('../utils/testrunner/');
|
||||
const { Environment } = require('../utils/testrunner/Test');
|
||||
const { DispatcherConnection } = require('../lib/rpc/server/dispatcher');
|
||||
const { Connection } = require('../lib/rpc/client/connection');
|
||||
const { BrowserTypeDispatcher } = require('../lib/rpc/server/browserTypeDispatcher');
|
||||
const { PlaywrightEnvironment, BrowserTypeEnvironment, BrowserEnvironment, PageEnvironment} = require('./environments.js');
|
||||
|
||||
Error.stackTraceLimit = 15;
|
||||
|
||||
|
|
@ -82,15 +79,7 @@ function collect(browserNames) {
|
|||
const { setUnderTest } = require(require('path').join(playwrightPath, 'lib/helper.js'));
|
||||
setUnderTest();
|
||||
|
||||
const playwrightEnvironment = new Environment('Playwright');
|
||||
playwrightEnvironment.beforeAll(async state => {
|
||||
state.playwright = playwright;
|
||||
});
|
||||
playwrightEnvironment.afterAll(async state => {
|
||||
delete state.playwright;
|
||||
});
|
||||
|
||||
testRunner.collector().useEnvironment(playwrightEnvironment);
|
||||
testRunner.collector().useEnvironment(new PlaywrightEnvironment(playwright));
|
||||
for (const e of config.globalEnvironments || [])
|
||||
testRunner.collector().useEnvironment(e);
|
||||
|
||||
|
|
@ -98,30 +87,7 @@ function collect(browserNames) {
|
|||
|
||||
for (const browserName of browserNames) {
|
||||
const browserType = playwright[browserName];
|
||||
|
||||
const browserTypeEnvironment = new Environment('BrowserType');
|
||||
browserTypeEnvironment.beforeAll(async state => {
|
||||
// Channel substitute
|
||||
let overridenBrowserType = browserType;
|
||||
if (process.env.PWCHANNEL) {
|
||||
const dispatcherConnection = new DispatcherConnection();
|
||||
const connection = new Connection();
|
||||
dispatcherConnection.onmessage = async message => {
|
||||
setImmediate(() => connection.dispatch(message));
|
||||
};
|
||||
connection.onmessage = async message => {
|
||||
const result = await dispatcherConnection.dispatch(message);
|
||||
await new Promise(f => setImmediate(f));
|
||||
return result;
|
||||
};
|
||||
new BrowserTypeDispatcher(dispatcherConnection.rootScope(), browserType);
|
||||
overridenBrowserType = await connection.waitForObjectWithKnownName(browserType.name());
|
||||
}
|
||||
state.browserType = overridenBrowserType;
|
||||
});
|
||||
browserTypeEnvironment.afterAll(async state => {
|
||||
delete state.browserType;
|
||||
});
|
||||
const browserTypeEnvironment = new BrowserTypeEnvironment(browserType);
|
||||
|
||||
// TODO: maybe launch options per browser?
|
||||
const launchOptions = {
|
||||
|
|
@ -141,33 +107,8 @@ function collect(browserNames) {
|
|||
throw new Error(`Browser is not downloaded. Run 'npm install' and try to re-run tests`);
|
||||
}
|
||||
|
||||
const browserEnvironment = new Environment(browserName);
|
||||
browserEnvironment.beforeAll(async state => {
|
||||
state._logger = utils.createTestLogger(config.dumpLogOnFailure);
|
||||
state.browser = await state.browserType.launch({...launchOptions, logger: state._logger});
|
||||
});
|
||||
browserEnvironment.afterAll(async state => {
|
||||
await state.browser.close();
|
||||
delete state.browser;
|
||||
delete state._logger;
|
||||
});
|
||||
browserEnvironment.beforeEach(async(state, testRun) => {
|
||||
state._logger.setTestRun(testRun);
|
||||
});
|
||||
browserEnvironment.afterEach(async (state, testRun) => {
|
||||
state._logger.setTestRun(null);
|
||||
});
|
||||
|
||||
const pageEnvironment = new Environment('Page');
|
||||
pageEnvironment.beforeEach(async state => {
|
||||
state.context = await state.browser.newContext();
|
||||
state.page = await state.context.newPage();
|
||||
});
|
||||
pageEnvironment.afterEach(async state => {
|
||||
await state.context.close();
|
||||
state.context = null;
|
||||
state.page = null;
|
||||
});
|
||||
const browserEnvironment = new BrowserEnvironment(launchOptions, config.dumpLogOnFailure);
|
||||
const pageEnvironment = new PageEnvironment();
|
||||
|
||||
const suiteName = { 'chromium': 'Chromium', 'firefox': 'Firefox', 'webkit': 'WebKit' }[browserName];
|
||||
describe(suiteName, () => {
|
||||
|
|
|
|||
|
|
@ -22,60 +22,6 @@ const TestExpectation = {
|
|||
Fail: 'fail',
|
||||
};
|
||||
|
||||
function createHook(callback, name) {
|
||||
const location = Location.getCallerLocation();
|
||||
return { name, body: callback, location };
|
||||
}
|
||||
|
||||
class Environment {
|
||||
constructor(name) {
|
||||
this._name = name;
|
||||
this._hooks = [];
|
||||
}
|
||||
|
||||
name() {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
beforeEach(callback) {
|
||||
this._hooks.push(createHook(callback, 'beforeEach'));
|
||||
return this;
|
||||
}
|
||||
|
||||
afterEach(callback) {
|
||||
this._hooks.push(createHook(callback, 'afterEach'));
|
||||
return this;
|
||||
}
|
||||
|
||||
beforeAll(callback) {
|
||||
this._hooks.push(createHook(callback, 'beforeAll'));
|
||||
return this;
|
||||
}
|
||||
|
||||
afterAll(callback) {
|
||||
this._hooks.push(createHook(callback, 'afterAll'));
|
||||
return this;
|
||||
}
|
||||
|
||||
globalSetup(callback) {
|
||||
this._hooks.push(createHook(callback, 'globalSetup'));
|
||||
return this;
|
||||
}
|
||||
|
||||
globalTeardown(callback) {
|
||||
this._hooks.push(createHook(callback, 'globalTeardown'));
|
||||
return this;
|
||||
}
|
||||
|
||||
hooks(name) {
|
||||
return this._hooks.filter(hook => !name || hook.name === name);
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return !this._hooks.length;
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
constructor(suite, name, callback, location) {
|
||||
this._suite = suite;
|
||||
|
|
@ -86,8 +32,7 @@ class Test {
|
|||
this._body = callback;
|
||||
this._location = location;
|
||||
this._timeout = 100000000;
|
||||
this._defaultEnvironment = new Environment(this._fullName);
|
||||
this._environments = [this._defaultEnvironment];
|
||||
this._environments = [];
|
||||
this.Expectations = { ...TestExpectation };
|
||||
}
|
||||
|
||||
|
|
@ -138,10 +83,6 @@ class Test {
|
|||
return this;
|
||||
}
|
||||
|
||||
environment() {
|
||||
return this._defaultEnvironment;
|
||||
}
|
||||
|
||||
addEnvironment(environment) {
|
||||
this._environments.push(environment);
|
||||
return this;
|
||||
|
|
@ -164,49 +105,50 @@ class Suite {
|
|||
this._location = location;
|
||||
this._skipped = false;
|
||||
this._expectation = TestExpectation.Ok;
|
||||
this._defaultEnvironment = new Environment(this._fullName);
|
||||
|
||||
this._defaultEnvironment = {
|
||||
name() { return this._fullName; },
|
||||
};
|
||||
|
||||
this._environments = [this._defaultEnvironment];
|
||||
this.Expectations = { ...TestExpectation };
|
||||
}
|
||||
|
||||
parentSuite() {
|
||||
return this._parentSuite;
|
||||
_addHook(name, callback) {
|
||||
if (this._defaultEnvironment[name])
|
||||
throw new Error(`ERROR: cannot re-assign hook "${name}" for suite "${this._fullName}"`);
|
||||
this._defaultEnvironment[name] = callback;
|
||||
}
|
||||
|
||||
name() {
|
||||
return this._name;
|
||||
}
|
||||
beforeEach(callback) { this._addHook('beforeEach', callback); }
|
||||
afterEach(callback) { this._addHook('afterEach', callback); }
|
||||
beforeAll(callback) { this._addHook('beforeAll', callback); }
|
||||
afterAll(callback) { this._addHook('afterAll', callback); }
|
||||
globalSetup(callback) { this._addHook('globalSetup', callback); }
|
||||
globalTeardown(callback) { this._addHook('globalTeardown', callback); }
|
||||
|
||||
fullName() {
|
||||
return this._fullName;
|
||||
}
|
||||
parentSuite() { return this._parentSuite; }
|
||||
|
||||
skipped() {
|
||||
return this._skipped;
|
||||
}
|
||||
name() { return this._name; }
|
||||
|
||||
fullName() { return this._fullName; }
|
||||
|
||||
skipped() { return this._skipped; }
|
||||
|
||||
setSkipped(skipped) {
|
||||
this._skipped = skipped;
|
||||
return this;
|
||||
}
|
||||
|
||||
location() {
|
||||
return this._location;
|
||||
}
|
||||
location() { return this._location; }
|
||||
|
||||
expectation() {
|
||||
return this._expectation;
|
||||
}
|
||||
expectation() { return this._expectation; }
|
||||
|
||||
setExpectation(expectation) {
|
||||
this._expectation = expectation;
|
||||
return this;
|
||||
}
|
||||
|
||||
environment() {
|
||||
return this._defaultEnvironment;
|
||||
}
|
||||
|
||||
addEnvironment(environment) {
|
||||
this._environments.push(environment);
|
||||
return this;
|
||||
|
|
@ -221,4 +163,4 @@ class Suite {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = { TestExpectation, Environment, Test, Suite };
|
||||
module.exports = { TestExpectation, Test, Suite };
|
||||
|
|
|
|||
|
|
@ -195,12 +195,12 @@ class TestCollector {
|
|||
callback(test, ...args);
|
||||
this._tests.push(test);
|
||||
});
|
||||
this._api.beforeAll = callback => this._currentSuite.environment().beforeAll(callback);
|
||||
this._api.beforeEach = callback => this._currentSuite.environment().beforeEach(callback);
|
||||
this._api.afterAll = callback => this._currentSuite.environment().afterAll(callback);
|
||||
this._api.afterEach = callback => this._currentSuite.environment().afterEach(callback);
|
||||
this._api.globalSetup = callback => this._currentSuite.environment().globalSetup(callback);
|
||||
this._api.globalTeardown = callback => this._currentSuite.environment().globalTeardown(callback);
|
||||
this._api.beforeAll = callback => this._currentSuite.beforeAll(callback);
|
||||
this._api.beforeEach = callback => this._currentSuite.beforeEach(callback);
|
||||
this._api.afterAll = callback => this._currentSuite.afterAll(callback);
|
||||
this._api.afterEach = callback => this._currentSuite.afterEach(callback);
|
||||
this._api.globalSetup = callback => this._currentSuite.globalSetup(callback);
|
||||
this._api.globalTeardown = callback => this._currentSuite.globalTeardown(callback);
|
||||
}
|
||||
|
||||
useEnvironment(environment) {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ const TestResult = {
|
|||
Crashed: 'crashed', // If testrunner crashed due to this test
|
||||
};
|
||||
|
||||
function isEmptyEnvironment(env) {
|
||||
return !env.afterEach && !env.afterAll && !env.beforeEach && !env.beforeAll &&
|
||||
!env.globalSetup && !env.globalTeardown;
|
||||
}
|
||||
|
||||
class TestRun {
|
||||
constructor(test) {
|
||||
this._test = test;
|
||||
|
|
@ -56,9 +61,9 @@ class TestRun {
|
|||
this._workerId = null;
|
||||
this._output = [];
|
||||
|
||||
this._environments = test._environments.filter(env => !env.isEmpty()).reverse();
|
||||
this._environments = test._environments.filter(env => !isEmptyEnvironment(env)).reverse();
|
||||
for (let suite = test.suite(); suite; suite = suite.parentSuite())
|
||||
this._environments.push(...suite._environments.filter(env => !env.isEmpty()).reverse());
|
||||
this._environments.push(...suite._environments.filter(env => !isEmptyEnvironment(env)).reverse());
|
||||
this._environments.reverse();
|
||||
}
|
||||
|
||||
|
|
@ -198,9 +203,9 @@ class TestWorker {
|
|||
if (this._markTerminated(testRun))
|
||||
return;
|
||||
const environment = this._environmentStack.pop();
|
||||
if (!await this._hookRunner.runAfterAll(environment, this, testRun, [this._state]))
|
||||
if (!await this._hookRunner.runHook(environment, 'afterAll', [this._state], this, testRun))
|
||||
return;
|
||||
if (!await this._hookRunner.ensureGlobalTeardown(environment))
|
||||
if (!await this._hookRunner.maybeRunGlobalTeardown(environment))
|
||||
return;
|
||||
}
|
||||
while (this._environmentStack.length < environmentStack.length) {
|
||||
|
|
@ -208,9 +213,9 @@ class TestWorker {
|
|||
return;
|
||||
const environment = environmentStack[this._environmentStack.length];
|
||||
this._environmentStack.push(environment);
|
||||
if (!await this._hookRunner.ensureGlobalSetup(environment))
|
||||
if (!await this._hookRunner.maybeRunGlobalSetup(environment))
|
||||
return;
|
||||
if (!await this._hookRunner.runBeforeAll(environment, this, testRun, [this._state]))
|
||||
if (!await this._hookRunner.runHook(environment, 'beforeAll', [this._state], this, testRun))
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +227,7 @@ class TestWorker {
|
|||
|
||||
await this._willStartTestRun(testRun);
|
||||
for (const environment of this._environmentStack) {
|
||||
await this._hookRunner.runBeforeEach(environment, this, testRun, [this._state, testRun]);
|
||||
await this._hookRunner.runHook(environment, 'beforeEach', [this._state, testRun], this, testRun);
|
||||
}
|
||||
|
||||
if (!testRun._error && !this._markTerminated(testRun)) {
|
||||
|
|
@ -245,7 +250,7 @@ class TestWorker {
|
|||
}
|
||||
|
||||
for (const environment of this._environmentStack.slice().reverse())
|
||||
await this._hookRunner.runAfterEach(environment, this, testRun, [this._state, testRun]);
|
||||
await this._hookRunner.runHook(environment, 'afterEach', [this._state, testRun], this, testRun);
|
||||
await this._didFinishTestRun(testRun);
|
||||
}
|
||||
|
||||
|
|
@ -274,8 +279,8 @@ class TestWorker {
|
|||
async shutdown() {
|
||||
while (this._environmentStack.length > 0) {
|
||||
const environment = this._environmentStack.pop();
|
||||
await this._hookRunner.runAfterAll(environment, this, null, [this._state]);
|
||||
await this._hookRunner.ensureGlobalTeardown(environment);
|
||||
await this._hookRunner.runHook(environment, 'afterAll', [this._state], this, null);
|
||||
await this._hookRunner.maybeRunGlobalTeardown(environment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -322,7 +327,7 @@ class HookRunner {
|
|||
}
|
||||
}
|
||||
|
||||
async _runHook(worker, testRun, hook, fullName, hookArgs = []) {
|
||||
async _runHookInternal(worker, testRun, hook, fullName, hookArgs = []) {
|
||||
await this._willStartHook(worker, testRun, hook, fullName);
|
||||
const timeout = this._testRunner._hookTimeout;
|
||||
const { promise, terminate } = runUserCallback(hook.body, timeout, hookArgs);
|
||||
|
|
@ -337,7 +342,7 @@ class HookRunner {
|
|||
}
|
||||
let message;
|
||||
if (error === TimeoutError) {
|
||||
message = `${hook.location.toDetailedString()} - Timeout Exceeded ${timeout}ms while running "${hook.name}" in "${fullName}"`;
|
||||
message = `Timeout Exceeded ${timeout}ms while running "${hook.name}" in "${fullName}"`;
|
||||
error = null;
|
||||
} else if (error === TerminatedError) {
|
||||
// Do not report termination details - it's just noise.
|
||||
|
|
@ -346,7 +351,7 @@ class HookRunner {
|
|||
} else {
|
||||
if (error.stack)
|
||||
await this._testRunner._sourceMapSupport.rewriteStackTraceWithSourceMaps(error);
|
||||
message = `${hook.location.toDetailedString()} - FAILED while running "${hook.name}" in suite "${fullName}": `;
|
||||
message = `FAILED while running "${hook.name}" in suite "${fullName}": `;
|
||||
}
|
||||
await this._didFailHook(worker, testRun, hook, fullName, message, error);
|
||||
if (testRun)
|
||||
|
|
@ -358,50 +363,18 @@ class HookRunner {
|
|||
return true;
|
||||
}
|
||||
|
||||
async runAfterAll(environment, worker, testRun, hookArgs) {
|
||||
for (const hook of environment.hooks('afterAll')) {
|
||||
if (!await this._runHook(worker, testRun, hook, environment.name(), hookArgs))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
async runHook(environment, hookName, hookArgs, worker = null, testRun = null) {
|
||||
const hookBody = environment[hookName];
|
||||
if (!hookBody)
|
||||
return true;
|
||||
const envName = environment.name ? environment.name() : environment.constructor.name;
|
||||
return await this._runHookInternal(worker, testRun, {name: hookName, body: hookBody.bind(environment)}, envName, hookArgs);
|
||||
}
|
||||
|
||||
async runBeforeAll(environment, worker, testRun, hookArgs) {
|
||||
for (const hook of environment.hooks('beforeAll')) {
|
||||
if (!await this._runHook(worker, testRun, hook, environment.name(), hookArgs))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async runAfterEach(environment, worker, testRun, hookArgs) {
|
||||
for (const hook of environment.hooks('afterEach')) {
|
||||
if (!await this._runHook(worker, testRun, hook, environment.name(), hookArgs))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async runBeforeEach(environment, worker, testRun, hookArgs) {
|
||||
for (const hook of environment.hooks('beforeEach')) {
|
||||
if (!await this._runHook(worker, testRun, hook, environment.name(), hookArgs))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async ensureGlobalSetup(environment) {
|
||||
async maybeRunGlobalSetup(environment) {
|
||||
const globalState = this._environmentToGlobalState.get(environment);
|
||||
if (!globalState.globalSetupPromise) {
|
||||
globalState.globalSetupPromise = (async () => {
|
||||
let result = true;
|
||||
for (const hook of environment.hooks('globalSetup')) {
|
||||
if (!await this._runHook(null /* worker */, null /* testRun */, hook, environment.name(), []))
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
}
|
||||
if (!globalState.globalSetupPromise)
|
||||
globalState.globalSetupPromise = this.runHook(environment, 'globalSetup', []);
|
||||
if (!await globalState.globalSetupPromise) {
|
||||
await this._testRunner._terminate(TestResult.Crashed, 'Global setup failed!', false, null);
|
||||
return false;
|
||||
|
|
@ -409,19 +382,11 @@ class HookRunner {
|
|||
return true;
|
||||
}
|
||||
|
||||
async ensureGlobalTeardown(environment) {
|
||||
async maybeRunGlobalTeardown(environment) {
|
||||
const globalState = this._environmentToGlobalState.get(environment);
|
||||
if (!globalState.globalTeardownPromise) {
|
||||
if (!globalState.pendingTestRuns.size || (this._testRunner._terminating && globalState.globalSetupPromise)) {
|
||||
globalState.globalTeardownPromise = (async () => {
|
||||
let result = true;
|
||||
for (const hook of environment.hooks('globalTeardown')) {
|
||||
if (!await this._runHook(null /* worker */, null /* testRun */, hook, environment.name(), []))
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
}
|
||||
if (!globalState.pendingTestRuns.size || (this._testRunner._terminating && globalState.globalSetupPromise))
|
||||
globalState.globalTeardownPromise = this.runHook(environment, 'globalTeardown', []);
|
||||
}
|
||||
if (!globalState.globalTeardownPromise)
|
||||
return true;
|
||||
|
|
@ -433,18 +398,18 @@ class HookRunner {
|
|||
}
|
||||
|
||||
async _willStartHook(worker, testRun, hook, fullName) {
|
||||
debug('testrunner:hook')(`${workerName(worker)} "${fullName}.${hook.name}" started for "${testRun ? testRun.test().fullName() : ''}" (${hook.location})`);
|
||||
debug('testrunner:hook')(`${workerName(worker)} "${fullName}.${hook.name}" started for "${testRun ? testRun.test().fullName() : ''}"`);
|
||||
}
|
||||
|
||||
async _didFailHook(worker, testRun, hook, fullName, message, error) {
|
||||
debug('testrunner:hook')(`${workerName(worker)} "${fullName}.${hook.name}" FAILED for "${testRun ? testRun.test().fullName() : ''}" (${hook.location})`);
|
||||
debug('testrunner:hook')(`${workerName(worker)} "${fullName}.${hook.name}" FAILED for "${testRun ? testRun.test().fullName() : ''}"`);
|
||||
if (message)
|
||||
this._testRunner._result.addError(message, error, worker);
|
||||
this._testRunner._result.setResult(TestResult.Crashed, message);
|
||||
}
|
||||
|
||||
async _didCompleteHook(worker, testRun, hook, fullName) {
|
||||
debug('testrunner:hook')(`${workerName(worker)} "${fullName}.${hook.name}" OK for "${testRun ? testRun.test().fullName() : ''}" (${hook.location})`);
|
||||
debug('testrunner:hook')(`${workerName(worker)} "${fullName}.${hook.name}" OK for "${testRun ? testRun.test().fullName() : ''}"`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -281,34 +281,27 @@ module.exports.addTests = function({describe, fdescribe, xdescribe, it, xit, fit
|
|||
it('should run all hooks in proper order', async() => {
|
||||
const log = [];
|
||||
const t = new Runner();
|
||||
const e = new Environment('env');
|
||||
e.beforeAll(() => log.push('env:beforeAll'));
|
||||
e.afterAll(() => log.push('env:afterAll'));
|
||||
e.beforeEach(() => log.push('env:beforeEach'));
|
||||
e.afterEach(() => log.push('env:afterEach'));
|
||||
const e2 = new Environment('env2');
|
||||
e2.beforeAll(() => log.push('env2:beforeAll'));
|
||||
e2.afterAll(() => log.push('env2:afterAll'));
|
||||
const e = {
|
||||
name() { return 'env'; },
|
||||
beforeAll() { log.push('env:beforeAll'); },
|
||||
afterAll() { log.push('env:afterAll'); },
|
||||
beforeEach() { log.push('env:beforeEach'); },
|
||||
afterEach() { log.push('env:afterEach'); },
|
||||
};
|
||||
const e2 = {
|
||||
name() { return 'env2'; },
|
||||
beforeAll() { log.push('env2:beforeAll'); },
|
||||
afterAll() { log.push('env2:afterAll'); },
|
||||
};
|
||||
t.beforeAll(() => log.push('root:beforeAll'));
|
||||
t.beforeEach(() => log.push('root:beforeEach1'));
|
||||
t.beforeEach(() => log.push('root:beforeEach2'));
|
||||
t.beforeEach(() => log.push('root:beforeEach'));
|
||||
t.it('uno', () => log.push('test #1'));
|
||||
t.describe('suite1', () => {
|
||||
t.beforeAll(() => log.push('suite:beforeAll1'));
|
||||
t.beforeAll(() => log.push('suite:beforeAll2'));
|
||||
t.beforeAll(() => log.push('suite:beforeAll'));
|
||||
t.beforeEach(() => log.push('suite:beforeEach'));
|
||||
t.it('dos', () => log.push('test #2'));
|
||||
t.tests()[t.tests().length - 1].environment().beforeEach(() => log.push('test:before1'));
|
||||
t.tests()[t.tests().length - 1].environment().beforeEach(() => log.push('test:before2'));
|
||||
t.tests()[t.tests().length - 1].environment().afterEach(() => log.push('test:after1'));
|
||||
t.tests()[t.tests().length - 1].environment().afterEach(() => log.push('test:after2'));
|
||||
t.it('tres', () => log.push('test #3'));
|
||||
t.tests()[t.tests().length - 1].environment().beforeEach(() => log.push('test:before1'));
|
||||
t.tests()[t.tests().length - 1].environment().beforeEach(() => log.push('test:before2'));
|
||||
t.tests()[t.tests().length - 1].environment().afterEach(() => log.push('test:after1'));
|
||||
t.tests()[t.tests().length - 1].environment().afterEach(() => log.push('test:after2'));
|
||||
t.afterEach(() => log.push('suite:afterEach1'));
|
||||
t.afterEach(() => log.push('suite:afterEach2'));
|
||||
t.afterEach(() => log.push('suite:afterEach'));
|
||||
t.afterAll(() => log.push('suite:afterAll'));
|
||||
});
|
||||
t.it('cuatro', () => log.push('test #4'));
|
||||
|
|
@ -326,41 +319,26 @@ module.exports.addTests = function({describe, fdescribe, xdescribe, it, xit, fit
|
|||
t.suites()[t.suites().length - 1].addEnvironment(e);
|
||||
t.suites()[t.suites().length - 1].addEnvironment(e2);
|
||||
t.afterEach(() => log.push('root:afterEach'));
|
||||
t.afterAll(() => log.push('root:afterAll1'));
|
||||
t.afterAll(() => log.push('root:afterAll2'));
|
||||
t.afterAll(() => log.push('root:afterAll'));
|
||||
await t.run();
|
||||
expect(log).toEqual([
|
||||
'root:beforeAll',
|
||||
'root:beforeEach1',
|
||||
'root:beforeEach2',
|
||||
'root:beforeEach',
|
||||
'test #1',
|
||||
'root:afterEach',
|
||||
|
||||
'suite:beforeAll1',
|
||||
'suite:beforeAll2',
|
||||
'suite:beforeAll',
|
||||
|
||||
'root:beforeEach1',
|
||||
'root:beforeEach2',
|
||||
'root:beforeEach',
|
||||
'suite:beforeEach',
|
||||
'test:before1',
|
||||
'test:before2',
|
||||
'test #2',
|
||||
'test:after1',
|
||||
'test:after2',
|
||||
'suite:afterEach1',
|
||||
'suite:afterEach2',
|
||||
'suite:afterEach',
|
||||
'root:afterEach',
|
||||
|
||||
'root:beforeEach1',
|
||||
'root:beforeEach2',
|
||||
'root:beforeEach',
|
||||
'suite:beforeEach',
|
||||
'test:before1',
|
||||
'test:before2',
|
||||
'test #3',
|
||||
'test:after1',
|
||||
'test:after2',
|
||||
'suite:afterEach1',
|
||||
'suite:afterEach2',
|
||||
'suite:afterEach',
|
||||
'root:afterEach',
|
||||
|
||||
'suite:afterAll',
|
||||
|
|
@ -368,16 +346,14 @@ module.exports.addTests = function({describe, fdescribe, xdescribe, it, xit, fit
|
|||
'env:beforeAll',
|
||||
'env2:beforeAll',
|
||||
|
||||
'root:beforeEach1',
|
||||
'root:beforeEach2',
|
||||
'root:beforeEach',
|
||||
'env:beforeEach',
|
||||
'test #4',
|
||||
'env:afterEach',
|
||||
'root:afterEach',
|
||||
|
||||
'suite2:beforeAll',
|
||||
'root:beforeEach1',
|
||||
'root:beforeEach2',
|
||||
'root:beforeEach',
|
||||
'env:beforeEach',
|
||||
'test #5',
|
||||
'env:afterEach',
|
||||
|
|
@ -387,23 +363,26 @@ module.exports.addTests = function({describe, fdescribe, xdescribe, it, xit, fit
|
|||
'env2:afterAll',
|
||||
'env:afterAll',
|
||||
|
||||
'root:afterAll1',
|
||||
'root:afterAll2',
|
||||
'root:afterAll',
|
||||
]);
|
||||
});
|
||||
it('should remove environment', async() => {
|
||||
const log = [];
|
||||
const t = new Runner();
|
||||
const e = new Environment('env');
|
||||
e.beforeAll(() => log.push('env:beforeAll'));
|
||||
e.afterAll(() => log.push('env:afterAll'));
|
||||
e.beforeEach(() => log.push('env:beforeEach'));
|
||||
e.afterEach(() => log.push('env:afterEach'));
|
||||
const e2 = new Environment('env2');
|
||||
e2.beforeAll(() => log.push('env2:beforeAll'));
|
||||
e2.afterAll(() => log.push('env2:afterAll'));
|
||||
e2.beforeEach(() => log.push('env2:beforeEach'));
|
||||
e2.afterEach(() => log.push('env2:afterEach'));
|
||||
const e = {
|
||||
name() { return 'env'; },
|
||||
beforeAll() { log.push('env:beforeAll'); },
|
||||
afterAll() { log.push('env:afterAll'); },
|
||||
beforeEach() { log.push('env:beforeEach'); },
|
||||
afterEach() { log.push('env:afterEach'); },
|
||||
};
|
||||
const e2 = {
|
||||
name() { return 'env2'; },
|
||||
beforeAll() { log.push('env2:beforeAll'); },
|
||||
afterAll() { log.push('env2:afterAll'); },
|
||||
beforeEach() { log.push('env2:beforeEach'); },
|
||||
afterEach() { log.push('env2:afterEach'); },
|
||||
};
|
||||
t.it('uno', () => log.push('test #1'));
|
||||
t.tests()[0].addEnvironment(e).addEnvironment(e2).removeEnvironment(e);
|
||||
await t.run();
|
||||
|
|
|
|||
Loading…
Reference in a new issue