--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import userEvent from '@testing-library/user-event';
+import * as React from 'react';
+import { byLabelText, byRole, byText } from 'testing-library-selector';
+import { changePassword } from '../../../api/users';
+import { mockAppState } from '../../../helpers/testMocks';
+import { renderApp } from '../../../helpers/testReactTestingUtils';
+import { AppState } from '../../../types/appstate';
+import ChangeAdminPasswordApp from '../ChangeAdminPasswordApp';
+import { DEFAULT_ADMIN_PASSWORD } from '../constants';
+
+jest.mock('../../../api/users', () => ({
+ changePassword: jest.fn().mockResolvedValue({})
+}));
+
+const ui = {
+ updateButton: byRole('button', { name: 'update_verb' }),
+ passwordInput: byLabelText('users.change_admin_password.form.password', {
+ selector: 'input',
+ exact: false
+ }),
+ confirmInput: byLabelText('users.change_admin_password.form.confirm', {
+ selector: 'input',
+ exact: false
+ }),
+ unauthorizedMessage: byText('unauthorized.message'),
+ defaultPasswordWarningMessage: byText(
+ 'users.change_admin_password.form.cannot_use_default_password'
+ )
+};
+
+it('should disallow change when not an admin', () => {
+ renderChangeAdminPasswordApp(mockAppState({ instanceUsesDefaultAdminCredentials: true }));
+ expect(ui.unauthorizedMessage.get()).toBeInTheDocument();
+});
+
+it('should allow changing password when using the default admin password', async () => {
+ const user = userEvent.setup();
+ renderChangeAdminPasswordApp(
+ mockAppState({ instanceUsesDefaultAdminCredentials: true, canAdmin: true })
+ );
+ expect(ui.updateButton.get()).toBeDisabled();
+ await user.type(ui.passwordInput.get(), 'password');
+
+ expect(ui.updateButton.get()).toBeDisabled();
+ await user.type(ui.confirmInput.get(), 'pass');
+ expect(ui.updateButton.get()).toBeDisabled();
+ await user.keyboard('word');
+ expect(ui.updateButton.get()).toBeEnabled();
+ await user.click(ui.updateButton.get());
+ expect(changePassword).toHaveBeenCalledWith({
+ login: 'admin',
+ password: 'password'
+ });
+});
+
+it('should not allow to submit the default password', async () => {
+ const user = userEvent.setup();
+ renderChangeAdminPasswordApp(
+ mockAppState({ instanceUsesDefaultAdminCredentials: true, canAdmin: true })
+ );
+
+ await user.type(ui.passwordInput.get(), DEFAULT_ADMIN_PASSWORD);
+ await user.type(ui.confirmInput.get(), DEFAULT_ADMIN_PASSWORD);
+
+ expect(ui.updateButton.get()).toBeDisabled();
+ expect(ui.defaultPasswordWarningMessage.get()).toBeInTheDocument();
+});
+
+function renderChangeAdminPasswordApp(appState?: AppState) {
+ return renderApp('admin/change_admin_password', <ChangeAdminPasswordApp />, { appState });
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { changePassword } from '../../../api/users';
-import { mockAppState, mockLocation } from '../../../helpers/testMocks';
-import { waitAndUpdate } from '../../../helpers/testUtils';
-import { ChangeAdminPasswordApp } from '../ChangeAdminPasswordApp';
-import { DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_PASSWORD } from '../constants';
-
-jest.mock('../../../api/users', () => ({
- changePassword: jest.fn().mockResolvedValue(null)
-}));
-
-beforeEach(jest.clearAllMocks);
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('default');
- expect(
- shallowRender({ appState: mockAppState({ instanceUsesDefaultAdminCredentials: undefined }) })
- ).toMatchSnapshot('admin is not using the default password');
-});
-
-it('should correctly handle input changes', () => {
- const wrapper = shallowRender();
-
- // Set different values; should not allow submission.
- wrapper.instance().handlePasswordChange('new pass');
- wrapper.instance().handleConfirmPasswordChange('confirm pass');
-
- expect(wrapper.state().passwordValue).toBe('new pass');
- expect(wrapper.state().confirmPasswordValue).toBe('confirm pass');
- expect(wrapper.state().canSubmit).toBe(false);
-
- // Set the same values; should allow submission.
- wrapper.instance().handleConfirmPasswordChange('new pass');
- expect(wrapper.state().canSubmit).toBe(true);
-
- // Set the default admin password; should not allow submission.
- wrapper.instance().handlePasswordChange(DEFAULT_ADMIN_PASSWORD);
- expect(wrapper.state().canSubmit).toBe(false);
-});
-
-it('should correctly update the password', async () => {
- (changePassword as jest.Mock).mockResolvedValueOnce(null).mockRejectedValueOnce(null);
- const wrapper = shallowRender();
- wrapper.setState({ canSubmit: false, passwordValue: 'new pass' });
-
- wrapper.instance().handleSubmit();
- expect(wrapper.state().submitting).toBe(false);
- expect(changePassword).not.toBeCalled();
-
- wrapper.setState({ canSubmit: true });
- wrapper.instance().handleSubmit();
- expect(wrapper.state().submitting).toBe(true);
-
- await waitAndUpdate(wrapper);
-
- expect(wrapper.state().submitting).toBe(false);
- expect(wrapper.state().success).toBe(true);
- expect(changePassword).toBeCalledWith({
- login: DEFAULT_ADMIN_LOGIN,
- password: 'new pass'
- });
-
- wrapper.instance().handleSubmit();
- expect(wrapper.state().submitting).toBe(true);
-
- await waitAndUpdate(wrapper);
-
- expect(wrapper.state().submitting).toBe(false);
- expect(wrapper.state().success).toBe(false);
-});
-
-function shallowRender(props: Partial<ChangeAdminPasswordApp['props']> = {}) {
- return shallow<ChangeAdminPasswordApp>(
- <ChangeAdminPasswordApp
- appState={mockAppState({ canAdmin: true, instanceUsesDefaultAdminCredentials: true })}
- location={mockLocation()}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockLocation } from '../../../helpers/testMocks';
-import { change, submit } from '../../../helpers/testUtils';
-import ChangeAdminPasswordAppRenderer, {
- ChangeAdminPasswordAppRendererProps
-} from '../ChangeAdminPasswordAppRenderer';
-import { DEFAULT_ADMIN_PASSWORD } from '../constants';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ canAdmin: false })).toMatchSnapshot('access denied');
- expect(shallowRender({ canSubmit: false })).toMatchSnapshot('cannot submit');
- expect(shallowRender({ submitting: true })).toMatchSnapshot('submitting');
- expect(
- shallowRender({
- passwordValue: DEFAULT_ADMIN_PASSWORD,
- confirmPasswordValue: DEFAULT_ADMIN_PASSWORD
- })
- ).toMatchSnapshot('trying to use default admin password');
- expect(shallowRender({ success: true })).toMatchSnapshot('success');
-});
-
-it('should correctly react to input changes', () => {
- const onConfirmPasswordChange = jest.fn();
- const onPasswordChange = jest.fn();
- const wrapper = shallowRender({ onConfirmPasswordChange, onPasswordChange });
-
- change(wrapper.find('#user-password'), 'new pass');
- change(wrapper.find('#confirm-user-password'), 'confirm pass');
- expect(onPasswordChange).toBeCalledWith('new pass');
- expect(onConfirmPasswordChange).toBeCalledWith('confirm pass');
-});
-
-it('should correctly submit the form', () => {
- const onSubmit = jest.fn();
- const wrapper = shallowRender({ onSubmit });
-
- submit(wrapper.find('form'));
- expect(onSubmit).toBeCalled();
-});
-
-function shallowRender(props: Partial<ChangeAdminPasswordAppRendererProps> = {}) {
- return shallow<ChangeAdminPasswordAppRendererProps>(
- <ChangeAdminPasswordAppRenderer
- canAdmin={true}
- canSubmit={true}
- passwordValue="password"
- confirmPasswordValue="confirm"
- onConfirmPasswordChange={jest.fn()}
- onPasswordChange={jest.fn()}
- onSubmit={jest.fn()}
- submitting={false}
- success={false}
- location={mockLocation()}
- {...props}
- />
- );
-}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: admin is not using the default password 1`] = `
-<ChangeAdminPasswordAppRenderer
- confirmPasswordValue=""
- location={
- Object {
- "hash": "",
- "key": "key",
- "pathname": "/path",
- "query": Object {},
- "search": "",
- "state": Object {},
- }
- }
- onConfirmPasswordChange={[Function]}
- onPasswordChange={[Function]}
- onSubmit={[Function]}
- passwordValue=""
- submitting={false}
- success={true}
-/>
-`;
-
-exports[`should render correctly: default 1`] = `
-<ChangeAdminPasswordAppRenderer
- canAdmin={true}
- confirmPasswordValue=""
- location={
- Object {
- "hash": "",
- "key": "key",
- "pathname": "/path",
- "query": Object {},
- "search": "",
- "state": Object {},
- }
- }
- onConfirmPasswordChange={[Function]}
- onPasswordChange={[Function]}
- onSubmit={[Function]}
- passwordValue=""
- submitting={false}
- success={false}
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: access denied 1`] = `<Unauthorized />`;
-
-exports[`should render correctly: cannot submit 1`] = `
-<div
- className="page-wrapper-simple"
->
- <div
- className="page-simple"
- >
- <h1
- className="text-center bg-danger big padded"
- >
- users.change_admin_password.instance_is_at_risk
- </h1>
- <p
- className="text-center huge huge-spacer-top"
- >
- users.change_admin_password.header
- </p>
- <p
- className="text-center huge-spacer-top huge-spacer-bottom"
- >
- users.change_admin_password.description
- </p>
- <form
- className="text-center"
- onSubmit={[Function]}
- >
- <h2
- className="big-spacer-bottom big"
- >
- users.change_admin_password.form.header
- </h2>
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="user-password"
- >
- users.change_admin_password.form.password
- <MandatoryFieldMarker />
- </label>
- <input
- id="user-password"
- name="password"
- onChange={[Function]}
- required={true}
- type="password"
- value="password"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="confirm-user-password"
- >
- users.change_admin_password.form.confirm
- <MandatoryFieldMarker />
- </label>
- <input
- id="confirm-user-password"
- name="confirm-password"
- onChange={[Function]}
- required={true}
- type="password"
- value="confirm"
- />
- </div>
- <div
- className="form-field"
- >
- <SubmitButton
- disabled={true}
- >
- update_verb
- </SubmitButton>
- </div>
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: default 1`] = `
-<div
- className="page-wrapper-simple"
->
- <div
- className="page-simple"
- >
- <h1
- className="text-center bg-danger big padded"
- >
- users.change_admin_password.instance_is_at_risk
- </h1>
- <p
- className="text-center huge huge-spacer-top"
- >
- users.change_admin_password.header
- </p>
- <p
- className="text-center huge-spacer-top huge-spacer-bottom"
- >
- users.change_admin_password.description
- </p>
- <form
- className="text-center"
- onSubmit={[Function]}
- >
- <h2
- className="big-spacer-bottom big"
- >
- users.change_admin_password.form.header
- </h2>
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="user-password"
- >
- users.change_admin_password.form.password
- <MandatoryFieldMarker />
- </label>
- <input
- id="user-password"
- name="password"
- onChange={[Function]}
- required={true}
- type="password"
- value="password"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="confirm-user-password"
- >
- users.change_admin_password.form.confirm
- <MandatoryFieldMarker />
- </label>
- <input
- id="confirm-user-password"
- name="confirm-password"
- onChange={[Function]}
- required={true}
- type="password"
- value="confirm"
- />
- </div>
- <div
- className="form-field"
- >
- <SubmitButton
- disabled={false}
- >
- update_verb
- </SubmitButton>
- </div>
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: submitting 1`] = `
-<div
- className="page-wrapper-simple"
->
- <div
- className="page-simple"
- >
- <h1
- className="text-center bg-danger big padded"
- >
- users.change_admin_password.instance_is_at_risk
- </h1>
- <p
- className="text-center huge huge-spacer-top"
- >
- users.change_admin_password.header
- </p>
- <p
- className="text-center huge-spacer-top huge-spacer-bottom"
- >
- users.change_admin_password.description
- </p>
- <form
- className="text-center"
- onSubmit={[Function]}
- >
- <h2
- className="big-spacer-bottom big"
- >
- users.change_admin_password.form.header
- </h2>
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="user-password"
- >
- users.change_admin_password.form.password
- <MandatoryFieldMarker />
- </label>
- <input
- id="user-password"
- name="password"
- onChange={[Function]}
- required={true}
- type="password"
- value="password"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="confirm-user-password"
- >
- users.change_admin_password.form.confirm
- <MandatoryFieldMarker />
- </label>
- <input
- id="confirm-user-password"
- name="confirm-password"
- onChange={[Function]}
- required={true}
- type="password"
- value="confirm"
- />
- </div>
- <div
- className="form-field"
- >
- <SubmitButton
- disabled={true}
- >
- update_verb
- <i
- className="spinner spacer-left"
- />
- </SubmitButton>
- </div>
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: success 1`] = `
-<div
- className="page-wrapper-simple"
->
- <div
- className="page-simple"
- >
- <Alert
- variant="success"
- >
- <p
- className="spacer-bottom"
- >
- users.change_admin_password.form.success
- </p>
- <a
- href="/"
- >
- users.change_admin_password.form.continue_to_app
- </a>
- </Alert>
- </div>
-</div>
-`;
-
-exports[`should render correctly: trying to use default admin password 1`] = `
-<div
- className="page-wrapper-simple"
->
- <div
- className="page-simple"
- >
- <h1
- className="text-center bg-danger big padded"
- >
- users.change_admin_password.instance_is_at_risk
- </h1>
- <p
- className="text-center huge huge-spacer-top"
- >
- users.change_admin_password.header
- </p>
- <p
- className="text-center huge-spacer-top huge-spacer-bottom"
- >
- users.change_admin_password.description
- </p>
- <form
- className="text-center"
- onSubmit={[Function]}
- >
- <h2
- className="big-spacer-bottom big"
- >
- users.change_admin_password.form.header
- </h2>
- <MandatoryFieldsExplanation
- className="form-field"
- />
- <div
- className="form-field"
- >
- <label
- htmlFor="user-password"
- >
- users.change_admin_password.form.password
- <MandatoryFieldMarker />
- </label>
- <input
- id="user-password"
- name="password"
- onChange={[Function]}
- required={true}
- type="password"
- value="admin"
- />
- </div>
- <div
- className="form-field"
- >
- <label
- htmlFor="confirm-user-password"
- >
- users.change_admin_password.form.confirm
- <MandatoryFieldMarker />
- </label>
- <input
- id="confirm-user-password"
- name="confirm-password"
- onChange={[Function]}
- required={true}
- type="password"
- value="admin"
- />
- <Alert
- className="spacer-top"
- variant="warning"
- >
- users.change_admin_password.form.cannot_use_default_password
- </Alert>
- </div>
- <div
- className="form-field"
- >
- <SubmitButton
- disabled={false}
- >
- update_verb
- </SubmitButton>
- </div>
- </form>
- </div>
-</div>
-`;