Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hooks API for setupContainerTest() to improve beforeEach/afterEach ordering #192

Merged
merged 1 commit into from
Feb 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions addon-test-support/ember-mocha/setup-application-test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import {
beforeEach,
afterEach
} from 'mocha';
import {
setupApplicationContext,
teardownApplicationContext
} from '@ember/test-helpers';
import setupContainerTest from './setup-container-test';

export default function setupApplicationTest(options) {
afterEach(function() {
return teardownApplicationContext(this);
});

setupContainerTest(options);
let hooks = setupContainerTest(options);

beforeEach(function() {
hooks.beforeEach(function() {
return setupApplicationContext(this);
});
hooks.afterEach(function() {
return teardownApplicationContext(this);
});

return hooks;
}
58 changes: 48 additions & 10 deletions addon-test-support/ember-mocha/setup-container-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,23 @@ import {
teardownContext
} from '@ember/test-helpers';
import { assign, merge } from '@ember/polyfills';
import { resolve } from 'rsvp';

const _assign = assign || merge;

function chainHooks(hooks, context, promise = resolve()) {
return hooks.reduce((promise, fn) => promise.then(fn.bind(context)), promise);
}

export default function setupUnitTest(options) {
let originalContext;
let beforeEachHooks = [];
let afterEachHooks = [];

beforeEach(function() {
originalContext = _assign({}, this);

return setupContext(this, options).then(() => {
let promise = setupContext(this, options).then(() => {

let originalPauseTest = this.pauseTest;
this.pauseTest = function Mocha_pauseTest() {
Expand All @@ -25,19 +32,50 @@ export default function setupUnitTest(options) {
return originalPauseTest.call(this);
};
});

return chainHooks(beforeEachHooks, this, promise);
});

afterEach(function() {
return teardownContext(this).then(() => {
// delete any extraneous properties
for (let key in this) {
if (!(key in originalContext)) {
delete this[key];
let promise = chainHooks(afterEachHooks, this);

return promise
.then(() => teardownContext(this))
.then(() => {
// delete any extraneous properties
for (let key in this) {
if (!(key in originalContext)) {
delete this[key];
}
}
}

// copy over the original values
_assign(this, originalContext);
});
// copy over the original values
_assign(this, originalContext);
});
});

/**
* Provide a workaround for the inconvenient FIFO-always order of beforeEach/afterEach calls
*
* ```js
* let hooks = setupTest();
* hooks.beforeEach(function() { ... });
* hooks.afterEach(function() { ... });
* ```
*
* beforeEach hooks are called after setupUnitTest#beforeEach in FIFO (first in first out) order
* afterEach hooks are called before setupUnitTest#afterEach in LIFO (last in first out) order
*
* @type {{beforeEach: (function(*)), afterEach: (function(*))}}
*/
let hooks = {
beforeEach(fn) {
beforeEachHooks.push(fn);
},
afterEach(fn) {
afterEachHooks.unshift(fn);
}
};

return hooks;
}
17 changes: 7 additions & 10 deletions addon-test-support/ember-mocha/setup-rendering-test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import {
beforeEach,
afterEach
} from 'mocha';
import {
setupRenderingContext,
teardownRenderingContext
} from '@ember/test-helpers';
import setupContainerTest from './setup-container-test';

export default function setupRenderingTest(options) {
afterEach(function() {
return teardownRenderingContext(this);
});

setupContainerTest(options);
let hooks = setupContainerTest(options);

beforeEach(function() {
hooks.beforeEach(function() {
return setupRenderingContext(this);
});
hooks.afterEach(function() {
return teardownRenderingContext(this);
});

return hooks;
}
26 changes: 20 additions & 6 deletions tests/acceptance/setup-application-test-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,27 @@ describe('setupApplicationTest', function() {

this.timeout(5000);

setupApplicationTest();
describe('acceptance test', function() {

it('can visit subroutes', async function() {
await visit('/');
expect(this.element.querySelector('h2').textContent.trim()).to.be.empty;
setupApplicationTest();

await visit('/foo');
expect(this.element.querySelector('h2').textContent.trim()).to.be.equal('this is an acceptance test');
it('can visit subroutes', async function() {
await visit('/');
expect(this.element.querySelector('h2').textContent.trim()).to.be.empty;

await visit('/foo');
expect(this.element.querySelector('h2').textContent.trim()).to.be.equal('this is an acceptance test');
});
});

describe('hooks API', function() {

let hooks = setupApplicationTest();

it('returns hooks API', function() {
expect(hooks)
.to.respondTo('beforeEach')
.and.to.respondTo('afterEach');
})
});
});
84 changes: 83 additions & 1 deletion tests/unit/setup-container-test-test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { setupContainerTest } from 'ember-mocha';
import { describe, it, beforeEach, afterEach } from 'mocha';
import { describe, it, beforeEach, afterEach, after } from 'mocha';
import { expect } from 'chai';
import { pauseTest, resumeTest } from '@ember/test-helpers';
import Service from '@ember/service';
import hasEmberVersion from 'ember-test-helpers/has-ember-version';
import { Promise } from 'rsvp';

describe('setupContainerTest', function() {
if (!hasEmberVersion(2, 4)) {
Expand Down Expand Up @@ -78,4 +79,85 @@ describe('setupContainerTest', function() {
this.set('name', 'blue');
});
});

describe('hooks API', function() {

describe('calls hooks in order', function() {
let calledSteps = [];

function setupFoo(hooks) {
hooks.beforeEach(function() {
expect(this.owner, 'Context is set up already').to.exist;
expect(calledSteps).to.deep.equal([]);
calledSteps.push('bE1');
});
hooks.afterEach(function() {
expect(calledSteps, 'afterEach is called in LIFO order').to.deep.equal(['bE1', 'bE2', 'it', 'aE2']);
calledSteps.push('aE1');
});
}

after(function() {
expect(calledSteps, 'hooks are called in correct order').to.deep.equal(['bE1', 'bE2', 'it', 'aE2', 'aE1']);
});

let hooks = setupContainerTest();
setupFoo(hooks);

hooks.beforeEach(function() {
expect(calledSteps, 'beforeEach is called in FIFO order').to.deep.equal(['bE1']);
calledSteps.push('bE2');
});
hooks.afterEach(function() {
expect(calledSteps, 'afterEach is called in LIFO order').to.deep.equal(['bE1', 'bE2', 'it']);
calledSteps.push('aE2');
});

it('calls beforeEach/afterEach in FIFO/LIFO order', function() {
expect(calledSteps, 'it() is called after all beforeEach').to.deep.equal(['bE1', 'bE2']);
calledSteps.push('it');
});
});

describe('is Promise aware', function() {
let calledSteps = [];
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

after(function() {
expect(calledSteps, 'hooks are called in correct order').to.deep.equal(['bE1', 'bE2', 'it', 'aE2', 'aE1']);
});

let hooks = setupContainerTest();

hooks.beforeEach(function() {
expect(calledSteps, 'beforeEach waits for promise').to.deep.equal([]);
return delay(10)
.then(() => calledSteps.push('bE1'));
});
hooks.beforeEach(function() {
expect(calledSteps, 'beforeEach waits for promise').to.deep.equal(['bE1']);
return delay(10)
.then(() => calledSteps.push('bE2'));
});
hooks.afterEach(function() {
expect(calledSteps, 'afterEach waits for promise').to.deep.equal(['bE1', 'bE2', 'it', 'aE2']);
return delay(10)
.then(() => calledSteps.push('aE1'));
});
hooks.afterEach(function() {
expect(calledSteps, 'afterEach waits for promise').to.deep.equal(['bE1', 'bE2', 'it']);
return delay(10)
.then(() => calledSteps.push('aE2'));
});

it('beforeEach/afterEach chain up promises', function() {
expect(calledSteps, 'it() is called after all beforeEach').to.deep.equal(['bE1', 'bE2']);
calledSteps.push('it');
});
});

});

});
10 changes: 10 additions & 0 deletions tests/unit/setup-rendering-test-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,14 @@ describe('setupRenderingTest', function() {
});
});

describe('hooks API', function() {

let hooks = setupRenderingTest();

it('returns hooks API', function() {
expect(hooks)
.to.respondTo('beforeEach')
.and.to.respondTo('afterEach');
});
});
});