summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorErik Larkö <erik@mullvad.net>2017-07-04 16:50:46 +0200
committerErik Larkö <erik@mullvad.net>2017-07-04 16:50:46 +0200
commitb0d7ea1ae5b4555ad0aa90451480bf7ae83b3be4 (patch)
tree396f3eae4ef55621836dca8f3f1a6bfb4c0602d9
parentc714a1cb4ea2cdb0a21e3c71684e7047a77da10e (diff)
parentdb63fd56226d97243aa7c5858ecc0ebd7099d5c9 (diff)
downloadmullvadvpn-b0d7ea1ae5b4555ad0aa90451480bf7ae83b3be4.tar.xz
mullvadvpn-b0d7ea1ae5b4555ad0aa90451480bf7ae83b3be4.zip
Merge branch 'testing-login-flow'
-rw-r--r--app/lib/backend.js7
-rw-r--r--test/helpers/ipc-helpers.js35
-rw-r--r--test/login.spec.js64
3 files changed, 102 insertions, 4 deletions
diff --git a/app/lib/backend.js b/app/lib/backend.js
index 4cd2cf49e9..2923baf34d 100644
--- a/app/lib/backend.js
+++ b/app/lib/backend.js
@@ -1,4 +1,5 @@
// @flow
+
import log from 'electron-log';
import EventEmitter from 'events';
import { servers } from '../config';
@@ -169,7 +170,11 @@ export class Backend {
error: null,
}));
- this._store.dispatch(push('/connect'));
+ // Redirect the user after some time to allow for
+ // the 'Login Successful' screen to be visible
+ setTimeout(() => {
+ this._store.dispatch(push('/connect'));
+ }, 1000);
}).catch(e => {
log.error('Failed to log in', e);
diff --git a/test/helpers/ipc-helpers.js b/test/helpers/ipc-helpers.js
index 39fc9fba06..bffc6b1540 100644
--- a/test/helpers/ipc-helpers.js
+++ b/test/helpers/ipc-helpers.js
@@ -4,6 +4,7 @@ import { Backend } from '../../app/lib/backend';
import { newMockIpc } from '../mocks/ipc';
import configureStore from '../../app/redux/store';
import { createMemoryHistory } from 'history';
+import { mockState, mockStore } from '../mocks/redux';
type DoneCallback = (?mixed) => void;
type Check = () => void;
@@ -17,7 +18,7 @@ export function setupBackendAndStore() {
const memoryHistory = createMemoryHistory();
const store = configureStore(null, memoryHistory);
-
+
const mockIpc = newMockIpc();
const backend = new Backend(store, mockIpc);
@@ -25,6 +26,13 @@ export function setupBackendAndStore() {
return { store, mockIpc, backend };
}
+export function setupBackendAndMockStore() {
+ const store = mockStore(mockState());
+ 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.
@@ -37,6 +45,16 @@ export function check(fn: Check, done: DoneCallback) {
}
}
+// Sometimes with redux we cannot know when all reducers have
+// finished running. This function puts the check at the end
+// of the execution queue, hopefully resulting in the check being
+// run after the reducers are finished
+export function checkNextTick(fn: Check, done: DoneCallback) {
+ setTimeout(() => {
+ check(fn, done);
+ }, 1);
+}
+
// In async tests where we want to test a chain of IPC messages
// we can only invoke `done` for the last message. This function
@@ -49,3 +67,18 @@ export function failFast(fn: Check, done: DoneCallback) {
}
}
+type MockStore = {
+ getActions: () => Array<{type: string, payload: Object}>,
+}
+// Parses the action log to find out which URL we most recently navigated to
+// Note that this cannot be done with the real redux store, but rather must be
+// done with the mock store.
+export function getLocation(store: MockStore): ?string {
+ const navigations = store.getActions().filter(action => action.type === '@@router/CALL_HISTORY_METHOD');
+ if (navigations.length === 0) {
+ return null;
+ }
+
+ return navigations[navigations.length - 1].payload.args[0];
+}
+
diff --git a/test/login.spec.js b/test/login.spec.js
index 84ca0f431a..3d7a1ed0d1 100644
--- a/test/login.spec.js
+++ b/test/login.spec.js
@@ -1,7 +1,7 @@
// @flow
import { expect } from 'chai';
-import { setupBackendAndStore } from './helpers/ipc-helpers';
+import { setupBackendAndStore, setupBackendAndMockStore, checkNextTick, getLocation, failFast, check } from './helpers/ipc-helpers';
import { IpcChain } from './helpers/IpcChain';
import accountActions from '../app/redux/account/actions';
@@ -26,5 +26,65 @@ describe('Logging in', () => {
const action: any = accountActions.login(backend, '123');
store.dispatch(action);
});
-});
+ it('should put the account data in the state', (done) => {
+ const { store, backend, mockIpc } = setupBackendAndStore();
+ mockIpc.getAccountData = () => new Promise(r => r({
+ paid_until: '2001-01-01T00:00:00',
+ }));
+
+ const action: any = accountActions.login(backend, '123');
+ store.dispatch(action);
+
+ checkNextTick( () => {
+ const state = store.getState().account;
+ expect(state.status).to.equal('ok');
+ expect(state.accountNumber).to.equal('123');
+ expect(state.paidUntil).to.equal('2001-01-01T00:00:00');
+ }, done);
+ });
+
+ it('should indicate failure for non-existing accounts', (done) => {
+ const { store, mockIpc, backend } = setupBackendAndStore();
+
+ mockIpc.getAccountData = (_num) => new Promise((_,reject) => {
+ reject('NO SUCH ACCOUNT');
+ });
+
+
+ const action: any = accountActions.login(backend, '123');
+ store.dispatch(action);
+
+
+ checkNextTick(() => {
+ const state = store.getState().account;
+ expect(state.status).to.equal('failed');
+ expect(state.error).to.not.be.null;
+ }, done);
+ });
+
+ it('should redirect to /connect after 1s after successful login', (done) => {
+ const { store, backend } = setupBackendAndMockStore();
+
+ const action: any = accountActions.login(backend, '123');
+ store.dispatch(action);
+
+
+ setTimeout(() => {
+
+ failFast(() => {
+ expect(getLocation(store)).not.to.equal('/connect');
+ }, done);
+
+ }, 100);
+
+
+ setTimeout(() => {
+
+ check(() => {
+ expect(getLocation(store)).to.equal('/connect');
+ }, done);
+
+ }, 1100);
+ });
+});