mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-27 19:42:11 +10:00
264 lines
9.9 KiB
JavaScript
264 lines
9.9 KiB
JavaScript
import { call, put, take, fork, cancel } from 'redux-saga/effects';
|
|
import { createMockTask } from 'redux-saga/utils';
|
|
import reducer, {
|
|
LOGIN_REQUEST,
|
|
LOGOUT_REQUEST,
|
|
NOTHING_IN_PROGRESS,
|
|
LOGIN_IN_PROGRESS,
|
|
LOGOUT_IN_PROGRESS,
|
|
authInfo,
|
|
authInfoSuccess,
|
|
authInfoError,
|
|
login,
|
|
loginSuccess,
|
|
loginError,
|
|
callLogin,
|
|
logout,
|
|
logoutSuccess,
|
|
logoutError,
|
|
callLogout,
|
|
watchAuth,
|
|
} from './Auth.duck';
|
|
|
|
describe('Auth duck', () => {
|
|
describe('reducer', () => {
|
|
it('should be logged out by default', () => {
|
|
const state = reducer();
|
|
expect(state.authInfoLoaded).toEqual(false);
|
|
expect(state.isAuthenticated).toEqual(false);
|
|
expect(state.authInfoError).toBeNull();
|
|
expect(state.loginError).toBeNull();
|
|
expect(state.logoutError).toBeNull();
|
|
});
|
|
|
|
it('should login successfully', () => {
|
|
const username = 'x@x.x';
|
|
const password = 'pass';
|
|
|
|
let state = reducer();
|
|
state = reducer(state, login(username, password));
|
|
expect(state.isAuthenticated).toEqual(false);
|
|
expect(state.loginError).toBeNull();
|
|
|
|
state = reducer(state, loginSuccess());
|
|
expect(state.isAuthenticated).toEqual(true);
|
|
expect(state.loginError).toBeNull();
|
|
});
|
|
|
|
it('should handle failed login', () => {
|
|
let state = reducer();
|
|
state = reducer(state, login('username', 'pass'));
|
|
expect(state.isAuthenticated).toEqual(false);
|
|
expect(state.loginError).toBeNull();
|
|
|
|
const error = new Error('test error');
|
|
state = reducer(state, loginError(error));
|
|
expect(state.isAuthenticated).toEqual(false);
|
|
expect(state.loginError).toEqual(error);
|
|
});
|
|
|
|
it('should set initial state for unauthenticated users', () => {
|
|
const authInfoLoggedOut = {};
|
|
const initialState = reducer();
|
|
expect(initialState.authInfoLoaded).toEqual(false);
|
|
const state = reducer(initialState, authInfoSuccess(authInfoLoggedOut));
|
|
expect(state.authInfoLoaded).toEqual(true);
|
|
expect(state.isAuthenticated).toEqual(false);
|
|
expect(state.authInfoError).toBeNull();
|
|
});
|
|
|
|
it('should set initial state for anonymous users', () => {
|
|
const authInfoAnonymous = { grantType: 'client_credentials' };
|
|
const initialState = reducer();
|
|
expect(initialState.authInfoLoaded).toEqual(false);
|
|
const state = reducer(initialState, authInfoSuccess(authInfoAnonymous));
|
|
expect(state.authInfoLoaded).toEqual(true);
|
|
expect(state.isAuthenticated).toEqual(false);
|
|
expect(state.authInfoError).toBeNull();
|
|
});
|
|
|
|
it('should set initial state for unauthenticated users', () => {
|
|
const authInfoLoggedIn = { grantType: 'refresh_token' };
|
|
const initialState = reducer();
|
|
expect(initialState.authInfoLoaded).toEqual(false);
|
|
const state = reducer(initialState, authInfoSuccess(authInfoLoggedIn));
|
|
expect(state.authInfoLoaded).toEqual(true);
|
|
expect(state.isAuthenticated).toEqual(true);
|
|
expect(state.authInfoError).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('login worker', () => {
|
|
it('should succeed when API call fulfills', () => {
|
|
const username = 'username';
|
|
const password = 'pass';
|
|
const payload = { username, password };
|
|
const sdk = { login: jest.fn() };
|
|
const loginAction = login(username, password);
|
|
const worker = callLogin(loginAction, sdk);
|
|
expect(worker.next()).toEqual({
|
|
done: false,
|
|
value: call(sdk.login, { username, password }),
|
|
});
|
|
expect(worker.next(payload)).toEqual({ done: false, value: put(loginSuccess()) });
|
|
expect(worker.next().done).toEqual(true);
|
|
expect(sdk.login).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should fail when API call rejects', () => {
|
|
const username = 'username';
|
|
const password = 'pass';
|
|
const sdk = { login: jest.fn() };
|
|
const loginAction = login(username, password);
|
|
const worker = callLogin(loginAction, sdk);
|
|
expect(worker.next()).toEqual({
|
|
done: false,
|
|
value: call(sdk.login, { username, password }),
|
|
});
|
|
const error = new Error('Test login failed');
|
|
expect(worker.throw(error)).toEqual({ done: false, value: put(loginError(error)) });
|
|
expect(worker.next().done).toEqual(true);
|
|
expect(sdk.login).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('logout worker', () => {
|
|
it('should redirect to root after logout', () => {
|
|
const sdk = { logout: jest.fn() };
|
|
const historyPush = jest.fn();
|
|
const action = logout(historyPush);
|
|
const worker = callLogout(action, sdk);
|
|
expect(worker.next()).toEqual({ done: false, value: call(sdk.logout) });
|
|
expect(worker.next()).toEqual({ done: false, value: put(logoutSuccess()) });
|
|
expect(worker.next()).toEqual({ done: false, value: call(historyPush, '/') });
|
|
expect(worker.next().done).toEqual(true);
|
|
expect(sdk.logout).not.toHaveBeenCalled();
|
|
expect(historyPush).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should not redirect if logout fails', () => {
|
|
const sdk = { logout: jest.fn() };
|
|
const historyPush = jest.fn();
|
|
const action = logout(historyPush);
|
|
const worker = callLogout(action, sdk);
|
|
expect(worker.next()).toEqual({ done: false, value: call(sdk.logout) });
|
|
const error = new Error('Test logout error');
|
|
expect(worker.throw(error)).toEqual({ done: false, value: put(logoutError(error)) });
|
|
expect(worker.next().done).toEqual(true);
|
|
expect(sdk.logout).not.toHaveBeenCalled();
|
|
expect(historyPush).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('auth watcher', () => {
|
|
it('calls login', () => {
|
|
const sdk = { login: jest.fn() };
|
|
const watcher = watchAuth(sdk);
|
|
const loginAction = login('username', 'password');
|
|
const takeLoginOrLogout = take([LOGIN_REQUEST, LOGOUT_REQUEST]);
|
|
const forkLogin = fork(callLogin, loginAction, sdk);
|
|
|
|
// The watcher should first take a login or a logout action
|
|
expect(watcher.next().value).toEqual(takeLoginOrLogout);
|
|
|
|
// If we pass it a login action, it should fork the login worker
|
|
expect(watcher.next(loginAction).value).toEqual(forkLogin);
|
|
|
|
// It should continue back at taking a login or a logout action
|
|
expect(watcher.next({}).value).toEqual(takeLoginOrLogout);
|
|
|
|
expect(sdk.login).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('calls logout', () => {
|
|
const sdk = { logout: jest.fn() };
|
|
const historyPush = jest.fn();
|
|
const watcher = watchAuth(sdk);
|
|
const logoutAction = logout(historyPush);
|
|
const takeLoginOrLogout = take([LOGIN_REQUEST, LOGOUT_REQUEST]);
|
|
const forkLogout = fork(callLogout, logoutAction, sdk);
|
|
|
|
// The watcher should first take a login or a logout action
|
|
expect(watcher.next().value).toEqual(takeLoginOrLogout);
|
|
|
|
// If we pass it a logout action, it should fork the logout worker
|
|
expect(watcher.next(logoutAction).value).toEqual(forkLogout);
|
|
|
|
// It should continue back at taking a login or a logout action
|
|
expect(watcher.next().value).toEqual(takeLoginOrLogout);
|
|
|
|
expect(sdk.logout).not.toHaveBeenCalled();
|
|
expect(historyPush).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should cancel login if another login comes', () => {
|
|
const sdk = { login: jest.fn(), logout: jest.fn() };
|
|
const watcher = watchAuth(sdk);
|
|
const loginAction1 = login('username1', 'password1');
|
|
const loginAction2 = login('username2', 'password2');
|
|
const task = createMockTask();
|
|
const takeLoginOrLogout = take([LOGIN_REQUEST, LOGOUT_REQUEST]);
|
|
const forkLogin1 = fork(callLogin, loginAction1, sdk);
|
|
const forkLogin2 = fork(callLogin, loginAction2, sdk);
|
|
const cancelLogin1 = cancel(task);
|
|
|
|
// First take a login or a logout
|
|
expect(watcher.next().value).toEqual(takeLoginOrLogout);
|
|
|
|
// Passing the first login should fork login worker
|
|
expect(watcher.next(loginAction1).value).toEqual(forkLogin1);
|
|
|
|
// Passing in the task to mock the fork results should make the
|
|
// watcher take a login or a logout again
|
|
expect(watcher.next(task).value).toEqual(takeLoginOrLogout);
|
|
|
|
// Passing in another login should cancel the first fork task
|
|
expect(watcher.next(loginAction2).value).toEqual(cancelLogin1);
|
|
|
|
// Then it should fork the second login
|
|
expect(watcher.next().value).toEqual(forkLogin2);
|
|
|
|
// And finally take a login or a logout again
|
|
expect(watcher.next().value).toEqual(takeLoginOrLogout);
|
|
|
|
expect(sdk.login).not.toHaveBeenCalled();
|
|
expect(sdk.logout).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should cancel login if a logout comes', () => {
|
|
const sdk = { login: jest.fn(), logout: jest.fn() };
|
|
const historyPush = jest.fn();
|
|
const watcher = watchAuth(sdk);
|
|
const loginAction = login('username', 'password');
|
|
const logoutAction = logout(historyPush);
|
|
const task = createMockTask();
|
|
const takeLoginOrLogout = take([LOGIN_REQUEST, LOGOUT_REQUEST]);
|
|
const forkLogin = fork(callLogin, loginAction, sdk);
|
|
const forkLogout = fork(callLogout, logoutAction, sdk);
|
|
const cancelLogin = cancel(task);
|
|
|
|
// First take a login or a logout
|
|
expect(watcher.next().value).toEqual(takeLoginOrLogout);
|
|
|
|
// Passing the login should fork login worker
|
|
expect(watcher.next(loginAction).value).toEqual(forkLogin);
|
|
|
|
// Passing in the task to mock the fork results should make the
|
|
// watcher take a login or a logout again
|
|
expect(watcher.next(task).value).toEqual(takeLoginOrLogout);
|
|
|
|
// Passing in logout should cancel the login fork task
|
|
expect(watcher.next(logoutAction).value).toEqual(cancelLogin);
|
|
|
|
// Then it should fork the logout
|
|
expect(watcher.next().value).toEqual(forkLogout);
|
|
|
|
// And finally take a login or a logout again
|
|
expect(watcher.next().value).toEqual(takeLoginOrLogout);
|
|
|
|
expect(sdk.login).not.toHaveBeenCalled();
|
|
expect(sdk.logout).not.toHaveBeenCalled();
|
|
expect(historyPush).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|