summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/auth.spec.js41
-rw-r--r--test/autologin.spec.js126
-rw-r--r--test/components/Login.spec.js27
-rw-r--r--test/connect.spec.js62
-rw-r--r--test/helpers/IpcChain.js100
-rw-r--r--test/helpers/ipc-helpers.js94
-rw-r--r--test/login.spec.js83
-rw-r--r--test/logout.spec.js67
-rw-r--r--test/mocks/redux.js4
-rw-r--r--test/mocks/rpc.js (renamed from test/mocks/ipc.js)37
10 files changed, 34 insertions, 607 deletions
diff --git a/test/auth.spec.js b/test/auth.spec.js
deleted file mode 100644
index 4f919dff87..0000000000
--- a/test/auth.spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-// @flow
-
-import { setupIpcAndStore, setupBackendAndStore } from './helpers/ipc-helpers';
-import { IpcChain } from './helpers/IpcChain';
-import { Backend } from '../app/lib/backend';
-
-describe('authentication', () => {
- it('authenticates before ipc call if unauthenticated', (done) => {
- const { store, mockIpc } = setupIpcAndStore();
-
- const credentials = {
- connectionString: 'ws://localhost:1234/',
- sharedSecret: '1234',
- };
-
- const chain = new IpcChain(mockIpc);
- chain.expect('authenticate').withInputValidation((secret) => {
- expect(secret).to.equal(credentials.sharedSecret);
- });
- chain.expect('connect');
- chain.end(done);
-
- const backend = new Backend(store, mockIpc);
- backend.connect(credentials);
-
- backend.connectTunnel();
- });
-
- it('reauthenticates on reconnect', async () => {
- const { mockIpc, backend } = setupBackendAndStore();
-
- mockIpc.authenticate = spy(mockIpc.authenticate);
- await mockIpc.connectTunnel();
- mockIpc.killWebSocket();
-
- expect(mockIpc.authenticate).to.not.have.been.called();
-
- await backend.connectTunnel();
- expect(mockIpc.authenticate).to.have.been.called.once;
- });
-});
diff --git a/test/autologin.spec.js b/test/autologin.spec.js
deleted file mode 100644
index 505ad285c0..0000000000
--- a/test/autologin.spec.js
+++ /dev/null
@@ -1,126 +0,0 @@
-// @flow
-
-import { setupBackendAndStore, setupBackendAndMockStore, getLocation } from './helpers/ipc-helpers';
-import { IpcChain } from './helpers/IpcChain';
-
-describe('autologin', () => {
- it('should send get_account then get_account_data if an account is set', (done) => {
- const { mockIpc, backend } = setupBackendAndStore();
-
- const randomAccountToken = '12345';
-
- const chain = new IpcChain(mockIpc);
- chain.expect('getAccount').withReturnValue(randomAccountToken);
-
- chain.expect('getAccountData').withInputValidation((num) => {
- expect(num).to.equal(randomAccountToken);
- });
-
- chain.end(done);
-
- backend.autologin();
- });
-
- it('should redirect to the login page if no account is set', () => {
- const { store, backend, mockIpc } = setupBackendAndMockStore();
-
- mockIpc.getAccount = () => new Promise((_, reject) => reject('NO_ACCOUNT'));
-
- return backend
- .autologin()
- .then(() => {
- expect(getLocation(store)).to.equal('/');
- })
- .catch((e) => {
- if (e !== 'NO_ACCOUNT') {
- throw e;
- }
- });
- });
-
- it('should redirect to the login page for non-existing accounts', () => {
- const { store, backend, mockIpc } = setupBackendAndMockStore();
-
- mockIpc.getAccount = () => new Promise((r) => r('123'));
- mockIpc.getAccountData = () => new Promise((_, reject) => reject('NO_ACCOUNT'));
-
- return backend
- .autologin()
- .then(() => {
- expect(getLocation(store)).to.equal('/');
- })
- .catch((e) => {
- if (e !== 'NO_ACCOUNT') {
- throw e;
- }
- });
- });
-
- it('should mark the state as not logged in if no account is set', () => {
- const { store, backend, mockIpc } = setupBackendAndStore();
-
- mockIpc.getAccount = () => Promise.resolve(null);
-
- return backend
- .autologin()
- .catch(() => {}) // ignore errors
- .then(() => {
- const state = store.getState().account;
-
- expect(state.status).to.equal('none');
- expect(state.accountToken).to.be.null;
- expect(state.error).to.be.null;
- });
- });
-
- it('should mark the state as not logged in for non-existing accounts', () => {
- const { store, backend, mockIpc } = setupBackendAndStore();
-
- mockIpc.getAccount = () => new Promise((r) => r('123'));
- mockIpc.getAccountData = () => new Promise((_, reject) => reject('NO ACCOUNT'));
-
- return backend
- .autologin()
- .catch(() => {}) // ignore errors
- .then(() => {
- const state = store.getState().account;
-
- expect(state.status).to.equal('none');
- expect(state.error).to.be.null;
- });
- });
-
- it('should put the account data in the state for existing accounts', () => {
- const { store, backend, mockIpc } = setupBackendAndStore();
- mockIpc.getAccount = () => new Promise((r) => r('123'));
- mockIpc.getAccountData = () =>
- new Promise((r) =>
- r({
- expiry: '2001-01-01T00:00:00Z',
- }),
- );
-
- return backend.autologin().then(() => {
- const state = store.getState().account;
- expect(state.status).to.equal('ok');
- expect(state.accountToken).to.equal('123');
- expect(state.expiry).to.equal('2001-01-01T00:00:00Z');
- });
- });
-
- it('should redirect to /connect for existing accounts', () => {
- const { store, backend, mockIpc } = setupBackendAndMockStore();
-
- mockIpc.getAccount = () => new Promise((r) => r('123'));
- mockIpc.getAccountData = () =>
- new Promise((r) =>
- r({
- expiry: '2001-01-01T00:00:00Z',
- }),
- );
-
- return backend.autologin().then(() => {
- expect(getLocation(store)).to.equal('/connect');
- });
- });
-});
diff --git a/test/components/Login.spec.js b/test/components/Login.spec.js
index f3cbc24de1..6a77313f5d 100644
--- a/test/components/Login.spec.js
+++ b/test/components/Login.spec.js
@@ -5,12 +5,14 @@ import { shallow } from 'enzyme';
import Login from '../../app/components/Login';
describe('components/Login', () => {
-
it('does not show the footer when logging in', () => {
const component = shallow(
- <Login { ...{ ...defaultProps,
- loginState: 'logging in'
- }} />
+ <Login
+ {...{
+ ...defaultProps,
+ loginState: 'logging in',
+ }}
+ />,
);
const visibleFooters = getComponent(component, 'footerVisibility true');
const invisibleFooters = getComponent(component, 'footerVisibility false');
@@ -19,9 +21,7 @@ describe('components/Login', () => {
});
it('shows the footer and account input when not logged in', () => {
- const component = shallow(
- <Login {...defaultProps} />
- );
+ const component = shallow(<Login {...defaultProps} />);
const visibleFooters = getComponent(component, 'footerVisibility true');
const invisibleFooters = getComponent(component, 'footerVisibility false');
expect(visibleFooters.length).to.equal(1);
@@ -31,9 +31,12 @@ describe('components/Login', () => {
it('does not show the footer nor account input when logged in', () => {
const component = shallow(
- <Login {...{ ...defaultProps,
- loginState: 'ok'
- }} />
+ <Login
+ {...{
+ ...defaultProps,
+ loginState: 'ok',
+ }}
+ />,
);
const visibleFooters = getComponent(component, 'footerVisibility true');
const invisibleFooters = getComponent(component, 'footerVisibility false');
@@ -43,9 +46,7 @@ describe('components/Login', () => {
});
it('logs in with the entered account number when clicking the login icon', (done) => {
- const component = shallow(
- <Login {...defaultProps } />
- );
+ const component = shallow(<Login {...defaultProps} />);
component.setProps({
accountToken: '12345',
login: (accountToken) => {
diff --git a/test/connect.spec.js b/test/connect.spec.js
deleted file mode 100644
index 409a04c0fb..0000000000
--- a/test/connect.spec.js
+++ /dev/null
@@ -1,62 +0,0 @@
-// @flow
-
-import connectionActions from '../app/redux/connection/actions';
-import { setupBackendAndStore, checkNextTick } from './helpers/ipc-helpers';
-
-describe('connect', () => {
- it("should set the connection state to 'disconnected' on failed attempts", (done) => {
- const { store, mockIpc, backend } = setupBackendAndStore();
-
- mockIpc.connectTunnel = () => new Promise((_, reject) => reject('Some error'));
-
- store.dispatch(connectionActions.connected());
-
- expect(store.getState().connection.status).not.to.equal('disconnected');
-
- store.dispatch(connectionActions.connect(backend));
-
- checkNextTick(() => {
- expect(store.getState().connection.status).to.equal('disconnected');
- }, done);
- });
-
- it('should update the state with the server address', () => {
- const { store, backend } = setupBackendAndStore();
-
- return backend.connectTunnel().then(() => {
- const state = store.getState().connection;
- expect(state.status).to.equal('connecting');
- });
- });
-
- it("should correctly deduce 'connected' from backend states", (done) => {
- const { store, mockIpc } = setupBackendAndStore();
-
- checkNextTick(() => {
- expect(store.getState().connection.status).not.to.equal('connected');
- mockIpc.sendNewState({ state: 'secured', target_state: 'secured' });
- expect(store.getState().connection.status).to.equal('connected');
- }, done);
- });
-
- it("should correctly deduce 'connecting' from backend states", (done) => {
- const { store, mockIpc } = setupBackendAndStore();
-
- checkNextTick(() => {
- expect(store.getState().connection.status).not.to.equal('connecting');
- mockIpc.sendNewState({ state: 'unsecured', target_state: 'secured' });
- expect(store.getState().connection.status).to.equal('connecting');
- }, done);
- });
-
- it("should correctly deduce 'disconnected' from backend states", (done) => {
- const { store, mockIpc } = setupBackendAndStore();
- store.dispatch(connectionActions.connected());
-
- checkNextTick(() => {
- expect(store.getState().connection.status).not.to.equal('disconnected');
- mockIpc.sendNewState({ state: 'unsecured', target_state: 'unsecured' });
- expect(store.getState().connection.status).to.equal('disconnected');
- }, done);
- });
-});
diff --git a/test/helpers/IpcChain.js b/test/helpers/IpcChain.js
deleted file mode 100644
index 547851fdcf..0000000000
--- a/test/helpers/IpcChain.js
+++ /dev/null
@@ -1,100 +0,0 @@
-// @flow
-
-export class IpcChain {
- _expectedCalls: Array<string>;
- _recordedCalls: Array<string>;
- _mockIpc: {};
- _done: (?Error) => void;
- _aborted: boolean;
-
- constructor(mockIpc: {}) {
- this._expectedCalls = [];
- this._recordedCalls = [];
- this._mockIpc = mockIpc;
- this._aborted = false;
- }
-
- expect<R>(ipcCall: string): StepBuilder<R> {
- const builder = new StepBuilder(ipcCall);
- this._expectedCalls.push(ipcCall);
- this._addStep(builder);
-
- return builder;
- }
-
- _addStep<R>(step: StepBuilder<R>) {
- this._mockIpc[step.ipcCall] = (...args: Array<mixed>) => {
- return new Promise((r) => this._stepPromiseCallback(step, r, args));
- };
- }
-
- _stepPromiseCallback<R>(step: StepBuilder<R>, resolve: (?R) => void, args: Array<mixed>) {
- if (this._aborted) {
- return;
- }
-
- this._registerCall(step.ipcCall);
-
- const inputValidation = step.inputValidation;
- if (inputValidation) {
- try {
- inputValidation(...args);
- } catch (error) {
- this._abort();
- this._done(error);
- return;
- }
- }
-
- if (this._isLastCall()) {
- this._ensureChainCalledCorrectly();
- }
-
- resolve(step.returnValue);
- }
-
- _abort() {
- this._aborted = true;
- }
-
- _isLastCall(): boolean {
- return this._recordedCalls.length === this._expectedCalls.length;
- }
-
- _ensureChainCalledCorrectly() {
- try {
- expect(this._expectedCalls).to.deep.equal(this._recordedCalls);
- this._done();
- } catch (error) {
- this._done(error);
- }
- }
-
- _registerCall(ipcCall: string) {
- this._recordedCalls.push(ipcCall);
- }
-
- end(done: (?Error) => void) {
- this._done = done;
- }
-}
-
-class StepBuilder<R> {
- ipcCall: string;
- inputValidation: ?(...args: Array<mixed>) => void;
- returnValue: ?R;
-
- constructor(ipcCall: string) {
- this.ipcCall = ipcCall;
- }
-
- withInputValidation(iv: (...args: Array<mixed>) => void): this {
- this.inputValidation = iv;
- return this;
- }
-
- withReturnValue(rv: R): this {
- this.returnValue = rv;
- return this;
- }
-}
diff --git a/test/helpers/ipc-helpers.js b/test/helpers/ipc-helpers.js
deleted file mode 100644
index 5bfe1d0267..0000000000
--- a/test/helpers/ipc-helpers.js
+++ /dev/null
@@ -1,94 +0,0 @@
-// @flow
-
-import { Backend } from '../../app/lib/backend';
-import { newMockIpc } from '../mocks/ipc';
-import configureStore from '../../app/redux/store';
-import { createMemoryHistory } from 'history';
-import { mockStore } from '../mocks/redux';
-
-type DoneCallback = (?Error) => void;
-type Check = () => void;
-
-export function setupIpcAndStore() {
- const memoryHistory = createMemoryHistory();
- const store = configureStore(null, memoryHistory);
- const mockIpc = newMockIpc();
-
- return { store, mockIpc };
-}
-
-export function setupBackendAndStore() {
- const { store, mockIpc } = setupIpcAndStore();
- const backend = new Backend(store, mockIpc);
-
- return { store, mockIpc, backend };
-}
-
-export function setupBackendAndMockStore() {
- const store = mockStore(_initialState());
- const mockIpc = newMockIpc();
- const backend = new Backend(store, mockIpc);
- return { store, mockIpc, backend };
-}
-
-function _initialState() {
- const { store } = setupIpcAndStore();
- return store.getState();
-}
-
-// 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);
- }
-}
-
-// 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
-// is for the intermediate messages.
-export function failFast(fn: Check, done: DoneCallback): boolean {
- try {
- fn();
- return false;
- } catch (e) {
- done(e);
- return true;
- }
-}
-export function failFastNextTick(fn: Check, done: DoneCallback) {
- setTimeout(() => {
- failFast(fn, done);
- }, 1);
-}
-
-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
deleted file mode 100644
index a2390b5241..0000000000
--- a/test/login.spec.js
+++ /dev/null
@@ -1,83 +0,0 @@
-// @flow
-
-import {
- setupBackendAndStore,
- setupBackendAndMockStore,
- checkNextTick,
- getLocation,
- failFast,
- check,
-} 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);
- chain.expect('getAccountData').withInputValidation((an) => {
- expect(an).to.equal('123');
- });
-
- chain.expect('setAccount').withInputValidation((an) => {
- expect(an).to.equal('123');
- });
-
- chain.end(done);
-
- store.dispatch(accountActions.login(backend, '123'));
- });
-
- it('should put the account data in the state', () => {
- const { store, backend, mockIpc } = setupBackendAndStore();
- mockIpc.getAccountData = () =>
- new Promise((r) =>
- r({
- expiry: '2001-01-01T00:00:00Z',
- }),
- );
-
- return backend.login('123').then(() => {
- const state = store.getState().account;
- expect(state.status).to.equal('ok');
- expect(state.accountToken).to.equal('123');
- expect(state.expiry).to.equal('2001-01-01T00:00:00Z');
- });
- });
-
- it('should indicate failure for non-existing accounts', (done) => {
- const { store, mockIpc, backend } = setupBackendAndStore();
-
- mockIpc.getAccountData = (_num) =>
- new Promise((_, reject) => {
- reject('NO SUCH ACCOUNT');
- });
-
- store.dispatch(accountActions.login(backend, '123'));
-
- 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();
-
- store.dispatch(accountActions.login(backend, '123'));
-
- setTimeout(() => {
- failFast(() => {
- expect(getLocation(store)).not.to.equal('/connect');
- }, done);
- }, 100);
-
- setTimeout(() => {
- check(() => {
- expect(getLocation(store)).to.equal('/connect');
- }, done);
- }, 1100);
- });
-});
diff --git a/test/logout.spec.js b/test/logout.spec.js
deleted file mode 100644
index c973c6a81a..0000000000
--- a/test/logout.spec.js
+++ /dev/null
@@ -1,67 +0,0 @@
-// @flow
-
-import {
- setupBackendAndStore,
- setupBackendAndMockStore,
- getLocation,
- checkNextTick,
- failFastNextTick,
-} from './helpers/ipc-helpers';
-import { IpcChain } from './helpers/IpcChain';
-import accountActions from '../app/redux/account/actions';
-
-describe('logging out', () => {
- it('should set the account to null and then disconnect', (done) => {
- const { mockIpc, backend } = setupBackendAndStore();
-
- const chain = new IpcChain(mockIpc);
- chain.expect('setAccount').withInputValidation((num) => {
- expect(num).to.be.null;
- });
- chain.expect('disconnect');
- chain.end(done);
-
- backend.logout();
- });
-
- it('should remove the account number from the store', (done) => {
- const { store, backend, mockIpc } = setupBackendAndStore();
- mockIpc.getAccountData = () =>
- new Promise((r) =>
- r({
- expiry: '2001-01-01T00:00:00.000Z',
- }),
- );
- const action: any = accountActions.login(backend, '123');
- store.dispatch(action);
-
- const expectedLogoutState = {
- status: 'none',
- accountToken: null,
- expiry: null,
- error: null,
- };
-
- failFastNextTick(() => {
- let state = store.getState().account;
- expect(state).not.to.include(expectedLogoutState);
-
- backend.logout();
-
- checkNextTick(() => {
- state = store.getState().account;
- expect(state).to.include(expectedLogoutState);
- }, done);
- }, done);
- });
-
- it('should redirect to / on logout', (done) => {
- const { store, backend } = setupBackendAndMockStore();
-
- backend.logout();
-
- checkNextTick(() => {
- expect(getLocation(store)).to.equal('/');
- }, done);
- });
-});
diff --git a/test/mocks/redux.js b/test/mocks/redux.js
deleted file mode 100644
index bbab7b17f4..0000000000
--- a/test/mocks/redux.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import configureMockStore from 'redux-mock-store';
-import thunk from 'redux-thunk';
-
-export const mockStore = configureMockStore([thunk]);
diff --git a/test/mocks/ipc.js b/test/mocks/rpc.js
index b610244462..4b37ca35aa 100644
--- a/test/mocks/ipc.js
+++ b/test/mocks/rpc.js
@@ -6,21 +6,20 @@ import type {
BackendState,
} from '../../app/lib/daemon-rpc';
-interface MockIpc {
+interface MockRpc {
sendNewState: (BackendState) => void;
- killWebSocket: () => void;
-getAccountData: (AccountToken) => Promise<AccountData>;
-connectTunnel: () => Promise<void>;
-getAccount: () => Promise<?AccountToken>;
-authenticate: (string) => Promise<void>;
}
-export function newMockIpc() {
+export function newMockRpc() {
const stateListeners = [];
- let connectionOpenListener: ?() => void;
- let connectionCloseListener: ?(error: ?Error) => void;
+ const openListeners = [];
+ const closeListeners = [];
- const mockIpc: DaemonRpcProtocol & MockIpc = {
+ const mockIpc: DaemonRpcProtocol & MockRpc = {
setConnectionString: (_str: string) => {},
getAccountData: (accountToken) =>
Promise.resolve({
@@ -49,11 +48,15 @@ export function newMockIpc() {
setAllowLan: (_allowLan: boolean) => Promise.resolve(),
getAllowLan: () => Promise.resolve(true),
connect: () => {
- if (connectionOpenListener) {
- connectionOpenListener();
+ for (const listener of openListeners) {
+ listener();
+ }
+ },
+ disconnect: () => {
+ for (const listener of closeListeners) {
+ listener();
}
},
- disconnect: () => {},
connectTunnel: () => Promise.resolve(),
disconnectTunnel: () => Promise.resolve(),
getLocation: () =>
@@ -80,20 +83,20 @@ export function newMockIpc() {
}
},
addOpenConnectionObserver: (listener: () => void) => {
- connectionOpenListener = listener;
+ openListeners.push(listener);
+ return {
+ unsubscribe: () => {},
+ };
},
addCloseConnectionObserver: (listener: (error: ?Error) => void) => {
- connectionCloseListener = listener;
+ closeListeners.push(listener);
+ return {
+ unsubscribe: () => {},
+ };
},
authenticate: (_secret: string) => Promise.resolve(),
getAccountHistory: () => Promise.resolve([]),
removeAccountFromHistory: (_accountToken) => Promise.resolve(),
-
- killWebSocket: () => {
- if (connectionCloseListener) {
- connectionCloseListener();
- }
- },
};
return mockIpc;