test: allow overriding test fixtures, add some test runner tests (#3463)
This commit is contained in:
parent
a64cdcc9a9
commit
ee02702203
|
|
@ -130,7 +130,7 @@ function compare(actual, expectedPath) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pass: false,
|
pass: false,
|
||||||
message: output.join('n'),
|
message: output.join('\n'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
const debug = require('debug');
|
const debug = require('debug');
|
||||||
|
|
||||||
const registrations = new Map();
|
const registrations = new Map();
|
||||||
const filesWithRegistrations = new Set();
|
const registrationsByFile = new Map();
|
||||||
|
|
||||||
class Fixture {
|
class Fixture {
|
||||||
constructor(pool, name, scope, fn) {
|
constructor(pool, name, scope, fn) {
|
||||||
|
|
@ -143,10 +144,11 @@ function innerRegisterFixture(name, scope, fn) {
|
||||||
const stackFrame = new Error().stack.split('\n').slice(1).filter(line => !line.includes(__filename))[0];
|
const stackFrame = new Error().stack.split('\n').slice(1).filter(line => !line.includes(__filename))[0];
|
||||||
const location = stackFrame.replace(/.*at Object.<anonymous> \((.*)\)/, '$1');
|
const location = stackFrame.replace(/.*at Object.<anonymous> \((.*)\)/, '$1');
|
||||||
const file = location.replace(/^(.+):\d+:\d+$/, '$1');
|
const file = location.replace(/^(.+):\d+:\d+$/, '$1');
|
||||||
const registration = { scope, fn, file, location };
|
const registration = { name, scope, fn, file, location };
|
||||||
registrations.set(name, registration);
|
registrations.set(name, registration);
|
||||||
if (scope === 'worker')
|
if (!registrationsByFile.has(file))
|
||||||
filesWithRegistrations.add(file);
|
registrationsByFile.set(file, []);
|
||||||
|
registrationsByFile.get(file).push(registration);
|
||||||
};
|
};
|
||||||
|
|
||||||
function registerFixture(name, fn) {
|
function registerFixture(name, fn) {
|
||||||
|
|
@ -157,4 +159,46 @@ function registerWorkerFixture (name, fn) {
|
||||||
innerRegisterFixture(name, 'worker', fn);
|
innerRegisterFixture(name, 'worker', fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = { FixturePool, registerFixture, registerWorkerFixture, filesWithRegistrations };
|
function collectRequires(file, result) {
|
||||||
|
if (result.has(file))
|
||||||
|
return;
|
||||||
|
result.add(file);
|
||||||
|
const cache = require.cache[file];
|
||||||
|
const deps = cache.children.map(m => m.id).slice().reverse();
|
||||||
|
for (const dep of deps)
|
||||||
|
collectRequires(dep, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookupRegistrations(file, scope) {
|
||||||
|
const deps = new Set();
|
||||||
|
collectRequires(file, deps);
|
||||||
|
const allDeps = [...deps].reverse();
|
||||||
|
let result = [];
|
||||||
|
for (const dep of allDeps) {
|
||||||
|
const registrationList = registrationsByFile.get(dep);
|
||||||
|
if (!registrationList)
|
||||||
|
continue;
|
||||||
|
result = result.concat(registrationList.filter(r => r.scope === scope));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rerunRegistrations(file, scope) {
|
||||||
|
// When we are running several tests in the same worker, we should re-run registrations before
|
||||||
|
// each file. That way we erase potential fixture overrides from the previous test runs.
|
||||||
|
for (const registration of lookupRegistrations(file, scope))
|
||||||
|
registrations.set(registration.name, registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeWorkerHash(file) {
|
||||||
|
// At this point, registrationsByFile contains all the files with worker fixture registrations.
|
||||||
|
// For every test, build the require closure and map each file to fixtures declared in it.
|
||||||
|
// This collection of fixtures is the fingerprint of the worker setup, a "worker hash".
|
||||||
|
// Tests with the matching "worker hash" will reuse the same worker.
|
||||||
|
const hash = crypto.createHash('sha1');
|
||||||
|
for (const registration of lookupRegistrations(file, 'worker'))
|
||||||
|
hash.update(registration.location);
|
||||||
|
return hash.digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { FixturePool, registerFixture, registerWorkerFixture, computeWorkerHash, rerunRegistrations };
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { FixturePool, registerFixture, registerWorkerFixture } = require('./fixtures');
|
const { FixturePool, registerFixture, registerWorkerFixture, rerunRegistrations } = require('./fixtures');
|
||||||
const { Test, Suite } = require('mocha');
|
const { Test, Suite } = require('mocha');
|
||||||
const { installTransform } = require('./transform');
|
const { installTransform } = require('./transform');
|
||||||
const commonSuite = require('mocha/lib/interfaces/common');
|
const commonSuite = require('mocha/lib/interfaces/common');
|
||||||
|
|
@ -139,6 +139,7 @@ function fixturesUI(trialRun, suite) {
|
||||||
|
|
||||||
suite.on(Suite.constants.EVENT_FILE_POST_REQUIRE, function(context, file, mocha) {
|
suite.on(Suite.constants.EVENT_FILE_POST_REQUIRE, function(context, file, mocha) {
|
||||||
revertBabelRequire();
|
revertBabelRequire();
|
||||||
|
rerunRegistrations(file, 'test');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const child_process = require('child_process');
|
const child_process = require('child_process');
|
||||||
const crypto = require('crypto');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { EventEmitter } = require('events');
|
const { EventEmitter } = require('events');
|
||||||
const Mocha = require('mocha');
|
const Mocha = require('mocha');
|
||||||
const builtinReporters = require('mocha/lib/reporters');
|
const builtinReporters = require('mocha/lib/reporters');
|
||||||
const DotRunner = require('./dotReporter');
|
const DotRunner = require('./dotReporter');
|
||||||
const { filesWithRegistrations } = require('./fixtures');
|
const { computeWorkerHash } = require('./fixtures');
|
||||||
|
|
||||||
const constants = Mocha.Runner.constants;
|
const constants = Mocha.Runner.constants;
|
||||||
// Mocha runner does not remove uncaughtException listeners.
|
// Mocha runner does not remove uncaughtException listeners.
|
||||||
|
|
@ -232,30 +231,4 @@ class Worker extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectRequires(file, allDeps) {
|
|
||||||
if (allDeps.has(file))
|
|
||||||
return;
|
|
||||||
allDeps.add(file);
|
|
||||||
const cache = require.cache[file];
|
|
||||||
const deps = cache.children.map(m => m.id);
|
|
||||||
for (const dep of deps)
|
|
||||||
collectRequires(dep, allDeps);
|
|
||||||
}
|
|
||||||
|
|
||||||
function computeWorkerHash(file) {
|
|
||||||
// At this point, filesWithRegistrations contains all the files with worker fixture registrations.
|
|
||||||
// For every test, build the require closure and map each file to fixtures declared in it.
|
|
||||||
// This collection of fixtures is the fingerprint of the worker setup, a "worker hash".
|
|
||||||
// Tests with the matching "worker hash" will reuse the same worker.
|
|
||||||
const deps = new Set();
|
|
||||||
const hash = crypto.createHash('sha1');
|
|
||||||
collectRequires(file, deps);
|
|
||||||
for (const dep of deps) {
|
|
||||||
if (!filesWithRegistrations.has(dep))
|
|
||||||
continue;
|
|
||||||
hash.update(dep);
|
|
||||||
}
|
|
||||||
return hash.digest('hex');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { Runner };
|
module.exports = { Runner };
|
||||||
|
|
|
||||||
27
test/test-runner-helper.ts
Normal file
27
test/test-runner-helper.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { registerFixture } from './runner/fixtures';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface FixtureState {
|
||||||
|
helperFixture: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerFixture('helperFixture', async ({}, test) => {
|
||||||
|
await test('helperFixture');
|
||||||
|
});
|
||||||
26
test/test-runner-overrides-1.spec.ts
Normal file
26
test/test-runner-overrides-1.spec.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import './test-runner-helper';
|
||||||
|
import { registerFixture } from './runner/fixtures';
|
||||||
|
|
||||||
|
registerFixture('helperFixture', async ({}, test) => {
|
||||||
|
await test('helperFixture - overridden');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should override fixture from requires', async ({helperFixture}) => {
|
||||||
|
expect(helperFixture).toBe('helperFixture - overridden');
|
||||||
|
});
|
||||||
21
test/test-runner-overrides-2.spec.ts
Normal file
21
test/test-runner-overrides-2.spec.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import './test-runner-helper';
|
||||||
|
|
||||||
|
it('override should have no side effects', async ({helperFixture}) => {
|
||||||
|
expect(helperFixture).toBe('helperFixture');
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue