- Capabilites
- Overview
- Compatibility
- Example
- Installation
- API
- setupAndTeardownApp
- scaleWindowWidth
- visit
- click
- mouseDown
- mouseUp
- mouseMove
- touchStart
- touchMove
- touchCancel
- touchEnd
- fillIn
- keyEventIn
- waitUntilExists
- waitUntilDisappears
- waitUntilDoesNotExist
- find
- jQuery
- setupAsync
- asyncIt
- andThen
- waitUntil
- waitMillis
- waitUntilChange
- setupFakeFetchAsync
- waitUntilFetchExists
- setupFakeFetch
- teardownFakeFetch
- fetchRespond
- startFakingXhr
- stopFakingXhr
- findXhr
- waitUntilXhrExists
- FakeRequest
- Browser level acceptance testing (
visit
,click
,waitUntilExists
) - Asynchronous unit testing (
andThen
,waitUntil
,waitUntilChange
) - Faking XHR (
waitUntilXhrExists
,findXhr
,resolveWithJson
) - Faking Fetch API (
waitUntilFetchExists
,fetchRespond
)
This library contains a bunch of helper methods that greatly simplify asynchronous unit and acceptance testing of front end web apps with Mocha. It also includes two different ways to stub server calls: one for when using window.fetch, and another for using regular XHR.
The core of this library is a set of Ember-inspired asynchronous acceptance testing tools
like visit
, click
, waitUntilExists
and andThen
among others.
A noteworthy aspect is that while Ember and others have changed their networking testing tools (for testing XHR and/or Fetch) to require the setting up the network responses prior to the actual network calls, this library simply catches all network requests and lets you deal with them after the fact.
So instead of this (in pseudo-code):
set up mock response for endpoint
run code that will call the endpoint
verify results
you would do this:
run code that will call the endpoint
trigger mock response to endpoint call
verify results
See Examples below for actual code.
We have found this order of things reads much more naturally. It also greatly simplify testing of pending states and enable a smoother way to verify that the correct network requests are being made in the first place.
For testing with Mocha. Currently only runs in browsers, see issue #1.
import {
setupAndTeardownApp,
asyncIt as it,
startFakingXhr,
stopFakingXhr,
visit,
click,
waitUntilExists,
andThen,
waitUntilXhrExists,
setupFakeFetchAsync,
waitUntilFetchExists,
} from 'acast-test-helpers';
import { createMemoryHistory } from 'react-router';
import renderMyApp from 'renderMyApp.js';
describe('my app', () => {
setupAndTeardownApp(renderMyApp, createMemoryHistory);
describe('with basic acceptance testing', () => {
it('shows greeting when I click the hello button on the say-hello page', () => {
visit('/say-hello');
click('.hello-button'); // This will wait for the button to show up, and then click it. Any jQuery selector will work.
waitUntilExists('.greeting'); // After the click has been performed, this will start looking for the greeting.
andThen(greeting => { // When the greeting appears, it will be passed as the single argument to the function passed to the following `andThen` call.
expect(greeting.text()).to.equal('Hello World!'); // `greeting` is a jQuery object.
});
});
});
describe('with fake XHR', () => {
beforeEach(startFakingXhr);
afterEach(stopFakingXhr);
it('displays the number of eggs we currently have on the server (using regular XHR)', () => {
visit('/eggs');
waitUntilXhrExists('GET', '/api/v1/eggs');
andThen(request => {
request.respondWithJson(200, {
eggCount: 42,
});
});
waitUntilExists('.egg-counter:contains("42")');
});
});
describe('with fake fetch', () => {
setupFakeFetchAsync();
it('shows the color of the day (using Fetch API)', () => {
visit('/color');
waitUntilFetchExists('/api/v1/color');
andThen(fetchRequest => {
fetchRequest.resolveWith(200, {
color: '#65a1e7'
});
});
waitUntil(() => expect(find('.color-of-the-day').css('background-color')).to.equal('#65a1e7'));
});
});
});
npm install --save-dev --save-exact acast-test-helpers
or the shorter
npm i -DE acast-test-helpers
This adds the necessary beforeEach
and afterEach
calls to set up and tear down the entire application
between each test method, for acceptance testing.
Parameters
renderAppIntoElementWithHistory
function The function that will render your app. It will be passed two arguments: the element to render the app into, and the history that will be used.createHistory
function? When called without arguments, this functions should return the history instance to use. This instance will then be passed as the second argument to renderAppIntoElementWithHistory (optional, default()=>{}
)unrenderAppFromElement
function? This will be called when your app should be torn down and removed from the DOM. It will receive as only argument the same element that was passed to renderAppIntoElementWithHistory. (optional, defaultroot=>{}
)
Used for testing reactions to window resize events.
The test root div starts at 1024 pixels and can be scaled up or down with this method.
Will trigger a resize event on the window
object.
Parameters
scale
number The scale by which to multiply the current width of the test root div.
Triggers a route change in your app by pushing to the history.
Parameters
route
string The path to go to.
Waits for an element to show up, and then simulates a user click by triggering a mouse event on that element.
Parameters
selector
(string | jQuery) The jQuery selector or jQuery object to simulate click on. Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.options
object? Any options to pass along to the simulated mouse event.
Examples
click('.element-to-click', { clientX: 1337, clientY: 1338 });
Waits for an element to show up, and then simulates a user mouse down by triggering a mouse event on that element.
Parameters
selector
(string | jQuery) The jQuery selector or jQuery object to simulate mouse down on. Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.options
object? Any options to pass along to the simulated mouse event.
Examples
mouseDown('.element-to-mouse-down-on', { clientX: 1337, clientY: 1338 });
Waits for an element to show up, and then simulates a user mouse up by triggering a mouse event on that element.
Parameters
selector
(string | jQuery) The jQuery selector or jQuery object to simulate mouse up on. Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.options
object? Any options to pass along to the simulated mouse event.
Examples
mouseUp('.element-to-mouse-up-on', { clientX: 1337, clientY: 1338 });
Waits for an element to show up, and then simulates a user mouse move by triggering a mouse event on that element.
Parameters
selector
(string | jQuery) The jQuery selector or jQuery object to simulate mouse move on. Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.options
object? Any options to pass along to the simulated mouse event.
Examples
mouseDown('.element-to-mouse-move-on', { clientX: 1337, clientY: 1338 });
Waits for an element to show up, and then simulates a user touch start by triggering a touch event on that element.
Parameters
selector
(string | jQuery) The jQuery selector or jQuery object to simulate touch on. Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.options
object? Any options to pass along to the simulated touch event.
Examples
touchStart('.element-to-touch', {touches: [{ clientX: 1337, clientY: 1338 }], changedTouches: [{ clientX: 1337, clientY: 1338}]});
Waits for an element to show up, and then simulates a user touch move by triggering a touch event on that element.
Parameters
selector
(string | jQuery) The jQuery selector or jQuery object to simulate touch on. Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.options
object? Any options to pass along to the simulated touch event.
Examples
touchMove('.element-to-touch', {touches: [{ clientX: 1337, clientY: 1338 }], changedTouches: [{ clientX: 1337, clientY: 1338}]});
Waits for an element to show up, and then simulates a user touch cancel by triggering a touch event on that element.
Parameters
selector
(string | jQuery) The jQuery selector or jQuery object to simulate touch on. Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.options
object? Any options to pass along to the simulated touch event.
Examples
touchCancel('.element-to-touch', {touches: [{ clientX: 1337, clientY: 1338 }], changedTouches: [{ clientX: 1337, clientY: 1338}]});
Waits for an element to show up, and then simulates a user touch end by triggering a touch event on that element.
Parameters
selector
(string | jQuery) The jQuery selector or jQuery object to simulate touch on. Note that the selector or jQuery object must represent exactly one (1) element in the app, or the call will fail.options
object? Any options to pass along to the simulated touch event.
Examples
touchEnd('.element-to-touch', {touches: [{ clientX: 1337, clientY: 1338 }], changedTouches: [{ clientX: 1337, clientY: 1338}]});
Waits for an input element to show up, and then simulates a user filling in the value of that input.
Parameters
selector
(string | jQuery) The jQuery selector or jQuery object to fill in. Note that the selector or jQuery object must represent exactly one (1) input element in the app, or the call will fail. This will trigger 'input' and 'changed' events on the selected input element.value
any The value to fill into the input.
Examples
fillIn('.input-container input', 'awesome value');
Trigger a key event in an element.
Parameters
selector
(string | jQuery) The jQuery selector or object of element(s) to trigger key event in.keyEventString
string The type of key event to trigger, e. g. 'keydown', 'keyup' or 'keypress'keyCode
number The integer representing which key is being pressed.
Waits until at least one element in the app matches a selector.
Parameters
selector
string The jQuery selector to wait for matches on.errorMessage
(string | function) The error message to show if the function times out waiting for the selector to give a match. If could also be a function that should return the error message string. If it is a function it will be called as late as possible, after having timed out. (optional, default`acast-test-helpers#waitUntilExists(): Selector never showed up: '${selector}'`
)
First waits for at least one element in the app to match a selector, and then waits for all of those elements to go away.
Parameters
selector
string The jQuery selector to first receive a match and then to stop doing so.
Waits for a selector not to have any matching elements in the app.
Parameters
selector
string The jQuery selector to check for match.errorMessage
(string | function) The error message to show, either as a string or as a function returning a string, that will be called when the error message is needed. (optional, default`acast-test-helpers#waitUntilDoesNotExist(): Selector never stopped existing: '${selector}'`
)
Convenience method to matching jQuery selectors within the app only (and not in the entire window).
Parameters
selector
string The jQuery selector to match.
Returns jQuery The jQuery object matching the selector within the app.
Simply the jQuery constructor.
Sets up the async test tools by adding the appropriate calls to beforeEach
and afterEach
.
Call once in the top of a describe
that you wish to use the async tools in.
NOTE: When using setupAndTeardownApp, it is not necessary to call this function separately.
Drop-in replacement for the regular it
function, with the same signature.
This will augments the it
functionality to automatically return the global test promise behind the scenes, to make
the asynchronous helpers work as expected without any need for the developer to return a promise or call a done
function.
The done
parameter can still be used and will override the global test promise flow as per the default mocha behavior.
Triggers a callback after the previous asynchronous tool function resolves.
Parameters
doThis
function The callback function to call when the previous asynchronous tool function resolves. This function will receive as argument the resolved result of that previous asynchronous tool function.
Examples
waitUntilExists('.some-element');
andThen(someElementAsJqueryObject => {
// someElementAsJqueryObject is the result of matching '.some-element'.
});
Waits until a callback returns any truthy value. It waits by polling the function repeatedly. This is very useful for verifying test results, among other things.
Parameters
thisReturnsTruthy
function The function to poll.errorMessage
(string | function) The string, or function returning a string, to be shown if this times out. (optional, default`acast-test-helpers#waitUntil() timed out since the following function never returned a truthy value within the timeout: ${thisReturnsTruthy}`
)
Examples
waitUntil(() => expect(foobar).to.equal(3)); // This will either pass as the expectation holds and is returned as truthy, or keep polling.
waitUntil(() => 3);
andThen(value => {
// value = 3
});
Waits a specific number of milliseconds. NOTE: Using this method is highly discouraged for anything other than temporary experiments. The reason is that it leads to either very long running or non-deterministic tests, none of which is desirable.
Parameters
milliseconds
number The number of milliseconds to wait.
Waits until a function gives a different return value from one call to the next.
Parameters
predicate
function The function to be polled.errorMessage
(string | function) The string, or function returning a string, to be shown if this times out. (optional, default`acast-test-helpers#waitUntilChange() timed out since the return value of the following function never changed: ${predicate}`
)
Examples
let foo = 'something';
waitUntilChange(() => foo);
andThen(theNewValueOfFoo => {
console.log(theNewValueOfFoo); // 'something else'
});
setTimeout(() => {
foo = 'something else';
}, 1000);
Convenience method to set up everything needed to use fake fetch in an async environment. Calls setupAsync, setupFakeFetch and teardownFakeFetch.
Use this by calling it once on top of the appropriate describe
.
- See: waitUntilXhrExists
Waits until a fetch call has been made, and resolves with the same return value as in fetchRespond.
Parameters
path
string The fetched path to wait for. Same as in fetchRespond.
Examples
waitUntilFetchExists('/api/user/1337');
andThen(request => {
request.resolveWith(200, {
id: 1337,
name: 'Fire'
});
});
Replaces the global window.fetch
function with a fake one to intercept any calls to fetch, and enable the
tools in this module.
Should be called before each test method that wants to fake fetch.
Restores the original window.fetch
method and tears down what was set up with setupFakeFetch.
Should be called after each test method before which setupFakeFetch was called.
Resolve to a previously intercepted fetch call.
Parameters
path
string The path of the previous fetch call to respond to.
Examples
fetchRespond('/api/user/1337').resolveWith(200, {
id: 1337,
name: 'Fire'
});
Returns {resolveWith: (function (any?, any?)), rejectWith: (function (any?))} An object with two methods:
resolveWith
and rejectWith
. Most often you want to use resolveWith
, since even HTTP errors such as 404 will
result in a resolved fetch promise. resolveWith
takes two arguments: the HTTP status, and the JSON return value.
Replaces the real XMLHttpRequest constructor with a fake one to intercept any subsequent XHR call.
Restores the real XMLHttpRequest constructor to the original one that was replaced by startFakingXhr.
Finds a previously made and still unresolved XHR request.
Parameters
method
string A string representing what HTTP Method of the request to find, like 'GET' or 'POST'url
string The complete url, including any query string, of the request to find
Returns (FakeRequest | null) The matching FakeRequest, or null if no matching request was found.
- See: waitUntilFetchExists
Asynchronous version of findXhr. Waits until the matched XHR request shows up, and then passes it to the next asynchronous function. If the test times out while waiting for the request to show up, a helpful error message will show which requests where active.
Parameters
Examples
waitUntilXhrExists('GET', '/api/user/1337');
andThen(request => {
request.respondWithJson(200, {
id: 1337,
name: 'Fire'
});
});
Extends FakeXMLHttpRequest to register each new request with fakehr
Also has the convenience method respondWithJson
that takes two arguments:
the HTTP status code as an int (optional, defaults to 200), and the response body as a plain object (also optional)