diff --git a/packages/marketplace/src/actions/__tests__/auth.ts b/packages/marketplace/src/actions/__tests__/auth.ts index 39f45ce4cd..1941a201ba 100644 --- a/packages/marketplace/src/actions/__tests__/auth.ts +++ b/packages/marketplace/src/actions/__tests__/auth.ts @@ -7,9 +7,10 @@ import { authChangeLoginType, authSetRefreshSession, authClear, - checkFirstTimeLogin, - toggleFirstLogin, - userAcceptTermAndCondition, + setInitDeveloperTermsAcceptedStateFromCookie, + setDeveloperTermAcceptedCookieAndState, + setInitClientTermsAcceptedStateFromCookie, + setClientTermAcceptedCookieAndState, } from '../auth' import ActionTypes from '../../constants/action-types' import { LoginType, LoginSession, LoginMode } from '@reapit/cognito-auth' @@ -31,6 +32,28 @@ describe('auth actions', () => { expect(authLoginSuccess(loginSession).data).toEqual(loginSession) }) + it('should create a setInitDeveloperTermsAcceptedStateFromCookie action', () => { + expect(setInitDeveloperTermsAcceptedStateFromCookie.type).toEqual( + ActionTypes.SET_INIT_DEVELOPER_TERMS_ACCEPTED_STATE_FROM_COOKIE, + ) + }) + + it('should create a setDeveloperTermAcceptedCookieAndState action', () => { + expect(setDeveloperTermAcceptedCookieAndState.type).toEqual( + ActionTypes.SET_DEVELOPER_TERM_ACCEPTED_COOKIE_AND_STATE, + ) + }) + + it('should create a setInitClientTermsAcceptedStateFromCookie action', () => { + expect(setInitClientTermsAcceptedStateFromCookie.type).toEqual( + ActionTypes.SET_INIT_CLIENT_TERMS_ACCEPTED_STATE_FROM_COOKIE, + ) + }) + + it('should create a setClientTermAcceptedCookieAndState action', () => { + expect(setClientTermAcceptedCookieAndState.type).toEqual(ActionTypes.SET_CLIENT_TERM_ACCEPTED_COOKIE_AND_STATE) + }) + it('should create a authLoginFailure action', () => { expect(authLoginFailure.type).toEqual(ActionTypes.AUTH_LOGIN_FAILURE) }) @@ -67,19 +90,4 @@ describe('auth actions', () => { expect(authClear.type).toEqual(ActionTypes.AUTH_CLEAR) 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/packages/marketplace/src/actions/auth.ts b/packages/marketplace/src/actions/auth.ts index 4431cc2383..fdd7035211 100644 --- a/packages/marketplace/src/actions/auth.ts +++ b/packages/marketplace/src/actions/auth.ts @@ -10,9 +10,20 @@ 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) -export const checkTermsAcceptedWithCookie = actionCreator(ActionTypes.CHECK_TERM_ACCEPTED_WITH_COOKIE) -export const setTermsAcceptedWithCookie = actionCreator(ActionTypes.SET_TERMS_ACCEPTED_WITH_COOKIE) + +export const setInitDeveloperTermsAcceptedStateFromCookie = actionCreator( + ActionTypes.SET_INIT_DEVELOPER_TERMS_ACCEPTED_STATE_FROM_COOKIE, +) +export const setDeveloperTermAcceptedCookieAndState = actionCreator( + ActionTypes.SET_DEVELOPER_TERM_ACCEPTED_COOKIE_AND_STATE, +) + +export const setInitClientTermsAcceptedStateFromCookie = actionCreator( + ActionTypes.SET_INIT_CLIENT_TERMS_ACCEPTED_STATE_FROM_COOKIE, +) + +export const setClientTermAcceptedCookieAndState = actionCreator( + ActionTypes.SET_CLIENT_TERM_ACCEPTED_COOKIE_AND_STATE, +) + export const setTermsAcceptedState = actionCreator(ActionTypes.SET_TERMS_ACCEPTED_STATE) diff --git a/packages/marketplace/src/components/pages/__tests__/__snapshots__/client-welcome.tsx.snap b/packages/marketplace/src/components/pages/__tests__/__snapshots__/client-welcome.tsx.snap index ddb21c8d46..370f79270b 100644 --- a/packages/marketplace/src/components/pages/__tests__/__snapshots__/client-welcome.tsx.snap +++ b/packages/marketplace/src/components/pages/__tests__/__snapshots__/client-welcome.tsx.snap @@ -236,11 +236,7 @@ exports[`ClientWelcomeMessage should match a snapshot 1`] = ` } heading="Lorem Ipsum" id="step-5" - render={ - - } + render={} subHeading="What is Lorem Ipsum?" /> diff --git a/packages/marketplace/src/components/pages/__tests__/__snapshots__/client.tsx.snap b/packages/marketplace/src/components/pages/__tests__/__snapshots__/client.tsx.snap index 8e90363876..bbfb4f1c4d 100644 --- a/packages/marketplace/src/components/pages/__tests__/__snapshots__/client.tsx.snap +++ b/packages/marketplace/src/components/pages/__tests__/__snapshots__/client.tsx.snap @@ -81,10 +81,6 @@ exports[`Client should match a snapshot when LOADING false 1`] = ` afterClose={[Function]} visible={false} /> - `; @@ -100,10 +96,6 @@ exports[`Client should match a snapshot when LOADING true 1`] = ` afterClose={[Function]} visible={false} /> - `; @@ -188,10 +180,6 @@ exports[`Client should match a snapshot when featured apps is empty [] 1`] = ` afterClose={[Function]} visible={false} /> - `; @@ -276,9 +264,5 @@ exports[`Client should match a snapshot when featured apps is undefined 1`] = ` afterClose={[Function]} visible={false} /> - `; diff --git a/packages/marketplace/src/components/pages/__tests__/__snapshots__/developer-welcome.tsx.snap b/packages/marketplace/src/components/pages/__tests__/__snapshots__/developer-welcome.tsx.snap index 8d20c06bad..2a940838d7 100644 --- a/packages/marketplace/src/components/pages/__tests__/__snapshots__/developer-welcome.tsx.snap +++ b/packages/marketplace/src/components/pages/__tests__/__snapshots__/developer-welcome.tsx.snap @@ -348,11 +348,7 @@ exports[`DeveloperWelcomeMessage should match a snapshot 1`] = ` } heading="On going support" id="step-5" - render={ - - } + render={} subHeading="We’re here to help." /> diff --git a/packages/marketplace/src/components/pages/__tests__/client-welcome.tsx b/packages/marketplace/src/components/pages/__tests__/client-welcome.tsx index 029af90944..9402495d1d 100644 --- a/packages/marketplace/src/components/pages/__tests__/client-welcome.tsx +++ b/packages/marketplace/src/components/pages/__tests__/client-welcome.tsx @@ -1,13 +1,10 @@ +jest.mock('@/utils/cookie') + import * as React from 'react' +import { setCookieString, COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE, COOKIE_MAX_AGE_INFINITY } from '@/utils/cookie' +import MockDate from 'mockdate' import { shallow } from 'enzyme' -import { - ClientWelcomeMessage, - ClientWelcomeMessageProps, - handleUserAccept, - mapDispatchToProps, - Support, - Welcome, -} from '../client-welcome' +import { ClientWelcomeMessage, ClientWelcomeMessageProps, handleUserAccept, Support, Welcome } from '../client-welcome' import routes from '@/constants/routes' import { HelpGuide } from '@reapit/elements' @@ -51,26 +48,29 @@ describe('ClientWelcomeMessage', () => { }) describe('handleUserAccept', () => { - it('should call dispatch', () => { - const mockUserAcceptTermAndCondition = jest.fn() + it('should set COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE cookie', () => { + const TIME_OFFSET = 0 + MockDate.set('2019-12-18T16:30:00', TIME_OFFSET) + const mockHistory = { push: jest.fn(), } - const fn = handleUserAccept(mockUserAcceptTermAndCondition, mockHistory) + const fn = handleUserAccept(mockHistory) fn() - expect(mockUserAcceptTermAndCondition).toBeCalled() - expect(mockHistory.push).toBeCalledWith(routes.INSTALLED_APPS) - }) - }) - describe('mapDispatchToProps', () => { - describe('login', () => { - it('should call dispatch correctly', () => { - const mockDispatch = jest.fn() - const { userAcceptTermAndCondition } = mapDispatchToProps(mockDispatch) - userAcceptTermAndCondition() - expect(mockDispatch).toBeCalled() - }) + expect(setCookieString).toHaveBeenCalledWith( + COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE, + new Date(), + COOKIE_MAX_AGE_INFINITY, + ) + }) + it('should redirect to INSTALLED_APPS', () => { + const mockHistory = { + push: jest.fn(), + } + const fn = handleUserAccept(mockHistory) + fn() + expect(mockHistory.push).toBeCalledWith(routes.INSTALLED_APPS) }) }) }) diff --git a/packages/marketplace/src/components/pages/__tests__/client.tsx b/packages/marketplace/src/components/pages/__tests__/client.tsx index 135e0a9a61..7121d14256 100644 --- a/packages/marketplace/src/components/pages/__tests__/client.tsx +++ b/packages/marketplace/src/components/pages/__tests__/client.tsx @@ -56,8 +56,6 @@ const props = (loading: boolean): ClientProps => ({ installationsFormState: 'PENDING', installationsSetFormState: jest.fn(), fetchAppDetail: jest.fn(), - userAcceptTermAndCondition: jest.fn(), - firstLogin: false, ...routerProps, }) @@ -105,8 +103,6 @@ describe('Client', () => { setStateViewBrowse: jest.fn(), installationsSetFormState: jest.fn(), fetchAppDetail: jest.fn(), - userAcceptTermAndCondition: jest.fn(), - firstLogin: false, ...routerProps, } @@ -137,8 +133,6 @@ describe('Client', () => { setStateViewBrowse: jest.fn(), installationsSetFormState: jest.fn(), fetchAppDetail: jest.fn(), - userAcceptTermAndCondition: jest.fn(), - firstLogin: false, ...routerProps, } @@ -246,7 +240,7 @@ describe('Client', () => { const wrapper = mount( - + , ) diff --git a/packages/marketplace/src/components/pages/__tests__/developer-welcome.tsx b/packages/marketplace/src/components/pages/__tests__/developer-welcome.tsx index 499abe46ce..116bbdcf6d 100644 --- a/packages/marketplace/src/components/pages/__tests__/developer-welcome.tsx +++ b/packages/marketplace/src/components/pages/__tests__/developer-welcome.tsx @@ -1,10 +1,10 @@ import * as React from 'react' +import MockDate from 'mockdate' import { shallow } from 'enzyme' import { DeveloperWelcomeMessage, DeveloperWelcomeMessageProps, handleUserAccept, - mapDispatchToProps, Submitting, Managing, Support, @@ -13,6 +13,9 @@ import { import routes from '@/constants/routes' import { HelpGuide } from '@reapit/elements' +jest.mock('@/utils/cookie') +import { setCookieString, COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, COOKIE_MAX_AGE_INFINITY } from '@/utils/cookie' + const mockProps: DeveloperWelcomeMessageProps = { userAcceptTermAndCondition: jest.fn(), } @@ -73,26 +76,29 @@ describe('DeveloperWelcomeMessage', () => { }) describe('handleUserAccept', () => { - it('should call dispatch', () => { - const mockUserAcceptTermAndCondition = jest.fn() + it('should set COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE cookie', () => { + const TIME_OFFSET = 0 + MockDate.set('2019-12-18T16:30:00', TIME_OFFSET) + const mockHistory = { push: jest.fn(), } - const fn = handleUserAccept(mockUserAcceptTermAndCondition, mockHistory) + const fn = handleUserAccept(mockHistory) fn() - expect(mockUserAcceptTermAndCondition).toBeCalled() - expect(mockHistory.push).toBeCalledWith(routes.DEVELOPER_MY_APPS) - }) - }) - describe('mapDispatchToProps', () => { - describe('login', () => { - it('should call dispatch correctly', () => { - const mockDispatch = jest.fn() - const { userAcceptTermAndCondition } = mapDispatchToProps(mockDispatch) - userAcceptTermAndCondition() - expect(mockDispatch).toBeCalled() - }) + expect(setCookieString).toHaveBeenCalledWith( + COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, + new Date(), + COOKIE_MAX_AGE_INFINITY, + ) + }) + it('should redirect to DEVELOPER_MY_APPS', () => { + const mockHistory = { + push: jest.fn(), + } + const fn = handleUserAccept(mockHistory) + fn() + expect(mockHistory.push).toBeCalledWith(routes.DEVELOPER_MY_APPS) }) }) }) diff --git a/packages/marketplace/src/components/pages/client-welcome.tsx b/packages/marketplace/src/components/pages/client-welcome.tsx index f8c8863be9..28aa0fd76d 100644 --- a/packages/marketplace/src/components/pages/client-welcome.tsx +++ b/packages/marketplace/src/components/pages/client-welcome.tsx @@ -1,19 +1,15 @@ import * as React from 'react' -import { connect } from 'react-redux' import { FlexContainerResponsive, useHelpGuideContext, HelpGuide, Button } from '@reapit/elements' import Routes from '@/constants/routes' import { history } from '@/core/router' -import { userAcceptTermAndCondition } from '@/actions/auth' import styles from '@/styles/pages/developer-welcome.scss?mod' import Step1 from '@/assets/images/step-1.png' import Step2 from '@/assets/images/step-2.png' import Step3 from '@/assets/images/step-3.png' import Step4 from '@/assets/images/step-4.png' import Step5 from '@/assets/images/step-5.png' - -export interface ClientWelcomeMappedActions { - userAcceptTermAndCondition: () => void -} +import { setCookieString, COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE, COOKIE_MAX_AGE_INFINITY } from '@/utils/cookie' +export interface ClientWelcomeMappedActions {} export type ClientWelcomeMessageProps = ClientWelcomeMappedActions @@ -36,7 +32,7 @@ export const Welcome = () => { ) } -export const Support = ({ onAccept }) => { +export const Support = () => { const { goPrev } = useHelpGuideContext() return (
@@ -51,15 +47,15 @@ export const Support = ({ onAccept }) => { -
) } -export const handleUserAccept = (userAcceptTermAndCondition, history) => () => { - userAcceptTermAndCondition() +export const handleUserAccept = history => () => { + setCookieString(COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE, new Date(), COOKIE_MAX_AGE_INFINITY) history.push(Routes.INSTALLED_APPS) } @@ -68,7 +64,7 @@ export const handleChangeSteps = (goTo: () => void) => () => { document.getElementById('developer-welcome')?.scrollIntoView({ behavior: 'smooth' }) } -export const ClientWelcomeMessage: React.FC = ({ userAcceptTermAndCondition }) => { +export const ClientWelcomeMessage: React.FC = () => { return (
@@ -103,7 +99,7 @@ export const ClientWelcomeMessage: React.FC = ({ user /> } + render={} heading="Lorem Ipsum" subHeading="What is Lorem Ipsum?" graphic={step-5} @@ -114,8 +110,4 @@ export const ClientWelcomeMessage: React.FC = ({ user ) } -export const mapDispatchToProps = (dispatch: any): ClientWelcomeMappedActions => ({ - userAcceptTermAndCondition: () => dispatch(userAcceptTermAndCondition()), -}) - -export default connect(null, mapDispatchToProps)(ClientWelcomeMessage) +export default ClientWelcomeMessage diff --git a/packages/marketplace/src/components/pages/client.tsx b/packages/marketplace/src/components/pages/client.tsx index 479e1ef651..a5ee30d3f7 100644 --- a/packages/marketplace/src/components/pages/client.tsx +++ b/packages/marketplace/src/components/pages/client.tsx @@ -14,16 +14,13 @@ import { selectClientId } from '@/selector/client' import { AppSummaryModel } from '@reapit/foundations-ts-definitions' import styles from '@/styles/pages/client.scss?mod' 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 { userAcceptTermAndCondition } from '@/actions/auth' export interface ClientMappedActions { setStateViewBrowse: () => void fetchAppDetail: (id: string, clientId: string) => void installationsSetFormState: (formState: FormState) => void - userAcceptTermAndCondition: () => void } export interface ClientMappedProps { @@ -31,7 +28,6 @@ export interface ClientMappedProps { appDetail: AppDetailState clientId: string installationsFormState: FormState - firstLogin?: boolean } export const handleAfterClose = ({ setVisible }) => () => setVisible(false) @@ -90,8 +86,6 @@ export const Client: React.FunctionComponent = ({ setStateViewBrowse, installationsFormState, installationsSetFormState, - firstLogin = false, - userAcceptTermAndCondition, }) => { const pageNumber = !isNaN(Number(getParamValueFromPath(location.search, 'page'))) && @@ -156,7 +150,6 @@ export const Client: React.FunctionComponent = ({ )}
- userAcceptTermAndCondition()} /> ) } @@ -166,14 +159,12 @@ export const mapStateToProps = (state: ReduxState): ClientMappedProps => ({ appDetail: state.appDetail, clientId: selectClientId(state), installationsFormState: state.installations.formState, - firstLogin: state.auth.firstLogin, }) export const mapDispatchToProps = (dispatch: any): ClientMappedActions => ({ setStateViewBrowse: () => dispatch(setAppDetailModalStateBrowse()), fetchAppDetail: (id: string, clientId: string) => dispatch(appDetailRequestData({ id, clientId })), installationsSetFormState: (formState: FormState) => dispatch(appInstallationsSetFormState(formState)), - userAcceptTermAndCondition: () => dispatch(userAcceptTermAndCondition()), }) export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Client)) diff --git a/packages/marketplace/src/components/pages/developer-welcome.tsx b/packages/marketplace/src/components/pages/developer-welcome.tsx index 2e4875a5a9..d6257f76f2 100644 --- a/packages/marketplace/src/components/pages/developer-welcome.tsx +++ b/packages/marketplace/src/components/pages/developer-welcome.tsx @@ -1,9 +1,7 @@ import * as React from 'react' -import { connect } from 'react-redux' import { FlexContainerResponsive, useHelpGuideContext, HelpGuide, Button } from '@reapit/elements' import Routes from '@/constants/routes' import { history } from '@/core/router' -import { userAcceptTermAndCondition } from '@/actions/auth' import styles from '@/styles/pages/developer-welcome.scss?mod' import Step1 from '@/assets/images/step-1.png' import Step2 from '@/assets/images/step-2.png' @@ -11,10 +9,9 @@ import Step3 from '@/assets/images/step-3.png' import Step4 from '@/assets/images/step-4.png' import Step5 from '@/assets/images/step-5.png' import linkStyles from '@/styles/elements/link.scss?mod' +import { setCookieString, COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, COOKIE_MAX_AGE_INFINITY } from '@/utils/cookie' -export interface DevelopeWelcomeMappedActions { - userAcceptTermAndCondition: () => void -} +export interface DevelopeWelcomeMappedActions {} export type DeveloperWelcomeMessageProps = DevelopeWelcomeMappedActions @@ -151,7 +148,7 @@ export const Managing = () => { ) } -export const Support = ({ onAccept }) => { +export const Support = () => { const { goPrev } = useHelpGuideContext() return (
@@ -170,15 +167,16 @@ export const Support = ({ onAccept }) => { -
) } -export const handleUserAccept = (userAcceptTermAndCondition, history) => () => { - userAcceptTermAndCondition() +export const handleUserAccept = history => () => { + setCookieString(COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, new Date(), COOKIE_MAX_AGE_INFINITY) + history.push(Routes.DEVELOPER_MY_APPS) } @@ -187,7 +185,7 @@ export const handleChangeSteps = (goTo: () => void) => () => { document.getElementById('developer-welcome')?.scrollIntoView({ behavior: 'smooth' }) } -export const DeveloperWelcomeMessage: React.FC = ({ userAcceptTermAndCondition }) => { +export const DeveloperWelcomeMessage: React.FC = () => { return (
@@ -222,7 +220,7 @@ export const DeveloperWelcomeMessage: React.FC = ( /> } + render={} heading="On going support" subHeading="We’re here to help." graphic={step-5} @@ -233,8 +231,4 @@ export const DeveloperWelcomeMessage: React.FC = ( ) } -export const mapDispatchToProps = (dispatch: any): DevelopeWelcomeMappedActions => ({ - userAcceptTermAndCondition: () => dispatch(userAcceptTermAndCondition()), -}) - -export default connect(null, mapDispatchToProps)(DeveloperWelcomeMessage) +export default DeveloperWelcomeMessage diff --git a/packages/marketplace/src/components/pages/login.tsx b/packages/marketplace/src/components/pages/login.tsx index dcf71639a7..57ec748fa6 100644 --- a/packages/marketplace/src/components/pages/login.tsx +++ b/packages/marketplace/src/components/pages/login.tsx @@ -11,7 +11,11 @@ import loginStyles from '@/styles/pages/login.scss?mod' import { withRouter, RouteComponentProps } from 'react-router' import logoImage from '@/assets/images/reapit-graphic.jpg' import { getLoginTypeByPath, getDefaultPathByLoginType, getDefaultRouteByLoginType } from '@/utils/auth-route' -import { getCookieString, COOKIE_FIRST_TIME_LOGIN } from '@/utils/cookie' +import { + getCookieString, + COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, + COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE, +} from '@/utils/cookie' import connectImage from '@/assets/images/reapit-connect.png' import { showNotificationMessage } from '@/actions/notification-message' import messages from '@/constants/messages' @@ -56,7 +60,8 @@ export const Login: React.FunctionComponent = (props: LoginProps) => const currentLoginType = getLoginTypeByPath(location.pathname) authChangeLoginType(currentLoginType) - const firstLoginCookie = getCookieString(COOKIE_FIRST_TIME_LOGIN) + const isDeveloperFirstTimeLoginComplete = Boolean(getCookieString(COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE)) + const isClientFirstTimeLoginComplete = Boolean(getCookieString(COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE)) if (isPasswordChanged) { showNotiAfterPasswordChanged() @@ -64,11 +69,20 @@ export const Login: React.FunctionComponent = (props: LoginProps) => } if (hasSession) { - const redirectRoute = getDefaultPathByLoginType(loginType, firstLoginCookie) + const redirectRoute = getDefaultPathByLoginType({ + loginType, + isDeveloperFirstTimeLoginComplete, + isClientFirstTimeLoginComplete, + }) return } const loginHandler = () => { - const redirectRoute = getDefaultRouteByLoginType(loginType, firstLoginCookie) + const redirectRoute = getDefaultRouteByLoginType({ + loginType, + isDeveloperFirstTimeLoginComplete, + isClientFirstTimeLoginComplete, + }) + redirectToLogin(window.reapit.config.cognitoClientId, redirectRoute, loginType) } diff --git a/packages/marketplace/src/constants/action-types.ts b/packages/marketplace/src/constants/action-types.ts index 7f69eea7fb..7e8d7857e2 100644 --- a/packages/marketplace/src/constants/action-types.ts +++ b/packages/marketplace/src/constants/action-types.ts @@ -23,11 +23,14 @@ 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', - CHECK_TERM_ACCEPTED_WITH_COOKIE: 'CHECK_TERM_ACCEPTED_WITH_COOKIE', - SET_TERMS_ACCEPTED_WITH_COOKIE: 'SET_TERMS_ACCEPTED_WITH_COOKIE', + + SET_INIT_DEVELOPER_TERMS_ACCEPTED_STATE_FROM_COOKIE: 'SET_INIT_DEVELOPER_TERMS_ACCEPTED_STATE_FROM_COOKIE', + SET_DEVELOPER_TERM_ACCEPTED_COOKIE_AND_STATE: 'SET_DEVELOPER_TERM_ACCEPTED_COOKIE_AND_STATE', + + SET_INIT_CLIENT_TERMS_ACCEPTED_STATE_FROM_COOKIE: 'SET_INIT_CLIENT_TERMS_ACCEPTED_STATE_FROM_COOKIE', + SET_CLIENT_TERM_ACCEPTED_COOKIE_AND_STATE: 'SET_CLIENT_TERM_ACCEPTED_COOKIE_AND_STATE', + SET_TERMS_ACCEPTED_STATE: 'SET_TERMS_ACCEPTED_STATE', // Error actions diff --git a/packages/marketplace/src/core/__tests__/__snapshots__/router.tsx.snap b/packages/marketplace/src/core/__tests__/__snapshots__/router.tsx.snap index 3722cd3ffe..b9c1b085a9 100644 --- a/packages/marketplace/src/core/__tests__/__snapshots__/router.tsx.snap +++ b/packages/marketplace/src/core/__tests__/__snapshots__/router.tsx.snap @@ -137,7 +137,6 @@ exports[`Router should match a snapshot 1`] = ` } } exact={true} - fetcher={true} path="/client/welcome" /> { it('should match a snapshot for desktop login', () => { expect(shallow()).toMatchSnapshot() }) + + describe('mapDispatchToProps', () => { + it('should call dispatch correctly', () => { + const mockDispatch = jest.fn() + + const { + setDeveloperTermAcceptedCookieAndState, + setRefreshSession, + setInitDeveloperTermsAcceptedStateFromCookie, + setClientTermAcceptedCookieAndState, + } = mapDispatchToProps(mockDispatch) + + setDeveloperTermAcceptedCookieAndState() + expect(mockDispatch).toHaveBeenCalledTimes(1) + + setRefreshSession(({ key: 'val' } as unknown) as RefreshParams) + expect(mockDispatch).toHaveBeenCalledTimes(2) + + setInitDeveloperTermsAcceptedStateFromCookie() + expect(mockDispatch).toHaveBeenCalledTimes(3) + + setClientTermAcceptedCookieAndState() + expect(mockDispatch).toHaveBeenCalledTimes(4) + }) + }) + + describe('mapStateToProps', () => { + it('return correct state', () => { + const mockedState = { + auth: { + refreshSession: { + mode: 'DESKTOP', + }, + isTermAccepted: true, + loginType: 'DEVELOPER', + }, + } + + expect(mapStateToProps((mockedState as unknown) as ReduxState)).toMatchObject({ + isDesktopMode: true, + hasSession: true, + isTermAccepted: true, + loginType: 'DEVELOPER', + }) + }) + describe('should return correct value of key "isDesktopMode"', () => { + it('return true if "state?.auth?.refreshSession?.mode" === "DESKTOP"', () => { + const mockedState = { + auth: { + refreshSession: { + mode: 'DESKTOP', + }, + }, + } + + expect(mapStateToProps((mockedState as unknown) as ReduxState)).toMatchObject({ + isDesktopMode: true, + }) + }) + it('return true if "state?.auth?.refreshSession?.mode" !== "DESKTOP"', () => { + const mockedState = { + auth: { + refreshSession: { + mode: 'NOT DESKTOP', + }, + }, + } + + expect(mapStateToProps((mockedState as unknown) as ReduxState)).toMatchObject({ + isDesktopMode: false, + }) + }) + }) + describe('should return correct value of key "hasSession"', () => { + it('return true if "state.auth.loginSession" truthy', () => { + const mockedState = { + auth: { + loginSession: 'truthy', + }, + } + + expect(mapStateToProps((mockedState as unknown) as ReduxState)).toMatchObject({ + hasSession: true, + }) + }) + it('return true if "state.auth.refreshSession" truthy', () => { + const mockedState = { + auth: { + refreshSession: 'truthy', + }, + } + + expect(mapStateToProps((mockedState as unknown) as ReduxState)).toMatchObject({ + hasSession: true, + }) + }) + it('return true if both "state.auth.refreshSession" and "state.auth.refreshSession" is falsy', () => { + const mockedState = { + auth: {}, + } + + expect(mapStateToProps((mockedState as unknown) as ReduxState)).toMatchObject({ + hasSession: false, + }) + }) + }) + }) }) diff --git a/packages/marketplace/src/core/private-route-wrapper.tsx b/packages/marketplace/src/core/private-route-wrapper.tsx index e962ae2435..17adaf32ef 100644 --- a/packages/marketplace/src/core/private-route-wrapper.tsx +++ b/packages/marketplace/src/core/private-route-wrapper.tsx @@ -1,4 +1,5 @@ import * as React from 'react' +import ClientWelcomeMessageModal from '@/components/ui/client-welcome-message' import { RouteComponentProps } from 'react-router-dom' import { connect } from 'react-redux' import { ReduxState } from 'src/types/core' @@ -9,15 +10,28 @@ import { LoginType, RefreshParams, getTokenFromQueryString, redirectToOAuth } fr import { Dispatch } from 'redux' import { withRouter, Redirect } from 'react-router' import { getDefaultRouteByLoginType, getAuthRouteByLoginType } from '@/utils/auth-route' -import { authSetRefreshSession, checkTermsAcceptedWithCookie, setTermsAcceptedWithCookie } from '../actions/auth' -import { getCookieString, COOKIE_FIRST_TIME_LOGIN } from '@/utils/cookie' +import { + authSetRefreshSession, + setInitDeveloperTermsAcceptedStateFromCookie, + setInitClientTermsAcceptedStateFromCookie, + setClientTermAcceptedCookieAndState, + setDeveloperTermAcceptedCookieAndState, +} from '../actions/auth' + +import { + getCookieString, + COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, + COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE, +} from '@/utils/cookie' const { Suspense } = React export interface PrivateRouteWrapperConnectActions { setRefreshSession: (refreshParams: RefreshParams) => void - checkTermsAcceptedWithCookie: () => void - setTermsAcceptedWithCookie: () => void + setInitClientTermsAcceptedStateFromCookie: () => void + setInitDeveloperTermsAcceptedStateFromCookie: () => void + setClientTermAcceptedCookieAndState: () => void + setDeveloperTermAcceptedCookieAndState: () => void } export interface PrivateRouteWrapperConnectState { @@ -39,31 +53,50 @@ export const PrivateRouteWrapper: React.FunctionComponent { - React.useEffect(checkTermsAcceptedWithCookie, []) + React.useEffect(() => { + setInitClientTermsAcceptedStateFromCookie() + setInitDeveloperTermsAcceptedStateFromCookie() + }, []) const params = new URLSearchParams(location.search) const state = params.get('state') const type = state && state.includes('ADMIN') ? 'ADMIN' : state && state.includes('CLIENT') ? 'CLIENT' : loginType - const firstLoginCookie = getCookieString(COOKIE_FIRST_TIME_LOGIN) - const route = getDefaultRouteByLoginType(type, firstLoginCookie) + const isDeveloperFirstTimeLoginComplete = Boolean(getCookieString(COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE)) + const isClientFirstTimeLoginComplete = Boolean(getCookieString(COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE)) + + const route = getDefaultRouteByLoginType({ + isClientFirstTimeLoginComplete, + isDeveloperFirstTimeLoginComplete, + /** + * loginType default in reducer = DEVELOPER + * when redirecting back to app after login + * -> use state in url param (this was setted by cognito) + * -> save the valid type to logintype + * further works will use loginType in reducer if param state isn't specificied in url param + */ + loginType: type || loginType, + }) + const cognitoClientId = window.reapit.config.cognitoClientId const refreshParams = getTokenFromQueryString(location.search, cognitoClientId, type, route) - if (type && location.pathname === '/') { - const path = getAuthRouteByLoginType(type) - return - } - if (refreshParams && !hasSession) { setRefreshSession(refreshParams) return null } + if (type && location.pathname === '/') { + const path = getAuthRouteByLoginType(type || loginType) + return + } + if (!hasSession) { redirectToOAuth(cognitoClientId, route, type) return null @@ -72,11 +105,17 @@ export const PrivateRouteWrapper: React.FunctionComponent - + {loginType === 'DEVELOPER' && ( + + )} + + {loginType === 'CLIENT' && ( + + )} ({ +export const mapStateToProps = (state: ReduxState): PrivateRouteWrapperConnectState => ({ hasSession: !!state.auth.loginSession || !!state.auth.refreshSession, - loginType: state.auth.loginType, + loginType: state?.auth?.loginType, isDesktopMode: state?.auth?.refreshSession?.mode === 'DESKTOP', - isTermAccepted: state.auth.isTermAccepted, + isTermAccepted: state?.auth?.isTermAccepted, }) -const mapDispatchToProps = (dispatch: Dispatch): PrivateRouteWrapperConnectActions => ({ +export const mapDispatchToProps = (dispatch: Dispatch): PrivateRouteWrapperConnectActions => ({ setRefreshSession: refreshParams => dispatch(authSetRefreshSession(refreshParams)), - checkTermsAcceptedWithCookie: () => { - dispatch(checkTermsAcceptedWithCookie()) + setInitDeveloperTermsAcceptedStateFromCookie: () => { + dispatch(setInitDeveloperTermsAcceptedStateFromCookie()) + }, + setInitClientTermsAcceptedStateFromCookie: () => { + dispatch(setInitClientTermsAcceptedStateFromCookie()) }, - setTermsAcceptedWithCookie: () => dispatch(setTermsAcceptedWithCookie(true)), + setClientTermAcceptedCookieAndState: () => dispatch(setClientTermAcceptedCookieAndState(true)), + setDeveloperTermAcceptedCookieAndState: () => dispatch(setDeveloperTermAcceptedCookieAndState(true)), }) export default withRouter(connect(mapStateToProps, mapDispatchToProps)(PrivateRouteWrapper)) diff --git a/packages/marketplace/src/core/router.tsx b/packages/marketplace/src/core/router.tsx index 68f7d8590a..d5627d6cd3 100644 --- a/packages/marketplace/src/core/router.tsx +++ b/packages/marketplace/src/core/router.tsx @@ -50,7 +50,7 @@ const Router = () => { - + diff --git a/packages/marketplace/src/reducers/__tests__/auth.ts b/packages/marketplace/src/reducers/__tests__/auth.ts index 6f06427082..fe20fdaba4 100644 --- a/packages/marketplace/src/reducers/__tests__/auth.ts +++ b/packages/marketplace/src/reducers/__tests__/auth.ts @@ -63,12 +63,6 @@ describe('auth reducer', () => { expect(newState.refreshSession).toEqual(data) }) - it('should set the firstLogin state when the TOGGLE_FIRST_LOGIN is called', () => { - const data = true - const newState = authReducer(undefined, { type: ActionTypes.TOGGLE_FIRST_LOGIN as ActionType, data }) - expect(newState.firstLogin).toEqual(data) - }) - it('should set isTermAccepted state when SET_TERMS_ACCEPTED_STATE is called ', () => { const newState = authReducer(undefined, { type: ActionTypes.SET_TERMS_ACCEPTED_STATE as ActionType, diff --git a/packages/marketplace/src/reducers/auth.ts b/packages/marketplace/src/reducers/auth.ts index 9b312098d5..a6db67a722 100644 --- a/packages/marketplace/src/reducers/auth.ts +++ b/packages/marketplace/src/reducers/auth.ts @@ -7,7 +7,6 @@ import { authLogoutSuccess, authSetRefreshSession, authChangeLoginType, - toggleFirstLogin, setTermsAcceptedState, } from '../actions/auth' import { LoginSession, RefreshParams, LoginType, getSessionCookie } from '@reapit/cognito-auth' @@ -15,10 +14,17 @@ import { COOKIE_SESSION_KEY_MARKETPLACE } from '../constants/api' export interface AuthState { error: boolean + firstLogin?: boolean + loginType: LoginType loginSession: LoginSession | null refreshSession: RefreshParams | null + + /** + * state for + * developer, client welcome modal + */ isTermAccepted: boolean } @@ -27,8 +33,8 @@ export const defaultState = (): AuthState => { return { error: false, loginSession: null, - firstLogin: false, - isTermAccepted: true, + + isTermAccepted: false, loginType: refreshSession ? refreshSession.loginType : 'DEVELOPER', refreshSession, } @@ -50,13 +56,6 @@ const authReducer = (state: AuthState = defaultState(), action: Action): Au } } - if (isType(action, toggleFirstLogin)) { - return { - ...state, - firstLogin: action.data, - } - } - if (isType(action, authLoginFailure)) { return { ...state, diff --git a/packages/marketplace/src/sagas/__tests__/auth.ts b/packages/marketplace/src/sagas/__tests__/auth.ts index d4b8c3fbed..953e95b23e 100644 --- a/packages/marketplace/src/sagas/__tests__/auth.ts +++ b/packages/marketplace/src/sagas/__tests__/auth.ts @@ -1,4 +1,3 @@ -import MockDate from 'mockdate' import { select, put, all, takeLatest, call, fork } from '@redux-saga/core/effects' import { setUserSession, removeSession, LoginParams, LoginSession, redirectToLogout } from '@reapit/cognito-auth' import { Action } from '@/types/core' @@ -6,9 +5,9 @@ import { cloneableGenerator } from '@redux-saga/testing-utils' import { getCookieString, setCookieString, - COOKIE_FIRST_TIME_LOGIN, - COOKIE_TERMS_ACCEPTED, COOKIE_MAX_AGE_INFINITY, + COOKIE_DEVELOPER_TERMS_ACCEPTED, + COOKIE_CLIENT_TERMS_ACCEPTED, } from '@/utils/cookie' import authSagas, { doLogin, @@ -17,23 +16,17 @@ import authSagas, { logoutListen, clearAuthListen, clearAuth, - setFirstLoginListen, - setFirstTimeLogin, - checkFirstTimeLoginListen, - checkFirstTimeLogin, - setTermsAcceptedWithCookieHelper, - checkTermsAcceptedWithCookieHelper, - checkTermsAcceptedListen, - setTermsAcceptedListen, + setInitDeveloperTermsAcceptedStateFromCookie, + setDeveloperTermAcceptedCookieAndState, + setInitDeveloperTermsAcceptedStateFromCookieListen, + setDeveloperTermAcceptedCookieAndStateListen, + setClientTermAcceptedCookieAndState, + setInitClientTermsAcceptedStateFromCookieListen, + setInitClientTermsAcceptedStateFromCookie, + setClientTermAcceptedCookieAndStateListen, } from '../auth' import ActionTypes from '../../constants/action-types' -import { - authLoginSuccess, - authLogoutSuccess, - authLoginFailure, - toggleFirstLogin, - setTermsAcceptedState, -} from '../../actions/auth' +import { authLoginSuccess, authLogoutSuccess, authLoginFailure, setTermsAcceptedState } from '../../actions/auth' import Routes from '../../constants/routes' import { ActionType } from '../../types/core' import { COOKIE_SESSION_KEY_MARKETPLACE } from '../../constants/api' @@ -152,22 +145,6 @@ 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() @@ -177,10 +154,10 @@ describe('auth thunks', () => { fork(loginListen), fork(logoutListen), fork(clearAuthListen), - fork(checkFirstTimeLoginListen), - fork(setFirstLoginListen), - fork(checkTermsAcceptedListen), - fork(setTermsAcceptedListen), + fork(setInitDeveloperTermsAcceptedStateFromCookieListen), + fork(setDeveloperTermAcceptedCookieAndStateListen), + fork(setInitClientTermsAcceptedStateFromCookieListen), + fork(setClientTermAcceptedCookieAndStateListen), ]), ) expect(gen.next().done).toBe(true) @@ -196,77 +173,119 @@ describe('auth thunks', () => { }) }) - describe('setFirstTimeLogin', () => { - it('should run correctly', () => { - const TIME_OFFSET = 0 - MockDate.set('2019-12-18T16:30:00', TIME_OFFSET) - const gen = cloneableGenerator(setFirstTimeLogin)() + describe('setInitClientTermsAcceptedStateFromCookie', () => { + it('should run correctly with developer login type', () => { + const gen = cloneableGenerator(setInitClientTermsAcceptedStateFromCookie)() + expect(gen.next().value).toEqual(select(selectLoginType)) + expect(gen.next('CLIENT').value).toEqual(call(getCookieString, COOKIE_CLIENT_TERMS_ACCEPTED)) + expect(gen.next('2019-12-18T16:30:00').value).toEqual(put(setTermsAcceptedState(true))) + expect(gen.next().done).toBe(true) + }) + + it('should run correctly with other login type', () => { + const gen = cloneableGenerator(setInitClientTermsAcceptedStateFromCookie)() + expect(gen.next().value).toEqual(select(selectLoginType)) + expect(gen.next('DEVELOPER').done).toBe(true) + }) + }) + + describe('setClientTermAcceptedCookieAndState', () => { + it('should run correctly with true', () => { + const gen = cloneableGenerator(setClientTermAcceptedCookieAndState)({ data: true }) expect(gen.next().value).toEqual( - call(setCookieString, COOKIE_FIRST_TIME_LOGIN, new Date(), COOKIE_MAX_AGE_INFINITY), + call(setCookieString, COOKIE_CLIENT_TERMS_ACCEPTED, new Date(), COOKIE_MAX_AGE_INFINITY), ) - expect(gen.next().value).toEqual(put(toggleFirstLogin(false))) + expect(gen.next().value).toEqual(put(setTermsAcceptedState(true))) expect(gen.next().done).toBe(true) - MockDate.reset() }) - }) - 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))) + it('should run correctly with false', () => { + const gen = cloneableGenerator(setClientTermAcceptedCookieAndState)({ data: false }) + expect(gen.next().value).toEqual(put(setTermsAcceptedState(false))) expect(gen.next().done).toBe(true) }) }) - describe('checkTermsAcceptedWithCookie', () => { + describe('setInitDeveloperTermsAcceptedStateFromCookie', () => { it('should run correctly with developer login type', () => { - const gen = cloneableGenerator(checkTermsAcceptedWithCookieHelper)() + const gen = cloneableGenerator(setInitDeveloperTermsAcceptedStateFromCookie)() expect(gen.next().value).toEqual(select(selectLoginType)) - expect(gen.next('DEVELOPER').value).toEqual(call(getCookieString, COOKIE_TERMS_ACCEPTED)) + expect(gen.next('DEVELOPER').value).toEqual(call(getCookieString, COOKIE_DEVELOPER_TERMS_ACCEPTED)) expect(gen.next('2019-12-18T16:30:00').value).toEqual(put(setTermsAcceptedState(true))) expect(gen.next().done).toBe(true) }) it('should run correctly with other login type', () => { - const gen = cloneableGenerator(checkTermsAcceptedWithCookieHelper)() + const gen = cloneableGenerator(setInitDeveloperTermsAcceptedStateFromCookie)() expect(gen.next().value).toEqual(select(selectLoginType)) expect(gen.next('CLIENT').done).toBe(true) }) }) - describe('setTermsAcceptedWithCookieHelper', () => { + describe('setDeveloperTermAcceptedCookieAndState', () => { it('should run correctly with true', () => { - const gen = cloneableGenerator(setTermsAcceptedWithCookieHelper)({ data: true }) + const gen = cloneableGenerator(setDeveloperTermAcceptedCookieAndState)({ data: true }) expect(gen.next().value).toEqual( - call(setCookieString, COOKIE_TERMS_ACCEPTED, new Date(), COOKIE_MAX_AGE_INFINITY), + call(setCookieString, COOKIE_DEVELOPER_TERMS_ACCEPTED, new Date(), COOKIE_MAX_AGE_INFINITY), ) expect(gen.next().value).toEqual(put(setTermsAcceptedState(true))) expect(gen.next().done).toBe(true) }) it('should run correctly with false', () => { - const gen = cloneableGenerator(setTermsAcceptedWithCookieHelper)({ data: false }) + const gen = cloneableGenerator(setDeveloperTermAcceptedCookieAndState)({ data: false }) expect(gen.next().value).toEqual(put(setTermsAcceptedState(false))) expect(gen.next().done).toBe(true) }) }) - describe('checkTermsAcceptedListen', () => { + describe('setInitDeveloperTermsAcceptedStateFromCookieListen', () => { + it('should run correctly', () => { + const gen = setInitDeveloperTermsAcceptedStateFromCookieListen() + expect(gen.next().value).toEqual( + takeLatest( + ActionTypes.SET_INIT_DEVELOPER_TERMS_ACCEPTED_STATE_FROM_COOKIE, + setInitDeveloperTermsAcceptedStateFromCookie, + ), + ) + expect(gen.next().done).toBe(true) + }) + }) + + describe('setDeveloperTermAcceptedCookieAndStateListen', () => { it('should run correctly', () => { - const gen = checkTermsAcceptedListen() + const gen = setDeveloperTermAcceptedCookieAndStateListen() expect(gen.next().value).toEqual( - takeLatest(ActionTypes.CHECK_TERM_ACCEPTED_WITH_COOKIE, checkTermsAcceptedWithCookieHelper), + takeLatest>( + ActionTypes.SET_DEVELOPER_TERM_ACCEPTED_COOKIE_AND_STATE, + setDeveloperTermAcceptedCookieAndState, + ), ) expect(gen.next().done).toBe(true) }) }) - describe('setTermsAcceptedListen', () => { + describe('setInitClientTermsAcceptedStateFromCookieListen', () => { it('should run correctly', () => { - const gen = setTermsAcceptedListen() + const gen = setInitDeveloperTermsAcceptedStateFromCookieListen() expect(gen.next().value).toEqual( - takeLatest>(ActionTypes.SET_TERMS_ACCEPTED_WITH_COOKIE, setTermsAcceptedWithCookieHelper), + takeLatest( + ActionTypes.SET_INIT_DEVELOPER_TERMS_ACCEPTED_STATE_FROM_COOKIE, + setInitDeveloperTermsAcceptedStateFromCookie, + ), + ) + expect(gen.next().done).toBe(true) + }) + }) + + describe('setClientTermAcceptedCookieAndStateListen', () => { + it('should run correctly', () => { + const gen = setDeveloperTermAcceptedCookieAndStateListen() + expect(gen.next().value).toEqual( + takeLatest>( + ActionTypes.SET_DEVELOPER_TERM_ACCEPTED_COOKIE_AND_STATE, + setDeveloperTermAcceptedCookieAndState, + ), ) expect(gen.next().done).toBe(true) }) diff --git a/packages/marketplace/src/sagas/auth.ts b/packages/marketplace/src/sagas/auth.ts index 0483c307da..2e84a8760a 100644 --- a/packages/marketplace/src/sagas/auth.ts +++ b/packages/marketplace/src/sagas/auth.ts @@ -1,12 +1,6 @@ import { takeLatest, put, call, all, fork, select } from '@redux-saga/core/effects' import ActionTypes from '../constants/action-types' -import { - authLoginSuccess, - authLoginFailure, - authLogoutSuccess, - toggleFirstLogin, - setTermsAcceptedState, -} from '../actions/auth' +import { authLoginSuccess, authLoginFailure, authLogoutSuccess, setTermsAcceptedState } from '../actions/auth' import { Action } from '@/types/core.ts' import { LoginSession, LoginParams, setUserSession, removeSession, redirectToLogout } from '@reapit/cognito-auth' import store from '../core/store' @@ -14,8 +8,8 @@ import { getAuthRouteByLoginType } from '@/utils/auth-route' import { getCookieString, setCookieString, - COOKIE_FIRST_TIME_LOGIN, - COOKIE_TERMS_ACCEPTED, + COOKIE_DEVELOPER_TERMS_ACCEPTED, + COOKIE_CLIENT_TERMS_ACCEPTED, COOKIE_MAX_AGE_INFINITY, } from '@/utils/cookie' import { COOKIE_SESSION_KEY_MARKETPLACE } from '../constants/api' @@ -57,31 +51,34 @@ 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 setInitDeveloperTermsAcceptedStateFromCookie = function*() { + const loginType = yield select(selectLoginType) + + if (loginType === 'DEVELOPER') { + const isTermAccepted = yield call(getCookieString, COOKIE_DEVELOPER_TERMS_ACCEPTED) + yield put(setTermsAcceptedState(!!isTermAccepted)) } } -export const setFirstTimeLogin = function*() { - yield call(setCookieString, COOKIE_FIRST_TIME_LOGIN, new Date(), COOKIE_MAX_AGE_INFINITY) - yield put(toggleFirstLogin(false)) +export const setDeveloperTermAcceptedCookieAndState = function*({ data: isAccepted }) { + if (isAccepted) { + yield call(setCookieString, COOKIE_DEVELOPER_TERMS_ACCEPTED, new Date(), COOKIE_MAX_AGE_INFINITY) + } + yield put(setTermsAcceptedState(isAccepted)) } -export const checkTermsAcceptedWithCookieHelper = function*() { +export const setInitClientTermsAcceptedStateFromCookie = function*() { const loginType = yield select(selectLoginType) // for now only check when login as developer - if (loginType === 'DEVELOPER') { - const isTermAccepted = yield call(getCookieString, COOKIE_TERMS_ACCEPTED) + if (loginType === 'CLIENT') { + const isTermAccepted = yield call(getCookieString, COOKIE_CLIENT_TERMS_ACCEPTED) yield put(setTermsAcceptedState(!!isTermAccepted)) } } -export const setTermsAcceptedWithCookieHelper = function*({ data: isAccepted }) { +export const setClientTermAcceptedCookieAndState = function*({ data: isAccepted }) { if (isAccepted) { - yield call(setCookieString, COOKIE_TERMS_ACCEPTED, new Date(), COOKIE_MAX_AGE_INFINITY) + yield call(setCookieString, COOKIE_CLIENT_TERMS_ACCEPTED, new Date(), COOKIE_MAX_AGE_INFINITY) } yield put(setTermsAcceptedState(isAccepted)) } @@ -98,20 +95,32 @@ export const clearAuthListen = function*() { yield takeLatest(ActionTypes.AUTH_CLEAR, clearAuth) } -export const checkFirstTimeLoginListen = function*() { - yield takeLatest(ActionTypes.CHECK_FIRST_TIME_LOGIN, checkFirstTimeLogin) +export const setInitDeveloperTermsAcceptedStateFromCookieListen = function*() { + yield takeLatest( + ActionTypes.SET_INIT_DEVELOPER_TERMS_ACCEPTED_STATE_FROM_COOKIE, + setInitDeveloperTermsAcceptedStateFromCookie, + ) } -export const setFirstLoginListen = function*() { - yield takeLatest(ActionTypes.USER_ACCEPT_TERM_AND_CONDITION, setFirstTimeLogin) +export const setDeveloperTermAcceptedCookieAndStateListen = function*() { + yield takeLatest>( + ActionTypes.SET_DEVELOPER_TERM_ACCEPTED_COOKIE_AND_STATE, + setDeveloperTermAcceptedCookieAndState, + ) } -export const checkTermsAcceptedListen = function*() { - yield takeLatest(ActionTypes.CHECK_TERM_ACCEPTED_WITH_COOKIE, checkTermsAcceptedWithCookieHelper) +export const setInitClientTermsAcceptedStateFromCookieListen = function*() { + yield takeLatest( + ActionTypes.SET_INIT_CLIENT_TERMS_ACCEPTED_STATE_FROM_COOKIE, + setInitClientTermsAcceptedStateFromCookie, + ) } -export const setTermsAcceptedListen = function*() { - yield takeLatest>(ActionTypes.SET_TERMS_ACCEPTED_WITH_COOKIE, setTermsAcceptedWithCookieHelper) +export const setClientTermAcceptedCookieAndStateListen = function*() { + yield takeLatest>( + ActionTypes.SET_CLIENT_TERM_ACCEPTED_COOKIE_AND_STATE, + setClientTermAcceptedCookieAndState, + ) } const authSaga = function*() { @@ -119,10 +128,10 @@ const authSaga = function*() { fork(loginListen), fork(logoutListen), fork(clearAuthListen), - fork(checkFirstTimeLoginListen), - fork(setFirstLoginListen), - fork(checkTermsAcceptedListen), - fork(setTermsAcceptedListen), + fork(setInitDeveloperTermsAcceptedStateFromCookieListen), + fork(setDeveloperTermAcceptedCookieAndStateListen), + fork(setInitClientTermsAcceptedStateFromCookieListen), + fork(setClientTermAcceptedCookieAndStateListen), ]) } diff --git a/packages/marketplace/src/utils/__tests__/auth-route.ts b/packages/marketplace/src/utils/__tests__/auth-route.ts index b01fb25187..a0ed07f109 100644 --- a/packages/marketplace/src/utils/__tests__/auth-route.ts +++ b/packages/marketplace/src/utils/__tests__/auth-route.ts @@ -1,8 +1,8 @@ import { getAuthRouteByLoginType, getLoginTypeByPath, - getDefaultPathByLoginType, getDefaultRouteByLoginType, + getDefaultPathByLoginType, } from '../auth-route' import Routes from '../../constants/routes' @@ -23,42 +23,93 @@ describe('getLoginTypeByPath', () => { }) describe('getDefaultRouteByLoginType', () => { - it('should return correct route', () => { - const firstLoginCookie = '1' - expect(getDefaultRouteByLoginType('ADMIN', firstLoginCookie)).toEqual( - `${window.location.origin}${Routes.ADMIN_APPROVALS}`, - ) - expect(getDefaultRouteByLoginType('DEVELOPER', firstLoginCookie)).toEqual( - `${window.location.origin}${Routes.DEVELOPER_MY_APPS}`, - ) - expect(getDefaultRouteByLoginType('CLIENT', firstLoginCookie)).toEqual( - `${window.location.origin}${Routes.INSTALLED_APPS}`, - ) + it('should return origin url + Routes.ADMIN_APPROVALS if loginType = ADMIN ', () => { + expect( + getDefaultRouteByLoginType({ + loginType: 'ADMIN', + }), + ).toEqual(`${window.location.origin}${Routes.ADMIN_APPROVALS}`) + }) + + /* eslint-disable max-len */ + it('should return origin url + Routes.DEVELOPER_MY_APPS if loginType = DEVELOPER and isDeveloperFirstTimeLoginComplete is true', () => { + expect( + getDefaultRouteByLoginType({ + loginType: 'DEVELOPER', + isDeveloperFirstTimeLoginComplete: true, + }), + ).toEqual(`${window.location.origin}${Routes.DEVELOPER_MY_APPS}`) + }) + + it('should return origin url + Routes.DEVELOPER_WELCOME if loginType = DEVELOPER and isDeveloperFirstTimeLoginComplete is false', () => { + expect( + getDefaultRouteByLoginType({ + loginType: 'DEVELOPER', + }), + ).toEqual(`${window.location.origin}${Routes.DEVELOPER_WELCOME}`) }) - it('should return correct route DEVELOPER when not found firstLoginCookie', () => { - const firstLoginCookie = undefined + it('should return origin url + Routes.INSTALLED_APPS if loginType = CLIENT and isClientFirstTimeLoginComplete is true', () => { + expect( + getDefaultRouteByLoginType({ + loginType: 'CLIENT', + isClientFirstTimeLoginComplete: true, + }), + ).toEqual(`${window.location.origin}${Routes.INSTALLED_APPS}`) + }) - expect(getDefaultRouteByLoginType('DEVELOPER', firstLoginCookie)).toEqual( - `${window.location.origin}${Routes.DEVELOPER_WELCOME}`, - ) - expect(getDefaultRouteByLoginType('CLIENT', firstLoginCookie)).toEqual( - `${window.location.origin}${Routes.CLIENT_WELCOME}`, - ) + it('should return origin url + Routes.CLIENT_WELCOME if loginType = CLIENT and isClientFirstTimeLoginComplete is false', () => { + expect( + getDefaultRouteByLoginType({ + loginType: 'CLIENT', + }), + ).toEqual(`${window.location.origin}${Routes.CLIENT_WELCOME}`) }) + /* eslint-enable*/ }) describe('getDefaultPathByLoginType', () => { - it('should return correct path', () => { - const firstLoginCookie = '1' - expect(getDefaultPathByLoginType('ADMIN', firstLoginCookie)).toEqual(Routes.ADMIN_APPROVALS) - expect(getDefaultPathByLoginType('DEVELOPER', firstLoginCookie)).toEqual(Routes.DEVELOPER_MY_APPS) - expect(getDefaultPathByLoginType('CLIENT', firstLoginCookie)).toEqual(Routes.INSTALLED_APPS) + it('should return Routes.ADMIN_APPROVALS if loginType = ADMIN ', () => { + expect( + getDefaultPathByLoginType({ + loginType: 'ADMIN', + }), + ).toEqual(Routes.ADMIN_APPROVALS) + }) + + /* eslint-disable max-len */ + it('should return Routes.DEVELOPER_MY_APPS if loginType = DEVELOPER and isDeveloperFirstTimeLoginComplete is true', () => { + expect( + getDefaultPathByLoginType({ + loginType: 'DEVELOPER', + isDeveloperFirstTimeLoginComplete: true, + }), + ).toEqual(Routes.DEVELOPER_MY_APPS) + }) + + it('should return Routes.DEVELOPER_WELCOME if loginType = DEVELOPER and isDeveloperFirstTimeLoginComplete is false', () => { + expect( + getDefaultPathByLoginType({ + loginType: 'DEVELOPER', + }), + ).toEqual(Routes.DEVELOPER_WELCOME) + }) + + it('should return Routes.INSTALLED_APPS if loginType = CLIENT and isClientFirstTimeLoginComplete is true', () => { + expect( + getDefaultPathByLoginType({ + loginType: 'CLIENT', + isClientFirstTimeLoginComplete: true, + }), + ).toEqual(Routes.INSTALLED_APPS) }) - it('should return correct path DEVELOPER when not found firstLoginCookie', () => { - const firstLoginCookie = undefined - expect(getDefaultPathByLoginType('DEVELOPER', firstLoginCookie)).toEqual(Routes.DEVELOPER_WELCOME) - expect(getDefaultPathByLoginType('CLIENT', firstLoginCookie)).toEqual(Routes.CLIENT_WELCOME) + it('should return Routes.CLIENT_WELCOME if loginType = CLIENT and isClientFirstTimeLoginComplete is false', () => { + expect( + getDefaultPathByLoginType({ + loginType: 'CLIENT', + }), + ).toEqual(Routes.CLIENT_WELCOME) }) + /* eslint-enable*/ }) diff --git a/packages/marketplace/src/utils/__tests__/cookie.ts b/packages/marketplace/src/utils/__tests__/cookie.ts index 0533ccea70..e39d364309 100644 --- a/packages/marketplace/src/utils/__tests__/cookie.ts +++ b/packages/marketplace/src/utils/__tests__/cookie.ts @@ -1,15 +1,17 @@ import hardtack from 'hardtack' -import { getCookieString, setCookieString, COOKIE_FIRST_TIME_LOGIN } from '../cookie' +import { getCookieString, setCookieString, COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE } from '../cookie' describe('cookie utils', () => { describe('getCookieString', () => { it('should get a session from the cookie if it exists', () => { - document.cookie = `${COOKIE_FIRST_TIME_LOGIN}=${COOKIE_FIRST_TIME_LOGIN}` - expect(getCookieString(COOKIE_FIRST_TIME_LOGIN)).toEqual(COOKIE_FIRST_TIME_LOGIN) + document.cookie = `${COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE}=${COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE}` + expect(getCookieString(COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE)).toEqual( + COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, + ) }) it('should return null if no cookie', () => { - document.cookie = `${COOKIE_FIRST_TIME_LOGIN}=` - expect(getCookieString(COOKIE_FIRST_TIME_LOGIN)).toEqual('') + document.cookie = `${COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE}=` + expect(getCookieString(COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE)).toEqual('') }) }) @@ -18,8 +20,8 @@ describe('cookie utils', () => { const validHost = 'https://something.reapit.cloud' hardtack.set = jest.fn() const now = new Date() - setCookieString(COOKIE_FIRST_TIME_LOGIN, now, 10, validHost) - expect(hardtack.set).toHaveBeenCalledWith(COOKIE_FIRST_TIME_LOGIN, now.toString(), { + setCookieString(COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, now, 10, validHost) + expect(hardtack.set).toHaveBeenCalledWith(COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, now.toString(), { path: '/', domain: '.reapit.cloud', samesite: 'lax', @@ -30,7 +32,7 @@ describe('cookie utils', () => { const inValidHost = 'https://something.com' hardtack.set = jest.fn() const now = new Date() - setCookieString(COOKIE_FIRST_TIME_LOGIN, now, 10, inValidHost) + setCookieString(COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE, now, 10, inValidHost) expect(hardtack.set).not.toHaveBeenCalled() }) }) diff --git a/packages/marketplace/src/utils/auth-route.ts b/packages/marketplace/src/utils/auth-route.ts index 6a2121b0e3..04a9691f49 100644 --- a/packages/marketplace/src/utils/auth-route.ts +++ b/packages/marketplace/src/utils/auth-route.ts @@ -12,16 +12,24 @@ export function getAuthRouteByLoginType(loginType: LoginType) { } } -export function getDefaultRouteByLoginType(loginType: LoginType, firstLoginCookie?: string | undefined) { +export function getDefaultRouteByLoginType({ + loginType, + isDeveloperFirstTimeLoginComplete, + isClientFirstTimeLoginComplete, +}: { + loginType: LoginType + isDeveloperFirstTimeLoginComplete?: boolean + isClientFirstTimeLoginComplete?: boolean +}) { switch (loginType) { case 'ADMIN': return `${window.location.origin}${Routes.ADMIN_APPROVALS}` case 'DEVELOPER': - return !firstLoginCookie + return !isDeveloperFirstTimeLoginComplete ? `${window.location.origin}${Routes.DEVELOPER_WELCOME}` : `${window.location.origin}${Routes.DEVELOPER_MY_APPS}` default: - return !firstLoginCookie + return !isClientFirstTimeLoginComplete ? `${window.location.origin}${Routes.CLIENT_WELCOME}` : `${window.location.origin}${Routes.INSTALLED_APPS}` } @@ -38,13 +46,21 @@ export function getLoginTypeByPath(path: string) { } } -export function getDefaultPathByLoginType(loginType: LoginType, firstLoginCookie?: string) { +export function getDefaultPathByLoginType({ + loginType, + isDeveloperFirstTimeLoginComplete, + isClientFirstTimeLoginComplete, +}: { + loginType: LoginType + isDeveloperFirstTimeLoginComplete?: boolean + isClientFirstTimeLoginComplete?: boolean +}) { switch (loginType) { case 'ADMIN': return Routes.ADMIN_APPROVALS case 'DEVELOPER': - return !firstLoginCookie ? Routes.DEVELOPER_WELCOME : Routes.DEVELOPER_MY_APPS + return !isDeveloperFirstTimeLoginComplete ? Routes.DEVELOPER_WELCOME : Routes.DEVELOPER_MY_APPS default: - return !firstLoginCookie ? Routes.CLIENT_WELCOME : Routes.INSTALLED_APPS + return !isClientFirstTimeLoginComplete ? Routes.CLIENT_WELCOME : Routes.INSTALLED_APPS } } diff --git a/packages/marketplace/src/utils/cookie.ts b/packages/marketplace/src/utils/cookie.ts index a7ee8c8c70..c9f106b1a3 100644 --- a/packages/marketplace/src/utils/cookie.ts +++ b/packages/marketplace/src/utils/cookie.ts @@ -2,12 +2,15 @@ import hardtack from 'hardtack' // cookie 10 years export const COOKIE_MAX_AGE_INFINITY = 60 * 60 * 24 * 365 * 10 -export const COOKIE_FIRST_TIME_LOGIN = 'reapit-first-login-complete' -export const COOKIE_FIRST_TIME_LOGIN_DEVELOPER = 'reapit-developer-first-login-complete' -export const COOKIE_TERMS_ACCEPTED = 'reapit-term-accepted' export const COOKIE_FIRST_SUBMIT = 'reapit-read-docs' +export const COOKIE_DEVELOPER_FIRST_TIME_LOGIN_COMPLETE = 'reapit-developer-first-login-complete' +export const COOKIE_DEVELOPER_TERMS_ACCEPTED = 'reapit-developer-term-accepted' + +export const COOKIE_CLIENT_FIRST_TIME_LOGIN_COMPLETE = 'reapit-client-first-login-complete' +export const COOKIE_CLIENT_TERMS_ACCEPTED = 'reapit-client-term-accepted' + export const COOKIE_DOMAIN_WHITELIST = ['.reapit.cloud', 'localhost'] export const getCookieString = (key: string): string => { diff --git a/packages/marketplace/src/utils/route-dispatcher.ts b/packages/marketplace/src/utils/route-dispatcher.ts index 84b77c11ba..70b85f716b 100644 --- a/packages/marketplace/src/utils/route-dispatcher.ts +++ b/packages/marketplace/src/utils/route-dispatcher.ts @@ -14,7 +14,6 @@ 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' import { adminAppsRequestData } from '@/actions/admin-apps' import { appInstallationsRequestData } from '@/actions/app-installations' import { selectClientId } from '@/selector/client' @@ -27,7 +26,6 @@ const routeDispatcher = async (route: RouteValue, params?: StringMap, search?: s switch (route) { case Routes.CLIENT: - store.dispatch(checkFirstTimeLogin()) store.dispatch(clientRequestData(getParamsFromPath(search || ''))) break case Routes.INSTALLED_APPS: