From a3891ca9a5e03ce2400975ecb2c74ff96ea6fbbb Mon Sep 17 00:00:00 2001 From: Khac Vy Date: Mon, 6 Jan 2020 13:28:21 +0700 Subject: [PATCH] Fix: only hide the welcome modal when client accept term and condition (#245) --- src/actions/__tests__/auth.ts | 14 +++++- src/actions/auth.ts | 2 + src/components/pages/__tests__/client.tsx | 20 ++------ src/components/pages/client.tsx | 19 ++----- src/constants/action-types.ts | 2 + src/sagas/__tests__/auth.ts | 60 +++++++++++++++++++++-- src/sagas/auth.ts | 35 ++++++++++--- src/utils/route-dispatcher.ts | 2 + yarn.lock | 2 +- 9 files changed, 112 insertions(+), 44 deletions(-) diff --git a/src/actions/__tests__/auth.ts b/src/actions/__tests__/auth.ts index 05cf7fe1ca..d8c5a47954 100644 --- a/src/actions/__tests__/auth.ts +++ b/src/actions/__tests__/auth.ts @@ -7,7 +7,9 @@ import { authChangeLoginType, authSetRefreshSession, authClear, - toggleFirstLogin + checkFirstTimeLogin, + toggleFirstLogin, + userAcceptTermAndCondition } from '../auth' import ActionTypes from '../../constants/action-types' import { LoginType, LoginSession, LoginMode } from '@reapit/cognito-auth' @@ -62,8 +64,18 @@ describe('auth actions', () => { expect(authClear().data).toEqual(undefined) }) + it('should create a checkFirstTimeLogin action', () => { + expect(checkFirstTimeLogin.type).toEqual(ActionTypes.CHECK_FIRST_TIME_LOGIN) + expect(checkFirstTimeLogin().data).toBeUndefined() + }) + it('should create a toggleFirstLogin action', () => { expect(toggleFirstLogin.type).toEqual(ActionTypes.TOGGLE_FIRST_LOGIN) expect(toggleFirstLogin(true).data).toBeTruthy() }) + + it('should create a userAcceptTermAndCondition action', () => { + expect(userAcceptTermAndCondition.type).toEqual(ActionTypes.USER_ACCEPT_TERM_AND_CONDITION) + expect(userAcceptTermAndCondition().data).toBeUndefined() + }) }) diff --git a/src/actions/auth.ts b/src/actions/auth.ts index 84502f1bdf..a24183725b 100644 --- a/src/actions/auth.ts +++ b/src/actions/auth.ts @@ -10,4 +10,6 @@ export const authLogoutSuccess = actionCreator(ActionTypes.AUTH_LOGOUT_SUC export const authSetRefreshSession = actionCreator(ActionTypes.AUTH_SET_REFRESH_SESSION) export const authChangeLoginType = actionCreator(ActionTypes.AUTH_CHANGE_LOGIN_TYPE) export const authClear = actionCreator(ActionTypes.AUTH_CLEAR) +export const checkFirstTimeLogin = actionCreator(ActionTypes.CHECK_FIRST_TIME_LOGIN) export const toggleFirstLogin = actionCreator(ActionTypes.TOGGLE_FIRST_LOGIN) +export const userAcceptTermAndCondition = actionCreator(ActionTypes.USER_ACCEPT_TERM_AND_CONDITION) diff --git a/src/components/pages/__tests__/client.tsx b/src/components/pages/__tests__/client.tsx index 48fa333e8b..fdf0476b22 100644 --- a/src/components/pages/__tests__/client.tsx +++ b/src/components/pages/__tests__/client.tsx @@ -12,9 +12,7 @@ import { mapDispatchToProps, handleAfterClose, handleOnChange, - handleOnCardClick, - ClientMappedProps, - userAcceptTerm + handleOnCardClick } from '../client' import { addQuery } from '@/utils/client-url-params' import { AppSummaryModel } from '@reapit/foundations-ts-definitions' @@ -57,7 +55,7 @@ const props = (loading: boolean): ClientProps => ({ installationsFormState: 'PENDING', installationsSetFormState: jest.fn(), fetchAppDetail: jest.fn(), - setFirstLogin: jest.fn(), + userAcceptTermAndCondition: jest.fn(), firstLogin: false, ...routerProps }) @@ -105,7 +103,7 @@ describe('Client', () => { setStateViewBrowse: jest.fn(), installationsSetFormState: jest.fn(), fetchAppDetail: jest.fn(), - setFirstLogin: jest.fn(), + userAcceptTermAndCondition: jest.fn(), firstLogin: false, ...routerProps } @@ -136,7 +134,7 @@ describe('Client', () => { setStateViewBrowse: jest.fn(), installationsSetFormState: jest.fn(), fetchAppDetail: jest.fn(), - setFirstLogin: jest.fn(), + userAcceptTermAndCondition: jest.fn(), firstLogin: false, ...routerProps } @@ -231,16 +229,6 @@ describe('Client', () => { }) }) - describe('handle userAcceptTerm', () => { - it('should run correctly', () => { - const mockProps = { - setFirstLogin: jest.fn() - } - userAcceptTerm(mockProps) - expect(mockProps.setFirstLogin).toBeCalled() - }) - }) - describe('show welcome modal when firstLogin', () => { it('should run correctly', () => { const wrapper = mount( diff --git a/src/components/pages/client.tsx b/src/components/pages/client.tsx index 4eea8682e9..45fc88ffa0 100644 --- a/src/components/pages/client.tsx +++ b/src/components/pages/client.tsx @@ -17,13 +17,13 @@ import { appInstallationsSetFormState } from '@/actions/app-installations' import ClientWelcomeMessageModal from '@/components/ui/client-welcome-message' import { addQuery, getParamValueFromPath, hasFilterParams } from '@/utils/client-url-params' import { setAppDetailModalStateBrowse } from '@/actions/app-detail-modal' -import { toggleFirstLogin } from '@/actions/auth' +import { userAcceptTermAndCondition } from '@/actions/auth' export interface ClientMappedActions { setStateViewBrowse: () => void fetchAppDetail: (id: string, clientId: string) => void installationsSetFormState: (formState: FormState) => void - setFirstLogin: (firstLogin: boolean) => void + userAcceptTermAndCondition: () => void } export interface ClientMappedProps { @@ -61,10 +61,6 @@ export const handleInstallationDone = ({ } } -export const userAcceptTerm = ({ setFirstLogin }) => { - setFirstLogin(false) -} - export type ClientProps = ClientMappedActions & ClientMappedProps & RouteComponentProps<{ page?: any }> export const Client: React.FunctionComponent = ({ @@ -78,7 +74,7 @@ export const Client: React.FunctionComponent = ({ installationsFormState, installationsSetFormState, firstLogin = false, - setFirstLogin + userAcceptTermAndCondition }) => { const pageNumber = !isNaN(Number(getParamValueFromPath(location.search, 'page'))) && @@ -143,12 +139,7 @@ export const Client: React.FunctionComponent = ({ )} - { - userAcceptTerm({ setFirstLogin }) - }} - /> + userAcceptTermAndCondition()} /> ) } @@ -165,7 +156,7 @@ export const mapDispatchToProps = (dispatch: any): ClientMappedActions => ({ setStateViewBrowse: () => dispatch(setAppDetailModalStateBrowse()), fetchAppDetail: (id: string, clientId: string) => dispatch(appDetailRequestData({ id, clientId })), installationsSetFormState: (formState: FormState) => dispatch(appInstallationsSetFormState(formState)), - setFirstLogin: (firstLogin: boolean) => dispatch(toggleFirstLogin(firstLogin)) + userAcceptTermAndCondition: () => dispatch(userAcceptTermAndCondition()) }) export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Client)) diff --git a/src/constants/action-types.ts b/src/constants/action-types.ts index 0b83ee0730..aea96e6355 100644 --- a/src/constants/action-types.ts +++ b/src/constants/action-types.ts @@ -23,7 +23,9 @@ const ActionTypes = { AUTH_CHANGE_LOGIN_TYPE: 'AUTH_CHANGE_LOGIN_TYPE', AUTH_SET_REFRESH_SESSION: 'AUTH_SET_REFRESH_SESSION', AUTH_CLEAR: 'AUTH_CLEAR', + CHECK_FIRST_TIME_LOGIN: 'CHECK_FIRST_TIME_LOGIN', TOGGLE_FIRST_LOGIN: 'TOGGLE_FIRST_LOGIN', + USER_ACCEPT_TERM_AND_CONDITION: 'USER_ACCEPT_TERM_AND_CONDITION', // Error actions ERROR_THROWN_COMPONENT: 'ERROR_THROWN_COMPONENT', diff --git a/src/sagas/__tests__/auth.ts b/src/sagas/__tests__/auth.ts index d91c9d29cc..b217747258 100644 --- a/src/sagas/__tests__/auth.ts +++ b/src/sagas/__tests__/auth.ts @@ -1,4 +1,15 @@ -import authSagas, { doLogin, doLogout, loginListen, logoutListen, clearAuthListen, clearAuth } from '../auth' +import authSagas, { + doLogin, + doLogout, + loginListen, + logoutListen, + clearAuthListen, + clearAuth, + setFirstLoginListen, + setFirstTimeLogin, + checkFirstTimeLoginListen, + checkFirstTimeLogin +} from '../auth' import ActionTypes from '../../constants/action-types' import { put, all, takeLatest, call, fork } from '@redux-saga/core/effects' import { authLoginSuccess, authLogoutSuccess, authLoginFailure, toggleFirstLogin } from '../../actions/auth' @@ -44,9 +55,6 @@ describe('login submit', () => { const gen = doLogin(action) expect(gen.next(mockLoginSession).value).toEqual(call(setUserSession, loginParams)) expect(gen.next(mockLoginSession).value).toEqual(put(authLoginSuccess(mockLoginSession))) - expect(gen.next().value).toEqual(call(getCookieString, COOKIE_FIRST_TIME_LOGIN)) - expect(gen.next().value).toEqual(put(toggleFirstLogin(true))) - expect(gen.next().value).toEqual(call(setCookieString, COOKIE_FIRST_TIME_LOGIN, COOKIE_FIRST_TIME_LOGIN)) expect(gen.next().done).toBe(true) }) @@ -98,11 +106,35 @@ describe('auth thunks', () => { }) }) + describe('checkFirstTimeLoginListen', () => { + it('should trigger checkFirstTimeLogin action', () => { + const gen = checkFirstTimeLoginListen() + expect(gen.next().value).toEqual(takeLatest(ActionTypes.CHECK_FIRST_TIME_LOGIN, checkFirstTimeLogin)) + expect(gen.next().done).toBe(true) + }) + }) + + describe('setFirstLoginListen', () => { + it('should trigger setFirstTimeLogin action', () => { + const gen = setFirstLoginListen() + expect(gen.next().value).toEqual(takeLatest(ActionTypes.USER_ACCEPT_TERM_AND_CONDITION, setFirstTimeLogin)) + expect(gen.next().done).toBe(true) + }) + }) + describe('itemSagas', () => { it('should wait for login and logout action get called', () => { const gen = authSagas() - expect(gen.next().value).toEqual(all([fork(loginListen), fork(logoutListen), fork(clearAuthListen)])) + expect(gen.next().value).toEqual( + all([ + fork(loginListen), + fork(logoutListen), + fork(clearAuthListen), + fork(checkFirstTimeLoginListen), + fork(setFirstLoginListen) + ]) + ) expect(gen.next().done).toBe(true) }) }) @@ -115,4 +147,22 @@ describe('auth thunks', () => { expect(gen.next().done).toBe(true) }) }) + + describe('setFirstTimeLogin', () => { + it('should run correctly', () => { + const gen = cloneableGenerator(setFirstTimeLogin)() + expect(gen.next().value).toEqual(call(setCookieString, COOKIE_FIRST_TIME_LOGIN, COOKIE_FIRST_TIME_LOGIN)) + expect(gen.next().value).toEqual(put(toggleFirstLogin(false))) + expect(gen.next().done).toBe(true) + }) + }) + + describe('checkFirstTimeLogin', () => { + it('should run correctly', () => { + const gen = cloneableGenerator(checkFirstTimeLogin)() + expect(gen.next().value).toEqual(call(getCookieString, COOKIE_FIRST_TIME_LOGIN)) + expect(gen.next().value).toEqual(put(toggleFirstLogin(true))) + expect(gen.next().done).toBe(true) + }) + }) }) diff --git a/src/sagas/auth.ts b/src/sagas/auth.ts index 9528352dcd..44686619fc 100644 --- a/src/sagas/auth.ts +++ b/src/sagas/auth.ts @@ -13,12 +13,6 @@ export const doLogin = function*({ data }: Action) { const loginSession: LoginSession | null = yield call(setUserSession, data) if (loginSession) { yield put(authLoginSuccess(loginSession)) - const firstLoginCookie = yield call(getCookieString, COOKIE_FIRST_TIME_LOGIN) - if (!firstLoginCookie) { - // TODO need to get createdDate from api , refer to https://reapit.atlassian.net/browse/CLD-593 - yield put(toggleFirstLogin(true)) - yield call(setCookieString, COOKIE_FIRST_TIME_LOGIN, COOKIE_FIRST_TIME_LOGIN) - } } else { yield put(authLoginFailure()) } @@ -49,6 +43,19 @@ export const clearAuth = function*() { } } +export const checkFirstTimeLogin = function*() { + const firstLoginCookie = yield call(getCookieString, COOKIE_FIRST_TIME_LOGIN) + if (!firstLoginCookie) { + // TODO need to get createdDate from api , refer to https://reapit.atlassian.net/browse/CLD-593 + yield put(toggleFirstLogin(true)) + } +} + +export const setFirstTimeLogin = function*() { + yield call(setCookieString, COOKIE_FIRST_TIME_LOGIN, COOKIE_FIRST_TIME_LOGIN) + yield put(toggleFirstLogin(false)) +} + export const loginListen = function*() { yield takeLatest(ActionTypes.AUTH_LOGIN, doLogin) } @@ -61,8 +68,22 @@ export const clearAuthListen = function*() { yield takeLatest(ActionTypes.AUTH_CLEAR, clearAuth) } +export const checkFirstTimeLoginListen = function*() { + yield takeLatest(ActionTypes.CHECK_FIRST_TIME_LOGIN, checkFirstTimeLogin) +} + +export const setFirstLoginListen = function*() { + yield takeLatest(ActionTypes.USER_ACCEPT_TERM_AND_CONDITION, setFirstTimeLogin) +} + const authSaga = function*() { - yield all([fork(loginListen), fork(logoutListen), fork(clearAuthListen)]) + yield all([ + fork(loginListen), + fork(logoutListen), + fork(clearAuthListen), + fork(checkFirstTimeLoginListen), + fork(setFirstLoginListen) + ]) } export default authSaga diff --git a/src/utils/route-dispatcher.ts b/src/utils/route-dispatcher.ts index 166c77992c..d620a123e0 100644 --- a/src/utils/route-dispatcher.ts +++ b/src/utils/route-dispatcher.ts @@ -13,11 +13,13 @@ import { submitAppRequestData } from '../actions/submit-app' import { getAccessToken } from './session' import { requestDeveloperData } from '@/actions/settings' import { getParamsFromPath } from '@/utils/client-url-params' +import { checkFirstTimeLogin } from '@/actions/auth' const routeDispatcher = async (route: RouteValue, params?: StringMap, search?: string) => { await getAccessToken() switch (route) { case Routes.CLIENT: + store.dispatch(checkFirstTimeLogin()) store.dispatch(clientRequestData(getParamsFromPath(search || ''))) break case Routes.INSTALLED_APPS: diff --git a/yarn.lock b/yarn.lock index 6944443558..7bd759c868 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5800,7 +5800,7 @@ husky@~2.3.0: run-node "^1.0.0" slash "^3.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.5, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==