Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Effectively allowing beforeEach and afterEach hooks to modify the same data without having to set intermediate variables. The most obvious use case is resetting global (or environment) variables which need to be in specific states for the tests, but should be reverted after the fact.
- Loading branch information
Showing
12 changed files
with
241 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ language: node_js | |
node_js: | ||
- 10 | ||
- 11 | ||
- 12 | ||
- lts/* | ||
|
||
os: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
'use strict'; | ||
|
||
const { nonExecutor, callTrace } = require('./spec_helper'); | ||
|
||
describe('aroundEach', () => { | ||
set('trace', callTrace); | ||
|
||
describe('call types and ordering', { random: false }, () => { | ||
const afterTrace = callTrace(); | ||
|
||
aroundEach('Named with option', {}, async (eg) => { | ||
afterTrace('A'); await eg(); afterTrace('B'); | ||
}); | ||
aroundEach('Named', async (eg) => { | ||
afterTrace('C'); await eg(); afterTrace('D'); | ||
}); | ||
aroundEach(async (eg) => { | ||
afterTrace('E'); await eg(); afterTrace('F'); | ||
}); | ||
|
||
it('all are called in order, once per example', () => expect(afterTrace('x')).to.eq('ACEx')); | ||
it('all are called in order, once per example', () => expect(afterTrace('y')).to.eq('ACExFDBACEy')); | ||
after(() => expect(afterTrace()).to.eq('ACExFDBACEyFDB')) | ||
}); | ||
|
||
describe('calling with no function', () => { | ||
try { | ||
aroundEach('what even is this'); | ||
it(nonExecutor); | ||
}catch (error) { | ||
it('throws', () => expect(error).to.be.an.instanceOf(TypeError)); | ||
} | ||
}); | ||
|
||
describe('calling from within an example', () => { | ||
it('throws', () => expect( | ||
() => aroundEach('called in executor', nonExecutor) | ||
).to.throw(ReferenceError, 'A hook (`aroundEach`) can not be defined inside an example block')); | ||
}); | ||
|
||
context('passed a generator', () => { | ||
set('endState', 'acb'); | ||
|
||
aroundEach(function* () { | ||
trace('a'); | ||
yield; | ||
trace('b'); | ||
|
||
expect(trace()).to.eq(endState); | ||
}); | ||
|
||
it('wraps correctly', () => { | ||
expect(trace('c')).to.eq('ac'); | ||
}); | ||
|
||
describe('at depth', () => { | ||
set('endState', 'adfeb'); | ||
|
||
aroundEach(function* () { | ||
trace('d'); | ||
yield; | ||
trace('e'); | ||
|
||
expect(trace()).to.eq('adfe'); | ||
}); | ||
|
||
it('wraps correctly', () => { | ||
expect(trace('f')).to.eq('adf'); | ||
}); | ||
}); | ||
}); | ||
|
||
context('passed an async generator', () => { | ||
set('endState', 'gih'); | ||
|
||
aroundEach(async function* () { | ||
trace('g'); | ||
yield; | ||
trace('h'); | ||
|
||
expect(trace()).to.eq(endState); | ||
}); | ||
|
||
it('wraps correctly', () => { | ||
expect(trace('i')).to.eq('gi'); | ||
}); | ||
}); | ||
|
||
context('passed an regular method (example as promise)', () => { | ||
set('endState', 'jlk'); | ||
|
||
aroundEach(example => { | ||
trace('j'); | ||
return example().then(() => { | ||
trace('k'); | ||
expect(trace()).to.eq(endState); | ||
}); | ||
}); | ||
|
||
it('wraps correctly', () => { | ||
expect(trace('l')).to.eq('jl'); | ||
}); | ||
}); | ||
|
||
context('passed an async method', () => { | ||
set('endState', 'mon'); | ||
|
||
aroundEach(async example => { | ||
trace('m'); | ||
await example(); | ||
trace('n'); | ||
expect(trace()).to.eq(endState); | ||
}); | ||
|
||
it('wraps correctly', () => { | ||
expect(trace('o')).to.eq('mo'); | ||
}); | ||
}); | ||
|
||
describe('ordering of hooks', () => { | ||
const afterTrace = callTrace(); | ||
|
||
set('endState', 'ptqvrus'); | ||
|
||
before(() => afterTrace('p')); | ||
beforeEach(() => afterTrace('q')); | ||
afterEach(() => afterTrace('r')); | ||
after(() => { | ||
expect(afterTrace('s')).to.eq(endState); | ||
}); | ||
aroundEach(function * () { | ||
afterTrace('t'); | ||
yield; | ||
afterTrace('u'); | ||
}); | ||
|
||
it('enters in order', () => expect(afterTrace('v')).to.eq('ptqv')); | ||
|
||
|
||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
'use strict'; | ||
|
||
const Example = require('./example'); | ||
|
||
/* c8 ignore next 2 */ | ||
const Generator = (function* () { }).constructor; | ||
const AsyncGenerator = (async function* () { }).constructor; | ||
|
||
class ExampleWrapper extends Example { | ||
around(example) { | ||
this.child = example; | ||
return this; | ||
} | ||
|
||
async runGenerator() { | ||
const runner = this.block(); | ||
let partial = runner.next(); | ||
await this.child.run(); | ||
if (!partial.done) { partial = runner.next(); } | ||
} | ||
|
||
async runAsyncGenerator() { | ||
const runner = this.block(); | ||
let partial = await runner.next(); | ||
await this.child.run(); | ||
if (!partial.done) { partial = await runner.next(); } | ||
} | ||
|
||
async run() { | ||
if (this.block instanceof Generator) await this.runGenerator(); | ||
else if (this.block instanceof AsyncGenerator) await this.runAsyncGenerator(); | ||
else await this.block(async () => await this.child.run()); | ||
} | ||
} | ||
|
||
module.exports = ExampleWrapper; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
const filterStack = require('../filter_stack'); | ||
const ExampleWrapper = require('../example_wrapper'); | ||
|
||
module.exports = { | ||
initialise() { | ||
this.aroundEach = []; | ||
}, | ||
instance: { | ||
addAroundEach(exampleWrapper) { | ||
this.aroundEach.push(exampleWrapper); | ||
}, | ||
|
||
wrapAroundEach(hookedExample) { | ||
let wrapped = this.aroundEach.reduceRight((inner,wrapper) => wrapper.around(inner), hookedExample); | ||
return this.parent.wrapAroundEach(wrapped); | ||
}, | ||
|
||
hookedExample(example) { | ||
return { | ||
run: async () => { | ||
const storeFailure = error => example.failure = example.failure || filterStack(error); | ||
|
||
await this.runBeforeEach().catch(storeFailure); | ||
if (!example.failure) { | ||
await example.run().catch(storeFailure); | ||
await this.runAfterEach().catch(storeFailure); | ||
} | ||
} | ||
}; | ||
}, | ||
|
||
runAroundEach: async function(example) { | ||
let wrapped = this.wrapAroundEach(this.hookedExample(example)); | ||
|
||
await wrapped.run().catch(error => example.failure = example.failure || filterStack(error)); | ||
} | ||
}, | ||
global(description, optionOrBlock, block) { | ||
if (this.executing) throw new ReferenceError('A hook (`aroundEach`) can not be defined inside an example block'); | ||
|
||
if (block instanceof Function) { /* noop */ } | ||
else if (optionOrBlock instanceof Function) [optionOrBlock, block] = [{}, optionOrBlock]; | ||
else if ( description instanceof Function) [description, optionOrBlock, block] = ['', {}, description]; | ||
else throw TypeError('`aroundEach` must be provided an executable block'); | ||
|
||
this.currentContext.addAroundEach(new ExampleWrapper(description, 'aroundEach', optionOrBlock, block, this.currentContext)); | ||
|
||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters