While you should always strive for 100% test coverage, it's sometimes impractical and time consuming to test every single bit of your code, which is why we sometimes tend to skip the small, seemingly unbreakable parts.
I've pushed code to production in the past that broke the whole application just because I untroduced a syntax error in one of the small helper files by mistake and rendered the whole application unusable.
To avoid making the same mistake again, I decided to write one single integration test that loads the whole application (as it was opened in a browser) and checks whether it actually loads. Then, to take it jsut a bit furher, it tries to log in (Because the login screen is what appears first when the app loads). All of this in the terminal.
Here's the test, and it doesn't require AVA to run, just use
$ NODE_ENV=test babel-node test/integration/login.scenario.js
Here's the script:
import path from 'path';
import assert from 'assert';
import proxyquire from 'proxyquire';
import nock from 'nock';
import jsdom from 'jsdom';
import mockLocalStorage from '../mockLocalStorage.js';
process.env.NODE_PATH = path.resolve(__dirname, '..', '..', '..', 'client', 'js');
require('module').Module._initPaths();
const markup = '<!doctype html><html><body><div id="root"></div></body></html>';
const virtualConsole = jsdom.createVirtualConsole().sendTo(console);
const document = jsdom.jsdom(markup, { virtualConsole });
const win = document.defaultView;
mockLocalStorage(win, document);
((window) => {
for (const key in window) {
if (! window.hasOwnProperty(key)) continue;
if (key in global) continue;
global[ key ] = window[ key ];
}
})(win);
const noCallThru = {
'@noCallThru': true,
'@global': true,
};
const config = Object.assign({}, noCallThru, {
apiUrl: 'http://test.com',
});
const stubs = {
'../../../img/logo.svg': noCallThru,
'../../img/logo.svg': noCallThru,
'../../img/modal-close.svg': noCallThru,
'../scss/styles.scss': noCallThru,
'config': config,
}
nock(config.apiUrl)
.defaultReplyHeaders({ 'Content-Type': 'application/json' })
.post('/user/login')
.replyWithFile(200, 'mocks/user/login.json');
nock(config.apiUrl)
.defaultReplyHeaders({ 'Content-Type': 'application/json' })
.get('/user/performance')
.replyWithFile(200, 'mocks/user/performance.json');
window.doneRendering = function() {
const root = document.getElementById('root');
const emailInput = document.querySelector('#email');
const button = document.querySelector('button');
// Some manualy work, just like we'd to it in browser
emailInput.value = 'john@test.com';
button.click();
// Wait a reasonable amount so that React can re-render properly
setTimeout(function() {
const header = document.querySelector('h1');
assert.equal(header.innerHTML, 'Showing performance for last 30 days');
nock.cleanAll();
console.log('Integration test complete. Passed with flying colors!');
}, 100);
};
proxyquire('../../js/index.js', stubs);