Migrate tests for Login to Testing Library and convert showNotification to a thunk (#26848)

* Migrate tests for Login to Testing Library

* Convert showNotification to a thunk
Этот коммит содержится в:
Harrison Healey
2024-04-25 17:24:03 -04:00
коммит произвёл GitHub
родитель 60c15c821f
Коммит 80e67ace86
7 изменённых файлов: 331 добавлений и 393 удалений

Просмотреть файл

@@ -1,118 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/login/Login should match snapshot 1`] = `
<div
className="login-body"
>
<div
className="login-body-content"
>
<Column
message="Please contact your System Administrator to resolve this."
title="This server doesnt have any sign-in methods enabled"
/>
</div>
</div>
`;
exports[`components/login/Login should match snapshot with base login 1`] = `
<div
className="login-body"
>
<div
className="login-body-content"
>
<div
className="login-body-message"
>
<h1
className="login-body-message-title"
>
Log in to your account
</h1>
<p
className="login-body-message-subtitle"
>
Collaborate with your team in real-time
</p>
<div
className="login-body-message-svg"
>
<Svg
width={270}
/>
</div>
</div>
<div
className="login-body-action"
>
<AlternateLink
alternateLinkLabel="Don't have an account?"
alternateLinkPath="/access_problem"
className="login-body-alternate-link"
onClick={[Function]}
/>
<div
className="login-body-card"
>
<div
className="login-body-card-content"
tabIndex={0}
>
<p
className="login-body-card-title"
>
Log in
</p>
<form
onSubmit={[Function]}
>
<div
className="login-body-card-form"
>
<ForwardRef
autoFocus={true}
containerClassName="login-body-card-form-input"
disabled={false}
hasError={false}
inputSize="large"
name="loginId"
onChange={[Function]}
placeholder="Email"
type="text"
value=""
/>
<ForwardRef
className="login-body-card-form-password-input"
disabled={false}
hasError={false}
inputSize="large"
onChange={[Function]}
value=""
/>
<div
className="login-body-card-form-link"
>
<Link
to="/reset_password"
>
Forgot your password?
</Link>
</div>
<SaveButton
btnClass=""
defaultMessage="Log in"
disabled={false}
extraClasses="login-body-card-form-button-submit large"
onClick={[Function]}
saving={false}
savingMessage="Logging in…"
/>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
`;

Просмотреть файл

@@ -1,307 +1,342 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import {createMemoryHistory} from 'history';
import React from 'react';
import {IntlProvider} from 'react-intl';
import {MemoryRouter} from 'react-router-dom';
import type {ClientConfig} from '@mattermost/types/config';
import {RequestStatus} from 'mattermost-redux/constants';
import LocalStorageStore from 'stores/local_storage_store';
import AlertBanner from 'components/alert_banner';
import ExternalLoginButton from 'components/external_login_button/external_login_button';
import Login from 'components/login/login';
import SaveButton from 'components/save_button';
import Input from 'components/widgets/inputs/input/input';
import PasswordInput from 'components/widgets/inputs/password_input/password_input';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import mergeObjects from 'packages/mattermost-redux/test/merge_objects';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import Constants, {WindowSizes} from 'utils/constants';
import type {GlobalState} from 'types/store';
let mockState: GlobalState;
let mockLocation = {pathname: '', search: '', hash: ''};
const mockHistoryReplace = jest.fn();
const mockHistoryPush = jest.fn();
const mockLicense = {IsLicensed: 'false'};
let mockConfig: Partial<ClientConfig>;
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux') as typeof import('react-redux'),
useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState),
useDispatch: jest.fn(() => (action: unknown) => action),
}));
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom') as typeof import('react-router-dom'),
useLocation: () => mockLocation,
useHistory: () => ({
replace: mockHistoryReplace,
push: mockHistoryPush,
}),
}));
jest.mock('mattermost-redux/selectors/entities/general', () => ({
...jest.requireActual('mattermost-redux/selectors/entities/general') as typeof import('mattermost-redux/selectors/entities/general'),
getLicense: () => mockLicense,
getConfig: () => mockConfig,
}));
jest.unmock('react-router-dom');
describe('components/login/Login', () => {
beforeEach(() => {
mockLocation = {pathname: '', search: '', hash: ''};
LocalStorageStore.setWasLoggedIn(false);
mockState = {
entities: {
general: {
config: {},
license: {},
const baseState = {
entities: {
general: {
config: {
EnableLdap: 'false',
EnableSaml: 'false',
EnableSignInWithEmail: 'false',
EnableSignInWithUsername: 'false',
EnableSignUpWithEmail: 'false',
EnableSignUpWithGitLab: 'false',
EnableSignUpWithOffice365: 'false',
EnableSignUpWithGoogle: 'false',
EnableSignUpWithOpenId: 'false',
EnableOpenServer: 'false',
LdapLoginFieldName: '',
GitLabButtonText: '',
GitLabButtonColor: '',
OpenIdButtonText: '',
OpenIdButtonColor: '',
SamlLoginButtonText: '',
EnableCustomBrand: 'false',
CustomBrandText: '',
CustomDescriptionText: '',
SiteName: 'Mattermost',
ExperimentalPrimaryTeam: '',
PasswordEnableForgotLink: 'true',
},
users: {
currentUserId: '',
profiles: {
user1: {
id: 'user1',
roles: '',
},
license: {
IsLicensed: 'false',
},
},
users: {
currentUserId: '',
profiles: {
user1: {
id: 'user1',
roles: '',
},
},
},
teams: {
currentTeamId: 'team1',
teams: {
currentTeamId: 'team1',
teams: {
team1: {
id: 'team1',
name: 'team-1',
displayName: 'Team 1',
},
},
myMembers: {
team1: {roles: 'team_role'},
team1: {
id: 'team1',
name: 'team-1',
displayName: 'Team 1',
},
},
},
requests: {
users: {
logout: {
status: RequestStatus.NOT_STARTED,
},
myMembers: {
team1: {roles: 'team_role'},
},
},
storage: {
initialized: true,
},
views: {
browser: {
windowSize: WindowSizes.DESKTOP_VIEW,
},
requests: {
users: {
logout: {
status: RequestStatus.NOT_STARTED,
},
},
} as unknown as GlobalState;
},
storage: {
initialized: true,
},
views: {
browser: {
windowSize: WindowSizes.DESKTOP_VIEW,
},
},
} as unknown as GlobalState;
mockConfig = {
EnableLdap: 'false',
EnableSaml: 'false',
EnableSignInWithEmail: 'false',
EnableSignInWithUsername: 'false',
EnableSignUpWithEmail: 'false',
EnableSignUpWithGitLab: 'false',
EnableSignUpWithOffice365: 'false',
EnableSignUpWithGoogle: 'false',
EnableSignUpWithOpenId: 'false',
EnableOpenServer: 'false',
LdapLoginFieldName: '',
GitLabButtonText: '',
GitLabButtonColor: '',
OpenIdButtonText: '',
OpenIdButtonColor: '',
SamlLoginButtonText: '',
EnableCustomBrand: 'false',
CustomBrandText: '',
CustomDescriptionText: '',
SiteName: 'Mattermost',
ExperimentalPrimaryTeam: '',
PasswordEnableForgotLink: 'true',
};
beforeEach(() => {
LocalStorageStore.setWasLoggedIn(false);
});
it('should match snapshot', () => {
const wrapper = shallow(
renderWithContext(
<Login/>,
baseState,
);
expect(wrapper).toMatchSnapshot();
expect(screen.queryByText('This server doesnt have any sign-in methods enabled')).toBeVisible();
expect(screen.queryByText('Log in to your account')).not.toBeInTheDocument();
});
it('should match snapshot with base login', () => {
mockConfig.EnableSignInWithEmail = 'true';
const state = mergeObjects(baseState, {
entities: {
general: {
config: {
EnableSignInWithEmail: 'true',
},
},
},
});
const wrapper = shallow(
renderWithContext(
<Login/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(screen.queryByText('This server doesnt have any sign-in methods enabled')).not.toBeInTheDocument();
expect(screen.queryByText('Log in to your account')).toBeVisible();
});
it('should handle session expired', () => {
it('should handle session expired', async () => {
LocalStorageStore.setWasLoggedIn(true);
mockConfig.EnableSignInWithEmail = 'true';
const wrapper = mountWithIntl(
<MemoryRouter><Login/></MemoryRouter>,
const state = mergeObjects(baseState, {
entities: {
general: {
config: {
EnableSignInWithEmail: 'true',
},
},
},
});
renderWithContext(
<Login/>,
state,
);
const alertBanner = wrapper.find(AlertBanner).first();
expect(alertBanner.props().mode).toEqual('warning');
expect(alertBanner.props().title).toEqual('Your session has expired. Please log in again.');
expect(await screen.findByText('Your session has expired. Please log in again.')).toBeVisible();
alertBanner.find('button').first().simulate('click');
screen.getByLabelText('Close').click();
expect(wrapper.find(AlertBanner)).toEqual({});
expect(screen.queryByText('Your session has expired. Please log in again.')).not.toBeInTheDocument();
});
it('should handle initializing when logout status success', () => {
mockState.requests.users.logout.status = RequestStatus.SUCCESS;
const state = mergeObjects(baseState, {
requests: {
users: {
logout: {
status: RequestStatus.SUCCESS,
},
},
},
});
const intlProviderProps = {
defaultLocale: 'en',
locale: 'en',
messages: {},
};
const wrapper = mountWithIntl(
<IntlProvider {...intlProviderProps}>
<MemoryRouter>
<Login/>
</MemoryRouter>
</IntlProvider>,
renderWithContext(
<Login/>,
state,
);
// eslint-disable-next-line react/jsx-key, react/jsx-no-literals
expect(wrapper.contains([<p>Loading</p>])).toEqual(true);
expect(screen.getByText('Loading')).toBeVisible();
});
it('should handle initializing when storage not initalized', () => {
mockState.storage.initialized = false;
const state = mergeObjects(baseState, {
storage: {
initialized: false,
},
});
const intlProviderProps = {
defaultLocale: 'en',
locale: 'en',
messages: {},
};
const wrapper = mountWithIntl(
<IntlProvider {...intlProviderProps}>
<Login/>
</IntlProvider>,
renderWithContext(
<Login/>,
state,
);
// eslint-disable-next-line react/jsx-no-literals, react/jsx-key
expect(wrapper.contains([<p>Loading</p>])).toEqual(true);
expect(screen.getByText('Loading')).toBeVisible();
});
it('should handle suppress session expired notification on sign in change', () => {
mockLocation.search = '?extra=' + Constants.SIGNIN_CHANGE;
it('should handle suppress session expired notification on sign in change', async () => {
LocalStorageStore.setWasLoggedIn(true);
mockConfig.EnableSignInWithEmail = 'true';
const wrapper = mountWithIntl(
<MemoryRouter>
<Login/>
</MemoryRouter>,
const history = createMemoryHistory({
initialEntries: [
{search: '?extra=' + Constants.SIGNIN_CHANGE},
],
});
const state = mergeObjects(baseState, {
entities: {
general: {
config: {
EnableSignInWithEmail: 'true',
},
},
},
});
renderWithContext(
<Login/>,
state,
{
history,
},
);
expect(LocalStorageStore.getWasLoggedIn()).toEqual(false);
const alertBanner = wrapper.find(AlertBanner).first();
expect(alertBanner.props().mode).toEqual('success');
expect(alertBanner.props().title).toEqual('Sign-in method changed successfully');
expect(await screen.findByText('Sign-in method changed successfully')).toBeVisible();
alertBanner.find('button').first().simulate('click');
screen.getByLabelText('Close').click();
expect(wrapper.find(AlertBanner)).toEqual({});
expect(screen.queryByText('Sign-in method changed successfully')).not.toBeInTheDocument();
});
it('should handle discard session expiry notification on failed sign in', () => {
it('should handle discard session expiry notification on sign in attempt', async () => {
LocalStorageStore.setWasLoggedIn(true);
mockConfig.EnableSignInWithEmail = 'true';
const wrapper = mountWithIntl(
<MemoryRouter>
<Login/>
</MemoryRouter>,
const state = mergeObjects(baseState, {
entities: {
general: {
config: {
EnableSignInWithEmail: 'true',
},
},
},
});
renderWithContext(
<Login/>,
state,
);
let alertBanner = wrapper.find(AlertBanner).first();
expect(alertBanner.props().mode).toEqual('warning');
expect(alertBanner.props().title).toEqual('Your session has expired. Please log in again.');
expect(await screen.findByText('Your session has expired. Please log in again.')).toBeVisible();
const input = wrapper.find(Input).first().find('input').first();
input.simulate('change', {target: {value: 'user1'}});
const emailInput = screen.getByLabelText('Email');
userEvent.type(emailInput, 'user1');
const passwordInput = wrapper.find(PasswordInput).first().find('input').first();
passwordInput.simulate('change', {target: {value: 'passw'}});
const passwordInput = screen.getByLabelText('Password');
userEvent.type(passwordInput, 'passw');
const saveButton = wrapper.find(SaveButton).first();
expect(saveButton.props().disabled).toEqual(false);
screen.getByRole('button', {name: 'Log in'}).click();
saveButton.find('button').first().simulate('click');
setTimeout(() => {
alertBanner = wrapper.find(AlertBanner).first();
expect(alertBanner.props().mode).toEqual('danger');
expect(alertBanner.props().title).toEqual('The email/username or password is invalid.');
});
expect(screen.queryByText('Your session has expired. Please log in again.')).not.toBeInTheDocument();
});
it('should handle gitlab text and color props', () => {
mockConfig.EnableSignInWithEmail = 'true';
mockConfig.EnableSignUpWithGitLab = 'true';
mockConfig.GitLabButtonText = 'GitLab 2';
mockConfig.GitLabButtonColor = '#00ff00';
const state = mergeObjects(baseState, {
entities: {
general: {
config: {
EnableSignInWithEmail: 'true',
EnableSignUpWithGitLab: 'true',
GitLabButtonText: 'GitLab 2',
GitLabButtonColor: '#00ff00',
},
},
},
});
const wrapper = shallow(
renderWithContext(
<Login/>,
state,
);
const externalLoginButton = wrapper.find(ExternalLoginButton).first();
expect(externalLoginButton.props().url).toEqual('/oauth/gitlab/login');
expect(externalLoginButton.props().label).toEqual('GitLab 2');
expect(externalLoginButton.props().style).toEqual({color: '#00ff00', borderColor: '#00ff00'});
const button = screen.getByRole('link', {name: 'Gitlab Icon GitLab 2'});
expect(button.style).toMatchObject({
color: 'rgb(0, 255, 0)',
borderColor: '#00ff00',
});
});
it('should handle openid text and color props', () => {
mockConfig.EnableSignInWithEmail = 'true';
mockConfig.EnableSignUpWithOpenId = 'true';
mockConfig.OpenIdButtonText = 'OpenID 2';
mockConfig.OpenIdButtonColor = '#00ff00';
const state = mergeObjects(baseState, {
entities: {
general: {
config: {
EnableSignInWithEmail: 'true',
EnableSignUpWithOpenId: 'true',
OpenIdButtonText: 'OpenID 2',
OpenIdButtonColor: '#00ff00',
},
},
},
});
const wrapper = shallow(
renderWithContext(
<Login/>,
state,
);
const externalLoginButton = wrapper.find(ExternalLoginButton).first();
expect(externalLoginButton.props().url).toEqual('/oauth/openid/login');
expect(externalLoginButton.props().label).toEqual('OpenID 2');
expect(externalLoginButton.props().style).toEqual({color: '#00ff00', borderColor: '#00ff00'});
const button = screen.getByRole('link', {name: 'OpenID Icon OpenID 2'});
expect(button.style).toMatchObject({
color: 'rgb(0, 255, 0)',
borderColor: '#00ff00',
});
});
it('should redirect on login', () => {
mockState.entities.users.currentUserId = 'user1';
it('should redirect on login', async () => {
LocalStorageStore.setWasLoggedIn(true);
mockConfig.EnableSignInWithEmail = 'true';
const redirectPath = '/boards/team/teamID/boardID';
mockLocation.search = '?redirect_to=' + redirectPath;
mountWithIntl(
<MemoryRouter>
<Login/>
</MemoryRouter>,
const history = createMemoryHistory({
initialEntries: [
{search: '?redirect_to=' + redirectPath},
],
});
history.push = jest.fn().mockImplementation(history.push);
const state = mergeObjects(baseState, {
entities: {
general: {
config: {
EnableSignInWithEmail: 'true',
},
},
users: {
currentUserId: 'user1',
},
},
});
renderWithContext(
<Login/>,
state,
{
history,
},
);
expect(mockHistoryPush).toHaveBeenCalledWith(redirectPath);
expect(history.push).toHaveBeenCalledWith(redirectPath);
});
});

Просмотреть файл

@@ -252,7 +252,7 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
const showSessionExpiredNotificationIfNeeded = useCallback(() => {
if (sessionExpired && !closeSessionExpiredNotification!.current) {
showNotification({
dispatch(showNotification({
title: siteName,
body: formatMessage({
id: 'login.session_expired.notification',
@@ -267,7 +267,7 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
closeSessionExpiredNotification.current = undefined;
}
},
}).then(({callback: closeNotification}) => {
})).then(({callback: closeNotification}) => {
closeSessionExpiredNotification.current = closeNotification;
}).catch(() => {
// Ignore the failure to display the notification.