Skip to content

Commit

Permalink
Add after hook
Browse files Browse the repository at this point in the history
  • Loading branch information
HookyQR committed Jul 12, 2019
1 parent d35094a commit 9fcba03
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 26 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
*** 0.0.7
* Add after hooks

*** 0.0.x
* Pre-release changes - organising supporting files
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,21 @@ Add the following to your `.eslintrc.json` file in your spec directory:
"jsspec/jsspec": true
},
```

## Future work:
* after hooks
Block runners:
* beforeEach and afterEach hooks
* sharedExamples
* sharedContexts

Runner:
* improved output for file level failures
* Glob filename matching
* Targeted test running
* Concurrent runners
* File watch running

Associated modules:
* companion expectation framework
* doubles (mocks/spies)
* more formatters
* improved output for file level failures
6 changes: 6 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Run:
Runs:
✔ Context @done(19-06-30 21:10)
✔ Describe @done(19-06-30 21:10)
✔ Before Hooks @done(19-07-12 22:19)
✔ After Hooks @done(19-07-12 22:19)
☐ AfterEach
☐ BeforeEach
☐ SharedContext
☐ SharedExample
✔ It - standard @done(19-06-30 21:10)
☐ It - shorthand
Expect:
Expand Down
8 changes: 7 additions & 1 deletion fail_spec/failure.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('timeout', {timeout: 200}, () => {
});
});

describe('hooks', () => {
describe('before hook', () => {
before(() => expect(1).to.eql(2));

it('fails on the initial call', () => {});
Expand All @@ -18,6 +18,12 @@ describe('hooks', () => {
});
});

describe('after hook', () => {
after('fails with it\'s own report', () => expect(1).to.eql(2));

it('block for after test', () => {});
});

describe('Error preparation', () => {
set('code', 'const a=1; a=2;');

Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jsspec/jsspec",
"version": "0.0.6",
"version": "0.0.7",
"description": "JSSpec - contextualised test runner for javascript",
"main": "index.js",
"scripts": {
Expand All @@ -19,7 +19,7 @@
],
"dependencies": {
"@jsspec/cli-options": "0.0.6",
"@jsspec/format": "0.0.9"
"@jsspec/format": "0.0.11"
},
"devDependencies": {
"c8": "^4.1.5",
Expand Down
12 changes: 8 additions & 4 deletions spec/failure.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ const cp = require('child_process');
const result = cp.spawnSync(process.argv[0], [process.argv[1], '-r', './not/a/file.js', '--', './fail_spec/failure.spec.js', './fail_spec/not.a.file.js'], { stdio: 'pipe' }).stdout.toString();

describe('failures', () => {
set('failures', 5);
set('failures', 6);
set('total', () => failures);

it('fails as expected', () => {
expect(result).to.include(`${failures} examples, ${failures} failures`);
expect(result).to.include(`${total} examples, ${failures} failures`);
// timeout
expect(result).to.match(/\d+\) timeout times out/);
expect(result).to.match(/example timeout \(200ms\) exceeded/);
Expand All @@ -17,8 +18,11 @@ describe('failures', () => {
expect(result).to.match(/LOAD ERROR (\{ )?Error: Cannot find module.*not\.a\.file\.js/);

// failure in before hook
expect(result).to.match(/\d+\) hooks fails on the initial call/);
expect(result).to.match(/\d+\) hooks at depth fails further calls/);
expect(result).to.match(/\d+\) before hook fails on the initial call/);
expect(result).to.match(/\d+\) before hook at depth fails further calls/);

// failure in after hook
expect(result).to.match(/\d+\) after hook In after hook: fails with it's own report/);

// silently ignores missing require files

Expand Down
79 changes: 69 additions & 10 deletions spec/hooks.spec.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
'use strict';
const nonExecutor = require('./spec_helper').nonExecutor;

describe('before', () => {
set('ping', () => {
let value = 0;
return (offset = 1, set = false) => {
if (set) value = offset;
else value += offset;
return value;
};
});
const pinger = () => {
let value = 0;
return (offset = 1, set = false) => {
if (set) value = offset;
else value += offset;
return value;
};
};

describe('hooks', () => {
context('hooks only run if there is an example', () => {
before(nonExecutor);
after(nonExecutor);
});
});

describe('before', () => {
set('ping', pinger);

describe('before hook call types', () => {
before('Named hook with option', {}, () => ping());
before('Named hook', () => ping());
before(() => ping());
it('pings for each hook, plus the expectation call', () => expect(ping()).to.eql(4));
it('each hook is called', () => expect(ping()).to.eql(4)); // 4th one is for the expectation
});

describe('before hook call order', () => {
Expand Down Expand Up @@ -72,3 +77,57 @@ describe('before', () => {
});
});
});

describe('after', () => {
set('ping', pinger);

describe('after hook call types', () => {
it('block for after hook test', () => {});
after('Named hook with option', {}, () => ping());
after('Named hook', () => ping());
after(() => ping());
after('each hook is called', () => expect(ping()).to.eql(4)); // 4th one is for the expectation
});

describe('after hook call order', () => {
it('block for after hook test', () => {});
after('first', () => ping(100, true));
after('second', () => ping(5, true));
after('runs in order of definition', () => expect(ping(0)).to.eql(5));
});

describe('calling with no function to execute', () => {
try {
after('what even is this');
it('should not execute this block', nonExecutor);
}catch (e) {
it('fails', () => expect(e).to.be.an.instanceOf(TypeError));
}
});

describe('calling from within an example', () => {
it('fails', () =>
expect(() =>
after('I should throw', nonExecutor)
).to.throw(ReferenceError, 'example block (`after`) can not be defined inside another')
);
});

describe('nested execution', () => {
after('order is maintained', () => expect(ping(10)).to.eql(12));
after(() => ping(0, true));

context('one deep', () => {
it('block for after hook test', () => {});

after('executes', () => expect(ping()).to.eql(2));

context('two deep', () => {
it('block for after hook test', () => {});
it('block 2 for after hook test', () => {});

after('executes only once', () => expect(ping()).to.eql(1));
});
});
});
});
1 change: 0 additions & 1 deletion src/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ class Context {

runBeforeEach() {}
runAfterEach() {}
runAfterHooks() {}

static begin(emitter, file, options) {
baseContext = currentContext = new Context('', options);
Expand Down
2 changes: 1 addition & 1 deletion src/expose_global.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const executionHook = {
// beforeEach: false,
// afterEach: false,
before: false,
// after: false
after: false
};

const shared = {
Expand Down
43 changes: 43 additions & 0 deletions src/global/after.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const Example = require('../example');

module.exports = {
initialise() {
this.afterHooks = [];
},
instance: {
addAfterHook(example) {
if (this.executing) { throw ReferenceError('An example block (`after`) can not be defined inside another'); }
this.afterHooks.push(example);
},

runAfterHooks: async function() {
this.setTreeExecution(true);
for(let i=0; i< this.afterHooks.length; i++) {
const hook = this.afterHooks[i];
if (!hook.hasRun) {
await hook.run().catch(error => hook.failure = error);
hook.hasRun = true;
}
if (hook.failure) {
this.emitter.emit('afterHookFailure', hook);
}
}
this.setTreeExecution(false);

if(this.parent) await this.parent.runAfterHooks();
}
},
global: {
build(description, optionOrBlock, block) {
let expandedDescription = description ? `In after hook: ${description.toString()}` : 'In after hook';

if (block instanceof Function)
this.currentContext.addAfterHook(new Example(expandedDescription, 'after', optionOrBlock, block, this.currentContext));
else if (optionOrBlock instanceof Function)
this.currentContext.addAfterHook(new Example(expandedDescription, 'after', {}, optionOrBlock, this.currentContext));
else if ( description instanceof Function)
this.currentContext.addAfterHook(new Example('In after hook', 'after', {}, description, this.currentContext));
else throw TypeError('`after` must be provided an executable block');
}
}
};
2 changes: 1 addition & 1 deletion src/global/it.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module.exports = {
runExample: async function(example) {
this.setTreeExecution(true);
try {
this.emitter.emit('exampleStart', example, this.executing);
this.emitter.emit('exampleStart', example);
await this.runBeforeHooks();
await this.runBeforeEach();

Expand Down

0 comments on commit 9fcba03

Please sign in to comment.