Skip to content

Commit d38baae

Browse files
authored
feat(testrunner): nested environments (#1635)
1 parent f3f10ae commit d38baae

File tree

2 files changed

+53
-19
lines changed

2 files changed

+53
-19
lines changed

utils/testrunner/TestRunner.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,25 +156,42 @@ class Test {
156156
}
157157

158158
environment(environment) {
159+
const parents = new Set();
160+
for (let parent = environment; !(parent instanceof Suite); parent = parent.parentEnvironment())
161+
parents.add(parent);
162+
for (const env of this._environments) {
163+
for (let parent = env; !(parent instanceof Suite); parent = parent.parentEnvironment()) {
164+
if (parents.has(parent))
165+
throw new Error(`Cannot use environments "${environment.name()}" and "${env.name()}" that share a parent environment "${parent.fullName()}" in test "${this.fullName()}"`);
166+
}
167+
}
168+
const environmentParentSuite = environment.parentSuite();
159169
for (let suite = this.suite(); suite; suite = suite.parentSuite()) {
160-
if (suite === environment.parentSuite()) {
170+
if (suite === environmentParentSuite) {
161171
this._environments.push(environment);
162-
return;
172+
return this;
163173
}
164174
}
165175
throw new Error(`Cannot use environment "${environment.name()}" from suite "${environment.parentSuite().fullName()}" in unrelated test "${this.fullName()}"`);
166176
}
167177
}
168178

169179
class Environment {
170-
constructor(parentSuite, name, location) {
171-
this._parentSuite = parentSuite;
180+
constructor(parentEnvironment, name, location) {
181+
this._parentEnvironment = parentEnvironment;
182+
this._parentSuite = parentEnvironment;
183+
if (parentEnvironment && !(parentEnvironment instanceof Suite))
184+
this._parentSuite = parentEnvironment.parentSuite();
172185
this._name = name;
173-
this._fullName = (parentSuite ? parentSuite.fullName() + ' ' + name : name).trim();
186+
this._fullName = (parentEnvironment ? parentEnvironment.fullName() + ' ' + name : name).trim();
174187
this._location = location;
175188
this._hooks = [];
176189
}
177190

191+
parentEnvironment() {
192+
return this._parentEnvironment;
193+
}
194+
178195
parentSuite() {
179196
return this._parentSuite;
180197
}
@@ -397,8 +414,12 @@ class TestWorker {
397414
for (let suite = test.suite(); suite; suite = suite.parentSuite())
398415
environmentStack.push(suite);
399416
environmentStack.reverse();
400-
for (const environment of test._environments)
401-
environmentStack.splice(environmentStack.indexOf(environment.parentSuite()) + 1, 0, environment);
417+
for (const environment of test._environments) {
418+
const insert = [];
419+
for (let parent = environment; !(parent instanceof Suite); parent = parent.parentEnvironment())
420+
insert.push(parent);
421+
environmentStack.splice(environmentStack.indexOf(environment.parentSuite()) + 1, 0, ...insert.reverse());
422+
}
402423

403424
let common = 0;
404425
while (common < environmentStack.length && this._environmentStack[common] === environmentStack[common])
@@ -677,13 +698,11 @@ class TestRunner extends EventEmitter {
677698
this.describe = this._suiteBuilder([]);
678699
this.it = this._testBuilder([]);
679700
this.environment = (name, callback) => {
680-
if (!(this._currentEnvironment instanceof Suite))
681-
throw new Error(`Cannot define an environment inside an environment`);
682701
const location = Location.getCallerLocation(__filename);
683702
const environment = new Environment(this._currentEnvironment, name, location);
684703
this._currentEnvironment = environment;
685704
callback();
686-
this._currentEnvironment = environment.parentSuite();
705+
this._currentEnvironment = environment.parentEnvironment();
687706
return environment;
688707
};
689708
this.Expectations = { ...TestExpectation };

utils/testrunner/test/testrunner.spec.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,16 @@ module.exports.addTests = function({testRunner, expect}) {
244244
it('should run all hooks in proper order', async() => {
245245
const log = [];
246246
const t = newTestRunner();
247-
const e = t.environment('env', () => {
247+
let e2;
248+
t.environment('env', () => {
248249
t.beforeAll(() => log.push('env:beforeAll'));
249250
t.afterAll(() => log.push('env:afterAll'));
250251
t.beforeEach(() => log.push('env:beforeEach'));
251252
t.afterEach(() => log.push('env:afterEach'));
253+
e2 = t.environment('env2', () => {
254+
t.beforeAll(() => log.push('env2:beforeAll'));
255+
t.afterAll(() => log.push('env2:afterAll'));
256+
});
252257
});
253258
t.beforeAll(() => log.push('root:beforeAll'));
254259
t.beforeEach(() => log.push('root:beforeEach1'));
@@ -270,13 +275,13 @@ module.exports.addTests = function({testRunner, expect}) {
270275
t.afterEach(() => log.push('suite:afterEach2'));
271276
t.afterAll(() => log.push('suite:afterAll'));
272277
});
273-
t.it('cuatro', () => log.push('test #4')).environment(e);
278+
t.it('cuatro', () => log.push('test #4')).environment(e2);
274279
t.describe('no hooks suite', () => {
275280
t.describe('suite2', () => {
276281
t.beforeAll(() => log.push('suite2:beforeAll'));
277282
t.afterAll(() => log.push('suite2:afterAll'));
278283
t.describe('no hooks suite 2', () => {
279-
t.it('cinco', () => log.push('test #5')).environment(e);
284+
t.it('cinco', () => log.push('test #5')).environment(e2);
280285
});
281286
});
282287
});
@@ -321,6 +326,7 @@ module.exports.addTests = function({testRunner, expect}) {
321326
'suite:afterAll',
322327

323328
'env:beforeAll',
329+
'env2:beforeAll',
324330

325331
'root:beforeEach1',
326332
'root:beforeEach2',
@@ -338,6 +344,7 @@ module.exports.addTests = function({testRunner, expect}) {
338344
'root:afterEach',
339345
'suite2:afterAll',
340346

347+
'env2:afterAll',
341348
'env:afterAll',
342349

343350
'root:afterAll1',
@@ -347,8 +354,10 @@ module.exports.addTests = function({testRunner, expect}) {
347354
it('environment restrictions', async () => {
348355
const t = newTestRunner();
349356
let env;
357+
let env2;
350358
t.describe('suite1', () => {
351359
env = t.environment('env', () => {
360+
env2 = t.environment('env2', () => {});
352361
try {
353362
t.it('test', () => {});
354363
expect(true).toBe(false);
@@ -361,13 +370,19 @@ module.exports.addTests = function({testRunner, expect}) {
361370
} catch (e) {
362371
expect(e.message).toBe('Cannot define a suite inside an environment');
363372
}
364-
try {
365-
t.environment('env2', () => {});
366-
expect(true).toBe(false);
367-
} catch (e) {
368-
expect(e.message).toBe('Cannot define an environment inside an environment');
369-
}
370373
});
374+
try {
375+
t.it('test', () => {}).environment(env).environment(env2);
376+
expect(true).toBe(false);
377+
} catch (e) {
378+
expect(e.message).toBe('Cannot use environments "env2" and "env" that share a parent environment "suite1 env" in test "suite1 test"');
379+
}
380+
try {
381+
t.it('test', () => {}).environment(env2).environment(env);
382+
expect(true).toBe(false);
383+
} catch (e) {
384+
expect(e.message).toBe('Cannot use environments "env" and "env2" that share a parent environment "suite1 env" in test "suite1 test"');
385+
}
371386
});
372387
try {
373388
t.it('test', () => {}).environment(env);

0 commit comments

Comments
 (0)