diff --git a/webapp/channels/src/components/root/root_redirect/index.ts b/webapp/channels/src/components/root/root_redirect/index.ts index d361a82c98ec2..1309717653c09 100644 --- a/webapp/channels/src/components/root/root_redirect/index.ts +++ b/webapp/channels/src/components/root/root_redirect/index.ts @@ -7,6 +7,7 @@ import type {Dispatch} from 'redux'; import {getFirstAdminSetupComplete} from 'mattermost-redux/actions/general'; import {getIsOnboardingFlowEnabled} from 'mattermost-redux/selectors/entities/preferences'; +import {getActiveTeamsList} from 'mattermost-redux/selectors/entities/teams'; import {getCurrentUserId, isCurrentUserSystemAdmin, isFirstAdmin} from 'mattermost-redux/selectors/entities/users'; import type {GlobalState} from 'types/store'; @@ -15,6 +16,7 @@ import RootRedirect from './root_redirect'; function mapStateToProps(state: GlobalState) { const onboardingFlowEnabled = getIsOnboardingFlowEnabled(state); + const teams = getActiveTeamsList(state); let isElegibleForFirstAdmingOnboarding = onboardingFlowEnabled; if (isElegibleForFirstAdmingOnboarding) { isElegibleForFirstAdmingOnboarding = isCurrentUserSystemAdmin(state); @@ -23,6 +25,7 @@ function mapStateToProps(state: GlobalState) { currentUserId: getCurrentUserId(state), isElegibleForFirstAdmingOnboarding, isFirstAdmin: isFirstAdmin(state), + areThereTeams: Boolean(teams.length), }; } diff --git a/webapp/channels/src/components/root/root_redirect/root_redirect.test.tsx b/webapp/channels/src/components/root/root_redirect/root_redirect.test.tsx new file mode 100644 index 0000000000000..8bb5278ec9647 --- /dev/null +++ b/webapp/channels/src/components/root/root_redirect/root_redirect.test.tsx @@ -0,0 +1,173 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {createMemoryHistory} from 'history'; +import React from 'react'; +import type {RouteComponentProps} from 'react-router-dom'; +import {Redirect} from 'react-router-dom'; + +import {getFirstAdminSetupComplete as getFirstAdminSetupCompleteAction} from 'mattermost-redux/actions/general'; + +import * as GlobalActions from 'actions/global_actions'; + +import {renderWithContext, waitFor} from 'tests/react_testing_utils'; + +import RootRedirect from './root_redirect'; +import type {Props} from './root_redirect'; + +jest.mock('actions/global_actions', () => ({ + redirectUserToDefaultTeam: jest.fn(), +})); + +jest.mock('mattermost-redux/actions/general', () => ({ + getFirstAdminSetupComplete: jest.fn(() => + Promise.resolve({ + data: true, + }), + ), +})); + +jest.mock('react-router-dom', () => { + const actual = jest.requireActual('react-router-dom'); + return { + ...actual, + Redirect: jest.fn(() => null), + }; +}); + +describe('components/RootRedirect', () => { + const baseProps: Props = { + currentUserId: '', + isElegibleForFirstAdmingOnboarding: false, + isFirstAdmin: false, + areThereTeams: false, + actions: { + getFirstAdminSetupComplete: getFirstAdminSetupCompleteAction as jest.Mock, + }, + }; + + const defaultProps = { + ...baseProps, + location: { + pathname: '/', + }, + } as Props & RouteComponentProps; + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('should redirect to /login when currentUserId is empty', () => { + renderWithContext(); + + expect(Redirect).toHaveBeenCalledTimes(1); + expect(Redirect).toHaveBeenCalledWith( + expect.objectContaining({ + to: expect.objectContaining({ + pathname: '/login', + }), + }), + {}, + ); + }); + + test('should call GlobalActions.redirectUserToDefaultTeam when user is logged in and not eligible for first admin onboarding', () => { + const props = { + ...defaultProps, + currentUserId: 'test-user-id', + isElegibleForFirstAdmingOnboarding: false, + }; + + renderWithContext(); + + expect(GlobalActions.redirectUserToDefaultTeam).toHaveBeenCalledTimes(1); + }); + + test('should redirect to preparing-workspace when eligible for first admin onboarding and no teams created', async () => { + const history = createMemoryHistory({initialEntries: ['/']}); + const mockHistoryPush = jest.spyOn(history, 'push'); + + const props = { + currentUserId: 'test-user-id', + isElegibleForFirstAdmingOnboarding: true, + isFirstAdmin: true, + areThereTeams: false, + actions: { + getFirstAdminSetupComplete: jest.fn().mockResolvedValue({data: false}), + }, + }; + + renderWithContext(, {}, {history}); + + expect(props.actions.getFirstAdminSetupComplete).toHaveBeenCalledTimes(1); + + await waitFor(() => { + expect(mockHistoryPush).toHaveBeenCalledWith('/preparing-workspace'); + }); + }); + + test('should NOT redirect to preparing-workspace when there are teams created, even if system value for first admin onboarding complete is false', async () => { + const history = createMemoryHistory({initialEntries: ['/']}); + + const props = { + ...defaultProps, + currentUserId: 'test-user-id', + isElegibleForFirstAdmingOnboarding: true, + isFirstAdmin: true, + areThereTeams: true, + actions: { + getFirstAdminSetupComplete: jest.fn().mockResolvedValue({data: false}), + }, + }; + + renderWithContext(, {}, {history}); + + expect(props.actions.getFirstAdminSetupComplete).toHaveBeenCalledTimes(1); + + await waitFor(() => { + expect(GlobalActions.redirectUserToDefaultTeam).toHaveBeenCalledTimes(1); + }); + }); + + test('should redirect to default team when first admin setup is complete', async () => { + const props = { + ...defaultProps, + currentUserId: 'test-user-id', + isElegibleForFirstAdmingOnboarding: true, + isFirstAdmin: true, + areThereTeams: false, + actions: { + getFirstAdminSetupComplete: jest.fn().mockResolvedValue({data: true}), + }, + }; + + renderWithContext(); + + expect(props.actions.getFirstAdminSetupComplete).toHaveBeenCalledTimes(1); + + await waitFor(() => { + expect(GlobalActions.redirectUserToDefaultTeam).toHaveBeenCalledTimes(1); + }); + }); + + test('should redirect to default team when not first admin or teams exist', async () => { + const props = { + ...defaultProps, + currentUserId: 'test-user-id', + isElegibleForFirstAdmingOnboarding: true, + isFirstAdmin: false, + areThereTeams: true, + actions: { + getFirstAdminSetupComplete: jest.fn().mockResolvedValue({data: false}), + }, + }; + + renderWithContext(); + + expect(props.actions.getFirstAdminSetupComplete).toHaveBeenCalledTimes(1); + + await waitFor(() => { + expect(GlobalActions.redirectUserToDefaultTeam).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/webapp/channels/src/components/root/root_redirect/root_redirect.tsx b/webapp/channels/src/components/root/root_redirect/root_redirect.tsx index c528a91c3aeb9..3a1db8624e92c 100644 --- a/webapp/channels/src/components/root/root_redirect/root_redirect.tsx +++ b/webapp/channels/src/components/root/root_redirect/root_redirect.tsx @@ -13,6 +13,7 @@ export type Props = { currentUserId: string; location?: Location; isFirstAdmin: boolean; + areThereTeams: boolean; actions: { getFirstAdminSetupComplete: () => Promise; }; @@ -26,7 +27,7 @@ export default function RootRedirect(props: Props) { if (props.isElegibleForFirstAdmingOnboarding) { props.actions.getFirstAdminSetupComplete().then((firstAdminCompletedSignup) => { // root.tsx ensures admin profiles are eventually loaded - if (firstAdminCompletedSignup.data === false && props.isFirstAdmin) { + if (firstAdminCompletedSignup.data === false && props.isFirstAdmin && !props.areThereTeams) { history.push('/preparing-workspace'); } else { GlobalActions.redirectUserToDefaultTeam();