import expect from 'app/test/unexpected'; import sinon from 'sinon'; import { SynchronousPromise } from 'synchronous-promise'; import { Store } from 'redux'; import AuthFlow from 'app/services/authFlow/AuthFlow'; import RegisterState from 'app/services/authFlow/RegisterState'; import ActivationState from 'app/services/authFlow/ActivationState'; import ResendActivationState from 'app/services/authFlow/ResendActivationState'; describe('AuthFlow.functional', () => { let flow: AuthFlow; let actions: {}; let store: Store; let state: { user?: { isGuest: boolean; isActive: boolean } }; let navigate: (path: string, extra?: {}) => void; beforeEach(() => { actions = {}; store = { getState: sinon.stub().named('store.getState'), // @ts-ignore dispatch: sinon .spy(({ type, payload = {} }) => { if (type === '@@router/TRANSITION' && payload.method === 'push') { // emulate redux-router // @ts-ignore navigate(...payload.args); } }) .named('store.dispatch'), }; state = {}; // @ts-ignore flow = new AuthFlow(actions); flow.setStore(store); let lastUrl: string; navigate = function navigate(path, extra = {}) { // emulates router behaviour if (lastUrl !== path) { lastUrl = path; flow.handleRequest({ path, query: new URLSearchParams(), params: {}, ...extra }, navigate); } }; sinon.stub(flow, 'run').named('flow.run'); sinon.spy(flow, 'navigate').named('flow.navigate'); // @ts-ignore store.getState.returns(state); }); describe('guest', () => { beforeEach(() => { Object.assign(state, { user: { isGuest: true, }, auth: { credentials: { login: null, }, }, }); }); it('should redirect guest / -> /login', () => { navigate('/'); expect(flow.navigate, 'was called twice'); expect(flow.navigate, 'to have a call satisfying', ['/login']); }); it('should redirect guest to /login after /login -> /', () => { // this is to ensure, that when AuthFlow is already on LoginState (on /login) // it will not allow user to go to / (which is forbidden for users) and will // always redirect to /login, so that enter condition of state is always satisfied navigate('/login'); navigate('/'); expect(flow.navigate, 'was called thrice'); expect(flow.navigate, 'to have calls satisfying', [['/login'], ['/login'], ['/login']]); }); }); describe('oauth', () => { it('should oauth without any rendering if no acceptance required', () => { const expectedRedirect = 'foo'; Object.assign(state, { user: { isGuest: false, }, auth: { credentials: {}, oauth: { params: { clientId: 123, }, prompt: [], }, }, }); // @ts-ignore flow.run.onCall(0).returns(SynchronousPromise.resolve()); // @ts-ignore flow.run.onCall(1).returns( SynchronousPromise.resolve({ redirectUri: expectedRedirect, }), ); navigate('/oauth2'); expect(flow.run, 'to have calls satisfying', [ ['oAuthValidate', {}], ['oAuthComplete', {}], ['redirect', expectedRedirect], ]); }); }); describe('/resend-activation #goBack()', () => { beforeEach(() => { state.user = { isGuest: true, isActive: false, }; }); it('should goBack to /activation', () => { navigate('/activation'); expect(flow.state, 'to be a', ActivationState); flow.state.reject(flow); expect(flow.state, 'to be a', ResendActivationState); flow.state.goBack(flow); expect(flow.state, 'to be a', ActivationState); }); it('should goBack to /register', () => { navigate('/register'); expect(flow.state, 'to be a', RegisterState); flow.state.reject(flow, { requestEmail: true }); expect(flow.state, 'to be a', ResendActivationState); flow.state.goBack(flow); expect(flow.state, 'to be a', RegisterState); }); }); });