*/
import userEvent from '@testing-library/user-event';
import * as React from 'react';
-import { byLabelText, byRole, byTestId } from '~sonar-aligned/helpers/testSelector';
+import { byLabelText, byRole } from '~sonar-aligned/helpers/testSelector';
import { mockLoggedInUser } from '../../../helpers/testMocks';
import { renderComponent } from '../../../helpers/testReactTestingUtils';
import { ResetPassword, ResetPasswordProps } from '../ResetPassword';
const ui = {
oldPasswordInput: byLabelText(/my_profile\.password\.old/),
- passwordInput: byTestId('create-password'),
+ passwordInput: byLabelText(/^password/),
passwordConfirmationInput: byLabelText(/confirm_password\*/i),
submitButton: byRole('button', { name: 'update_verb' }),
};
exact: false,
});
- const newPasswordField = screen.getByTestId('create-password');
+ const newPasswordField = screen.getByLabelText(/^password/);
const confirmPasswordField = screen.getByLabelText(/confirm_password*/i);
await fillTextField(user, oldPasswordField, '123456old');
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import { AppState } from '../../types/appstate';
import ChangeAdminPasswordAppRenderer from './ChangeAdminPasswordAppRenderer';
-import { DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_PASSWORD } from './constants';
+import { DEFAULT_ADMIN_LOGIN } from './constants';
interface Props {
appState: AppState;
}
interface State {
- canSubmit?: boolean;
- confirmPasswordValue: string;
- passwordValue: string;
submitting: boolean;
success: boolean;
}
super(props);
this.state = {
- passwordValue: '',
- confirmPasswordValue: '',
submitting: false,
success: !props.appState.instanceUsesDefaultAdminCredentials,
};
this.mounted = false;
}
- handlePasswordChange = (passwordValue: string) => {
- this.setState({ passwordValue }, this.checkCanSubmit);
- };
-
- handleConfirmPasswordChange = (confirmPasswordValue: string) => {
- this.setState({ confirmPasswordValue }, this.checkCanSubmit);
- };
-
- handleSubmit = async () => {
- const { canSubmit, passwordValue } = this.state;
- if (canSubmit) {
- this.setState({ submitting: true });
- const success = await changePassword({
+ handleSubmit = async (password: string) => {
+ this.setState({ submitting: true });
+ let success = true;
+ try {
+ await changePassword({
login: DEFAULT_ADMIN_LOGIN,
- password: passwordValue,
- }).then(
- () => true,
- () => false,
- );
- if (this.mounted) {
- this.setState({ submitting: false, success });
- }
+ password,
+ });
+ } catch (_) {
+ success = false;
}
- };
- checkCanSubmit = () => {
- this.setState(({ passwordValue, confirmPasswordValue }) => ({
- canSubmit: passwordValue === confirmPasswordValue && passwordValue !== DEFAULT_ADMIN_PASSWORD,
- }));
+ if (this.mounted) {
+ this.setState({ submitting: false, success });
+ }
};
render() {
appState: { canAdmin },
location,
} = this.props;
- const { canSubmit, confirmPasswordValue, passwordValue, submitting, success } = this.state;
+ const { submitting, success } = this.state;
return (
<ChangeAdminPasswordAppRenderer
canAdmin={canAdmin}
- passwordValue={passwordValue}
- confirmPasswordValue={confirmPasswordValue}
- canSubmit={canSubmit}
- onPasswordChange={this.handlePasswordChange}
- onConfirmPasswordChange={this.handleConfirmPasswordChange}
onSubmit={this.handleSubmit}
submitting={submitting}
success={success}
CenteredLayout,
DarkLabel,
FlagMessage,
- FormField,
- InputField,
PageContentFontWrapper,
Spinner,
SubTitle,
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { Location } from '~sonar-aligned/types/router';
+import UserPasswordInput, {
+ PasswordChangeHandlerParams,
+} from '../../components/common/UserPasswordInput';
import { translate } from '../../helpers/l10n';
import { getReturnUrl } from '../../helpers/urls';
import Unauthorized from '../sessions/components/Unauthorized';
export interface ChangeAdminPasswordAppRendererProps {
canAdmin?: boolean;
- canSubmit?: boolean;
- confirmPasswordValue: string;
location: Location;
- onConfirmPasswordChange: (password: string) => void;
- onPasswordChange: (password: string) => void;
- onSubmit: () => void;
- passwordValue: string;
+ onSubmit: (password: string) => void;
submitting: boolean;
success: boolean;
}
-const PASSWORD_FIELD_ID = 'user-password';
-const CONFIRM_PASSWORD_FIELD_ID = 'confirm-user-password';
-
export default function ChangeAdminPasswordAppRenderer(
props: Readonly<ChangeAdminPasswordAppRendererProps>,
) {
- const {
- canAdmin,
- canSubmit,
- confirmPasswordValue,
- location,
- passwordValue,
- submitting,
- success,
- } = props;
+ const { canAdmin, location, onSubmit, submitting, success } = props;
+ const [newPassword, setNewPassword] = React.useState<PasswordChangeHandlerParams>({
+ value: '',
+ isValid: false,
+ });
+ const canSubmit = newPassword.isValid && newPassword.value !== DEFAULT_ADMIN_PASSWORD;
if (!canAdmin) {
return <Unauthorized />;
className="sw-mt-8"
onSubmit={(e: React.SyntheticEvent<HTMLFormElement>) => {
e.preventDefault();
- props.onSubmit();
+ onSubmit(newPassword.value);
}}
>
<SubTitle className="sw-mb-4">
{translate('users.change_admin_password.form.header')}
</SubTitle>
- <FormField
- htmlFor={PASSWORD_FIELD_ID}
- label={translate('users.change_admin_password.form.password')}
- required
- >
- <InputField
- id={PASSWORD_FIELD_ID}
- name="password"
- onChange={(e: React.SyntheticEvent<HTMLInputElement>) => {
- props.onPasswordChange(e.currentTarget.value);
- }}
- required
- type="password"
- value={passwordValue}
- />
- </FormField>
-
- <FormField
- description={
- confirmPasswordValue === passwordValue &&
- passwordValue === DEFAULT_ADMIN_PASSWORD && (
- <FlagMessage className="sw-mt-2" variant="warning">
- {translate('users.change_admin_password.form.cannot_use_default_password')}
- </FlagMessage>
- )
- }
- htmlFor={CONFIRM_PASSWORD_FIELD_ID}
- label={translate('users.change_admin_password.form.confirm')}
- required
- >
- <InputField
- id={CONFIRM_PASSWORD_FIELD_ID}
- name="confirm-password"
- onChange={(e: React.SyntheticEvent<HTMLInputElement>) => {
- props.onConfirmPasswordChange(e.currentTarget.value);
- }}
- required
- type="password"
- value={confirmPasswordValue}
- />
- </FormField>
+ <UserPasswordInput
+ value={newPassword.value}
+ onChange={setNewPassword}
+ size="medium"
+ />
<ButtonPrimary
className="sw-mt-8"
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,
- }),
+ passwordInput: byLabelText(/^password/),
+ confirmInput: byLabelText(/confirm_password\*/i),
unauthorizedMessage: byText('unauthorized.message'),
- defaultPasswordWarningMessage: byText(
- 'users.change_admin_password.form.cannot_use_default_password',
- ),
};
it('should disallow change when not an admin', () => {
mockAppState({ instanceUsesDefaultAdminCredentials: true, canAdmin: true }),
);
expect(ui.updateButton.get()).toBeDisabled();
- await user.type(ui.passwordInput.get(), 'password');
+ await user.type(ui.passwordInput.get(), 'passworD$123');
expect(ui.updateButton.get()).toBeDisabled();
await user.type(ui.confirmInput.get(), 'pass');
expect(ui.updateButton.get()).toBeDisabled();
- await user.keyboard('word');
+ await user.keyboard('worD$123');
expect(ui.updateButton.get()).toBeEnabled();
await user.click(ui.updateButton.get());
expect(changePassword).toHaveBeenCalledWith({
login: 'admin',
- password: 'password',
+ password: 'passworD$123',
});
});
await user.type(ui.confirmInput.get(), DEFAULT_ADMIN_PASSWORD);
expect(ui.updateButton.get()).toBeDisabled();
- expect(ui.defaultPasswordWarningMessage.get()).toBeInTheDocument();
});
function renderChangeAdminPasswordApp(appState?: AppState) {
import * as React from 'react';
import { changePassword } from '../../../api/users';
import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext';
-import UserPasswordInput from '../../../components/common/UserPasswordInput';
+import UserPasswordInput, {
+ PasswordChangeHandlerParams,
+} from '../../../components/common/UserPasswordInput';
import { translate } from '../../../helpers/l10n';
import { ChangePasswordResults, RestUserDetailed, isLoggedIn } from '../../../types/users';
const [errorTranslationKey, setErrorTranslationKey] = React.useState<string | undefined>(
undefined,
);
- const [newPassword, setNewPassword] = React.useState<{ isValid: boolean; value: string }>({
+ const [newPassword, setNewPassword] = React.useState<PasswordChangeHandlerParams>({
value: '',
isValid: false,
});
addGlobalErrorMessage,
} from 'design-system';
import * as React from 'react';
-import UserPasswordInput from '../../../components/common/UserPasswordInput';
+import UserPasswordInput, {
+ PasswordChangeHandlerParams,
+} from '../../../components/common/UserPasswordInput';
import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { parseErrorResponse } from '../../../helpers/request';
const [email, setEmail] = React.useState<string>(user?.email ?? '');
const [login, setLogin] = React.useState<string>(user?.login ?? '');
const [name, setName] = React.useState<string>(user?.name ?? '');
- const [password, setPassword] = React.useState<{ isValid: boolean; value: string }>({
+ const [password, setPassword] = React.useState<PasswordChangeHandlerParams>({
value: '',
isValid: false,
});
import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation';
import { translate } from '../../helpers/l10n';
import { ChangePasswordResults, LoggedInUser } from '../../types/users';
-import UserPasswordInput from './UserPasswordInput';
+import UserPasswordInput, { PasswordChangeHandlerParams } from './UserPasswordInput';
interface Props {
className?: string;
}: Readonly<Props>) {
const [error, setError] = React.useState<string | undefined>(undefined);
const [oldPassword, setOldPassword] = React.useState('');
- const [password, setPassword] = React.useState<{ isValid: boolean; value: string }>({
+ const [password, setPassword] = React.useState<PasswordChangeHandlerParams>({
value: '',
isValid: false,
});
<div className="sw-py-3">
<Button
- isDisabled={oldPassword === '' || password.value === '' || !password.isValid}
+ isDisabled={oldPassword === '' || !password.isValid}
id="change-password"
type="submit"
variety={ButtonVariety.Primary}
const MIN_PASSWORD_LENGTH = 12;
-export type PasswordChangeHandler = (password: { isValid: boolean; value: string }) => void;
+export type PasswordChangeHandlerParams = { isValid: boolean; value: string };
export interface Props {
- onChange: PasswordChangeHandler;
+ onChange: (password: PasswordChangeHandlerParams) => void;
size?: InputSizeKeys;
value: string;
}
my_profile.scm_accounts.tooltip=SCM accounts are used for automatic issue assignment. Login and email are automatically considered as SCM account.
my_profile.password.title=Enter a new password
my_profile.password.old=Old Password
-my_profile.password.new=New Password
-my_profile.password.confirm=Confirm Password
my_profile.password.changed=The password has been changed!
my_profile.notifications.submit=Save changes
my_profile.overall_notifications.title=Overall notifications
users.change_admin_password.header=Default Administrator credentials are still used
users.change_admin_password.description=Your SonarQube instance is still using default administrator credentials. You must change the password for the 'admin' account to secure your SonarQube instance.
users.change_admin_password.form.header=Change the password for user 'admin'
-users.change_admin_password.form.password=New password for user 'admin'
-users.change_admin_password.form.confirm=Confirm password for user 'admin'
-users.change_admin_password.form.cannot_use_default_password=You must choose a password that is different from the default password.
users.change_admin_password.form.success=The admin user's password was successfully changed.
users.change_admin_password.form.continue_to_app=Continue to SonarQube
users.filter.by=Filter by