diff options
| author | Erik Larkö <erik@mullvad.net> | 2017-06-30 10:14:36 +0200 |
|---|---|---|
| committer | Erik Larkö <erik@mullvad.net> | 2017-07-03 15:26:08 +0200 |
| commit | d06281eb566330a38b13a56bb9fdadbd7ef9aa99 (patch) | |
| tree | e1bc465e4e1b54b9ccbb8c500ad9b921fd2e16f9 /test | |
| parent | 005a6094aa48738f54e97d809de6e11f3b966da0 (diff) | |
| download | mullvadvpn-d06281eb566330a38b13a56bb9fdadbd7ef9aa99.tar.xz mullvadvpn-d06281eb566330a38b13a56bb9fdadbd7ef9aa99.zip | |
Add first login workflow test
Diffstat (limited to 'test')
| -rw-r--r-- | test/helpers/IpcChain.js | 84 | ||||
| -rw-r--r-- | test/helpers/ipc-helpers.js | 51 | ||||
| -rw-r--r-- | test/login.spec.js | 30 | ||||
| -rw-r--r-- | test/routing.spec.js | 2 |
4 files changed, 166 insertions, 1 deletions
diff --git a/test/helpers/IpcChain.js b/test/helpers/IpcChain.js new file mode 100644 index 0000000000..247ea0526b --- /dev/null +++ b/test/helpers/IpcChain.js @@ -0,0 +1,84 @@ +// @flow + +import { expect } from 'chai'; +import { check, failFast } from './ipc-helpers'; + +export class IpcChain { + _expectedCalls: Array<string>; + _recordedCalls: Array<string>; + _mockIpc: {}; + _done: (*) => void; + + constructor(mockIpc: {}, done: (*) => void) { + this._expectedCalls = []; + this._recordedCalls = []; + this._mockIpc = mockIpc; + this._done = done; + } + + addRequiredStep(ipcCall: string): StepBuilder { + this._expectedCalls.push(ipcCall); + return new StepBuilder(ipcCall, this._addStep.bind(this)); + } + + _addStep(step: StepBuilder) { + const me = this; + this._mockIpc[step.ipcCall] = function() { + return new Promise(r => me._stepPromiseCallback(step, r, arguments)); + }; + } + + _stepPromiseCallback(step, resolve, args) { + this._registerCall(step.ipcCall); + + if (step.inputValidation) { + failFast(() => step.inputValidation(...args), this._done); + } + + if (this._isLastCall()) { + this._ensureChainCalledCorrectly(); + } + + resolve(step.returnValue); + } + + _isLastCall(): boolean { + return this._recordedCalls.length === this._expectedCalls.length; + } + + _ensureChainCalledCorrectly() { + check(() => { + expect(this._expectedCalls).to.deep.equal(this._recordedCalls); + }, this._done); + } + + _registerCall(ipcCall: string) { + this._recordedCalls.push(ipcCall); + } +} + +class StepBuilder { + ipcCall: string; + inputValidation: () => void; + returnValue: *; + _cb: (StepBuilder) => void; + + constructor(ipcCall: string, cb: (StepBuilder)=> void) { + this.ipcCall = ipcCall; + this._cb = cb; + } + + withInputValidation(iv: () => void): StepBuilder { + this.inputValidation = iv; + return this; + } + + withReturnValue(rv: *): StepBuilder { + this.returnValue = rv; + return this; + } + + done() { + this._cb(this); + } +} diff --git a/test/helpers/ipc-helpers.js b/test/helpers/ipc-helpers.js new file mode 100644 index 0000000000..39fc9fba06 --- /dev/null +++ b/test/helpers/ipc-helpers.js @@ -0,0 +1,51 @@ +// @flow + +import { Backend } from '../../app/lib/backend'; +import { newMockIpc } from '../mocks/ipc'; +import configureStore from '../../app/redux/store'; +import { createMemoryHistory } from 'history'; + +type DoneCallback = (?mixed) => void; +type Check = () => void; + +// Mock localStorage because redux-localstorage has no test helpers +// We use redux-localstorage when we setup the redux store to have the +// store persist when the application is shut down. +global.localStorage = {getItem: ()=>'{}', setItem: ()=>{}}; + +export function setupBackendAndStore() { + + const memoryHistory = createMemoryHistory(); + const store = configureStore(null, memoryHistory); + + const mockIpc = newMockIpc(); + + const backend = new Backend(store, mockIpc); + + return { store, mockIpc, backend }; +} + +// chai and async aren't the best of friends. To allow us +// to get the assertion error in the output of failed async +// tests we need to do this try-catch thing. +export function check(fn: Check, done: DoneCallback) { + try { + fn(); + done(); + } catch (e) { + done(e); + } +} + + +// In async tests where we want to test a chain of IPC messages +// we can only invoke `done` for the last message. This function +// is for the intermediate messages. +export function failFast(fn: Check, done: DoneCallback) { + try { + fn(); + } catch(e) { + done(e); + } +} + diff --git a/test/login.spec.js b/test/login.spec.js new file mode 100644 index 0000000000..84ca0f431a --- /dev/null +++ b/test/login.spec.js @@ -0,0 +1,30 @@ +// @flow + +import { expect } from 'chai'; +import { setupBackendAndStore } from './helpers/ipc-helpers'; +import { IpcChain } from './helpers/IpcChain'; +import accountActions from '../app/redux/account/actions'; + +describe('Logging in', () => { + + it('should validate the account number and then set it in the backend', (done) => { + const { store, mockIpc, backend } = setupBackendAndStore(); + + const chain = new IpcChain(mockIpc, done); + chain.addRequiredStep('getAccountData') + .withInputValidation((an) => { + expect(an).to.equal('123'); + }) + .done(); + + chain.addRequiredStep('setAccount') + .withInputValidation((an) => { + expect(an).to.equal('123'); + }) + .done(); + + const action: any = accountActions.login(backend, '123'); + store.dispatch(action); + }); +}); + diff --git a/test/routing.spec.js b/test/routing.spec.js index 3e1f80be3c..ccb702de8b 100644 --- a/test/routing.spec.js +++ b/test/routing.spec.js @@ -24,7 +24,7 @@ describe('routing', function() { }); const store = mockStore(state); - const backend = new Backend(newMockIpc()); + const backend = new Backend(store, newMockIpc()); mapBackendEventsToRouter(backend, store); store.dispatch(accountActions.logout(backend)); |
