import reducer, { FETCH_ME, FETCH_ME_FAILURE, FETCH_ME_PENDING, FETCH_ME_SUCCESS, FETCH_ME_UNAUTHORIZED, fetchMe, fetchMeSuccess, fetchMeUnauthenticated, getFetchMeFailure, getLoginFailure, getLogoutFailure, getMe, isAuthenticated, isFetchMePending, isLoginPending, isLogoutPending, isRedirecting, login, LOGIN, LOGIN_FAILURE, LOGIN_PENDING, LOGIN_SUCCESS, loginSuccess, logout, LOGOUT, LOGOUT_FAILURE, LOGOUT_PENDING, LOGOUT_REDIRECT, LOGOUT_SUCCESS, logoutSuccess, redirectAfterLogout, } from './auth'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import fetchMock from 'fetch-mock'; import { FETCH_INDEXRESOURCES_PENDING, FETCH_INDEXRESOURCES_SUCCESS, } from './indexResource'; const me = { name: 'tricia', displayName: 'Tricia McMillian', mail: 'trillian@heartofgold.universe', }; describe('auth reducer', () => { it('should set me and login on successful fetch of me', () => { const state = reducer(undefined, fetchMeSuccess(me)); expect(state.me).toBe(me); }); it('should set authenticated to false', () => { const initialState = { me, }; const state = reducer(initialState, fetchMeUnauthenticated()); expect(state.me.name).toBeUndefined(); }); it('should reset the state after logout', () => { const initialState = { me, }; const state = reducer(initialState, logoutSuccess()); expect(state.me).toBeUndefined(); expect(state.authenticated).toBeUndefined(); }); it('should keep state and set redirecting to true', () => { const initialState = { me, }; const state = reducer(initialState, redirectAfterLogout()); expect(state.me).toBe(initialState.me); expect(state.redirecting).toBe(true); }); it('should set state authenticated and me after login', () => { const state = reducer(undefined, loginSuccess(me)); expect(state.me).toBe(me); }); }); describe('auth actions', () => { const mockStore = configureMockStore([thunk]); afterEach(() => { fetchMock.reset(); fetchMock.restore(); }); it('should dispatch login success and dispatch fetch me', () => { fetchMock.postOnce('/api/v2/auth/access_token', { body: { cookie: true, grant_type: 'password', username: 'tricia', password: 'secret123', }, headers: { 'content-type': 'application/json', }, }); fetchMock.getOnce('/api/v2/me', { body: me, headers: { 'content-type': 'application/json', }, }); const meLink = { me: { href: '/me', }, }; fetchMock.getOnce('/api/v2/', { _links: meLink, }); const expectedActions = [ { type: LOGIN_PENDING, }, { type: FETCH_INDEXRESOURCES_PENDING, }, { type: FETCH_INDEXRESOURCES_SUCCESS, payload: { _links: meLink, }, }, { type: LOGIN_SUCCESS, payload: me, }, ]; const store = mockStore({}); return store .dispatch(login('/auth/access_token', 'tricia', 'secret123')) .then(() => { expect(store.getActions()).toEqual(expectedActions); }); }); it('should dispatch login failure', () => { fetchMock.postOnce('/api/v2/auth/access_token', { status: 400, }); const store = mockStore({}); return store .dispatch(login('/auth/access_token', 'tricia', 'secret123')) .then(() => { const actions = store.getActions(); expect(actions[0].type).toEqual(LOGIN_PENDING); expect(actions[1].type).toEqual(LOGIN_FAILURE); expect(actions[1].payload).toBeDefined(); }); }); it('should dispatch fetch me success', () => { fetchMock.getOnce('/api/v2/me', { body: me, headers: { 'content-type': 'application/json', }, }); const expectedActions = [ { type: FETCH_ME_PENDING, }, { type: FETCH_ME_SUCCESS, payload: me, }, ]; const store = mockStore({}); return store.dispatch(fetchMe('me')).then(() => { expect(store.getActions()).toEqual(expectedActions); }); }); it('should dispatch fetch me failure', () => { fetchMock.getOnce('/api/v2/me', { status: 500, }); const store = mockStore({}); return store.dispatch(fetchMe('me')).then(() => { const actions = store.getActions(); expect(actions[0].type).toEqual(FETCH_ME_PENDING); expect(actions[1].type).toEqual(FETCH_ME_FAILURE); expect(actions[1].payload).toBeDefined(); }); }); it('should dispatch fetch me unauthorized', () => { fetchMock.getOnce('/api/v2/me', 401); const expectedActions = [ { type: FETCH_ME_PENDING, }, { type: FETCH_ME_UNAUTHORIZED, resetPending: true, }, ]; const store = mockStore({}); return store.dispatch(fetchMe('me')).then(() => { // return of async actions expect(store.getActions()).toEqual(expectedActions); }); }); it('should dispatch logout success', () => { fetchMock.deleteOnce('/api/v2/auth/access_token', { status: 204, }); fetchMock.getOnce('/api/v2/me', { status: 401, }); fetchMock.getOnce('/api/v2/', { _links: { login: { login: '/login', }, }, }); const expectedActions = [ { type: LOGOUT_PENDING, }, { type: LOGOUT_SUCCESS, }, { type: FETCH_INDEXRESOURCES_PENDING, }, ]; const store = mockStore({}); return store.dispatch(logout('/auth/access_token')).then(() => { expect(store.getActions()).toEqual(expectedActions); }); }); it('should dispatch logout success and redirect', () => { fetchMock.deleteOnce('/api/v2/auth/access_token', { status: 200, body: { logoutRedirect: 'http://example.com/cas/logout', }, }); fetchMock.getOnce('/api/v2/me', { status: 401, }); fetchMock.getOnce('/api/v2/', { _links: { login: { login: '/login', }, }, }); window.location.assign = jest.fn(); const expectedActions = [ { type: LOGOUT_PENDING, }, { type: LOGOUT_REDIRECT, }, ]; const store = mockStore({}); return store.dispatch(logout('/auth/access_token')).then(() => { expect(window.location.assign.mock.calls[0][0]).toBe( 'http://example.com/cas/logout', ); expect(store.getActions()).toEqual(expectedActions); }); }); it('should dispatch logout failure', () => { fetchMock.deleteOnce('/api/v2/auth/access_token', { status: 500, }); const store = mockStore({}); return store.dispatch(logout('/auth/access_token')).then(() => { const actions = store.getActions(); expect(actions[0].type).toEqual(LOGOUT_PENDING); expect(actions[1].type).toEqual(LOGOUT_FAILURE); expect(actions[1].payload).toBeDefined(); }); }); }); describe('auth selectors', () => { const error = new Error('yo it failed'); it('should return true if me exist and login Link does not exist', () => { expect( isAuthenticated({ auth: { me, }, indexResources: { links: {}, }, }), ).toBe(true); }); it('should return false if me exist and login Link does exist', () => { expect( isAuthenticated({ auth: { me, }, indexResources: { links: { login: { href: 'login.href', }, }, }, }), ).toBe(false); }); it('should return me', () => { expect( getMe({ auth: { me, }, }), ).toBe(me); }); it('should return undefined, if me is not set', () => { expect(getMe({})).toBeUndefined(); }); it('should return true, if FETCH_ME is pending', () => { expect( isFetchMePending({ pending: { [FETCH_ME]: true, }, }), ).toBe(true); }); it('should return false, if FETCH_ME is not in pending state', () => { expect( isFetchMePending({ pending: {}, }), ).toBe(false); }); it('should return true, if LOGIN is pending', () => { expect( isLoginPending({ pending: { [LOGIN]: true, }, }), ).toBe(true); }); it('should return false, if LOGIN is not in pending state', () => { expect( isLoginPending({ pending: {}, }), ).toBe(false); }); it('should return true, if LOGOUT is pending', () => { expect( isLogoutPending({ pending: { [LOGOUT]: true, }, }), ).toBe(true); }); it('should return false, if LOGOUT is not in pending state', () => { expect( isLogoutPending({ pending: {}, }), ).toBe(false); }); it('should return the error, if failure state is set for FETCH_ME', () => { expect( getFetchMeFailure({ failure: { [FETCH_ME]: error, }, }), ).toBe(error); }); it('should return unknown, if failure state is not set for FETCH_ME', () => { expect(getFetchMeFailure({})).toBeUndefined(); }); it('should return the error, if failure state is set for LOGIN', () => { expect( getLoginFailure({ failure: { [LOGIN]: error, }, }), ).toBe(error); }); it('should return unknown, if failure state is not set for LOGIN', () => { expect(getLoginFailure({})).toBeUndefined(); }); it('should return the error, if failure state is set for LOGOUT', () => { expect( getLogoutFailure({ failure: { [LOGOUT]: error, }, }), ).toBe(error); }); it('should return unknown, if failure state is not set for LOGOUT', () => { expect(getLogoutFailure({})).toBeUndefined(); }); it('should return false, if redirecting is not set', () => { expect(isRedirecting({})).toBe(false); }); it('should return false, if redirecting is false', () => { expect( isRedirecting({ auth: { redirecting: false, }, }), ).toBe(false); }); it('should return true, if redirecting is true', () => { expect( isRedirecting({ auth: { redirecting: true, }, }), ).toBe(true); }); });