import { IdentityProvider, Paging, SysInfoCluster } from '../../types/types';
import { User } from '../../types/users';
import { getSystemInfo } from '../system';
-import { getIdentityProviders, searchUsers } from '../users';
+import { createUser, getIdentityProviders, searchUsers } from '../users';
+
+const DEFAULT_USERS = [
+ mockUser({
+ managed: true,
+ login: 'bob.marley',
+ name: 'Bob Marley',
+ }),
+ mockUser({
+ managed: false,
+ login: 'alice.merveille',
+ name: 'Alice Merveille',
+ }),
+];
export default class UsersServiceMock {
isManaged = true;
- users = [
- mockUser({
- managed: true,
- login: 'bob.marley',
- name: 'Bob Marley',
- }),
- mockUser({
- managed: false,
- login: 'alice.merveille',
- name: 'Alice Merveille',
- }),
- ];
+ users = cloneDeep(DEFAULT_USERS);
constructor() {
jest.mocked(getSystemInfo).mockImplementation(this.handleGetSystemInfo);
jest.mocked(getIdentityProviders).mockImplementation(this.handleGetIdentityProviders);
jest.mocked(searchUsers).mockImplementation((p) => this.handleSearchUsers(p));
+ jest.mocked(createUser).mockImplementation(this.handleCreateUser);
}
setIsManaged(managed: boolean) {
return this.reply({ paging, users: this.users });
};
+ handleCreateUser = (data: {
+ email?: string;
+ local?: boolean;
+ login: string;
+ name: string;
+ password?: string;
+ scmAccount: string[];
+ }) => {
+ const { email, local, login, name, scmAccount } = data;
+ const newUser = mockUser({
+ email,
+ local,
+ login,
+ name,
+ scmAccounts: scmAccount,
+ });
+ this.users.push(newUser);
+ return this.reply(undefined);
+ };
+
handleGetIdentityProviders = (): Promise<{ identityProviders: IdentityProvider[] }> => {
return this.reply({ identityProviders: [mockIdentityProvider()] });
};
);
};
+ reset = () => {
+ this.isManaged = true;
+ this.users = cloneDeep(DEFAULT_USERS);
+ };
+
reply<T>(response: T): Promise<T> {
return Promise.resolve(cloneDeep(response));
}
padding: 0;
}
-legend {
- color: #000;
-}
-
pre,
code,
kbd,
import userEvent from '@testing-library/user-event';
import * as React from 'react';
-import { byRole, byText } from 'testing-library-selector';
+import { byLabelText, byRole, byText } from 'testing-library-selector';
import UsersServiceMock from '../../../api/mocks/UsersServiceMock';
import { renderApp } from '../../../helpers/testReactTestingUtils';
import UsersApp from '../UsersApp';
bobUpdateGroupButton: byRole('button', { name: 'users.update_users_groups.bob.marley' }),
bobUpdateButton: byRole('button', { name: 'users.manage_user.bob.marley' }),
bobRow: byRole('row', { name: 'BM Bob Marley bob.marley never' }),
+ loginInput: byRole('textbox', { name: /login/ }),
+ userNameInput: byRole('textbox', { name: /name/ }),
+ passwordInput: byLabelText(/password/),
+ scmAddButton: byRole('button', { name: 'add_verb' }),
+ createUserDialogButton: byRole('button', { name: 'create' }),
+ dialogSCMInputs: byRole('textbox', { name: /users.create_user.scm_account/ }),
+ dialogSCMInput: (value?: string) =>
+ byRole('textbox', { name: `users.create_user.scm_account_${value ? `x.${value}` : 'new'}` }),
+ deleteSCMButton: (value?: string) =>
+ byRole('button', {
+ name: `remove_x.users.create_user.scm_account_${value ? `x.${value}` : 'new'}`,
+ }),
+ jackRow: byRole('row', { name: /Jack/ }),
};
+afterAll(() => {
+ handler.reset();
+});
+
describe('in non managed mode', () => {
beforeEach(() => {
handler.setIsManaged(false);
expect(await ui.description.find()).toBeInTheDocument();
expect(ui.createUserButton.get()).toBeEnabled();
+ await userEvent.click(ui.createUserButton.get());
+
+ await userEvent.type(ui.loginInput.get(), 'Login');
+ await userEvent.type(ui.userNameInput.get(), 'Jack');
+ await userEvent.type(ui.passwordInput.get(), 'Password');
+ // Add SCM account
+ expect(ui.dialogSCMInputs.queryAll()).toHaveLength(0);
+ await userEvent.click(ui.scmAddButton.get());
+ expect(ui.dialogSCMInputs.getAll()).toHaveLength(1);
+ await userEvent.type(ui.dialogSCMInput().get(), 'SCM');
+ expect(ui.dialogSCMInput('SCM').get()).toBeInTheDocument();
+ // Remove SCM account
+ await userEvent.click(ui.deleteSCMButton('SCM').get());
+ expect(ui.dialogSCMInputs.queryAll()).toHaveLength(0);
+
+ await userEvent.click(ui.createUserDialogButton.get());
+ expect(ui.jackRow.get()).toBeInTheDocument();
});
it("should be able to add/remove user's group", async () => {
</div>
)}
<div className="modal-field">
- <label>{translate('my_profile.scm_accounts')}</label>
- {this.state.scmAccounts.map((scm, idx) => (
- <UserScmAccountInput
- idx={idx}
- key={idx}
- onChange={this.handleUpdateScmAccount}
- onRemove={this.handleRemoveScmAccount}
- scmAccount={scm}
- />
- ))}
- <div className="spacer-bottom">
- <Button className="js-scm-account-add" onClick={this.handleAddScmAccount}>
- {translate('add_verb')}
- </Button>
- </div>
+ <fieldset>
+ <legend>{translate('my_profile.scm_accounts')}</legend>
+ {this.state.scmAccounts.map((scm, idx) => (
+ <UserScmAccountInput
+ idx={idx}
+ key={idx}
+ onChange={this.handleUpdateScmAccount}
+ onRemove={this.handleRemoveScmAccount}
+ scmAccount={scm}
+ />
+ ))}
+ <div className="spacer-bottom">
+ <Button className="js-scm-account-add" onClick={this.handleAddScmAccount}>
+ {translate('add_verb')}
+ </Button>
+ </div>
+ </fieldset>
<p className="note">{translate('user.login_or_email_used_as_scm_account')}</p>
</div>
</div>
*/
import * as React from 'react';
import { DeleteButton } from '../../../components/controls/buttons';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
export interface Props {
idx: number;
onRemove: (idx: number) => void;
}
-export default class UserScmAccountInput extends React.PureComponent<Props> {
- handleChange = (event: React.SyntheticEvent<HTMLInputElement>) =>
- this.props.onChange(this.props.idx, event.currentTarget.value);
+export default function UserScmAccountInput(props: Props) {
+ const { idx, scmAccount } = props;
- handleRemove = () => this.props.onRemove(this.props.idx);
+ const inputAriaLabel = scmAccount.trim()
+ ? translateWithParameters('users.create_user.scm_account_x', scmAccount)
+ : translate('users.create_user.scm_account_new');
- render() {
- return (
- <div className="js-scm-account display-flex-row spacer-bottom">
- <input
- maxLength={255}
- onChange={this.handleChange}
- type="text"
- value={this.props.scmAccount}
- />
- <DeleteButton onClick={this.handleRemove} />
- </div>
- );
- }
+ return (
+ <div className="js-scm-account display-flex-row spacer-bottom">
+ <input
+ maxLength={255}
+ onChange={(event) => {
+ props.onChange(idx, event.currentTarget.value);
+ }}
+ type="text"
+ aria-label={inputAriaLabel}
+ value={scmAccount}
+ />
+ <DeleteButton
+ aria-label={translateWithParameters('remove_x', inputAriaLabel)}
+ onClick={() => {
+ props.onRemove(idx);
+ }}
+ />
+ </div>
+ );
}
<div
className="modal-field"
>
- <label>
- my_profile.scm_accounts
- </label>
- <div
- className="spacer-bottom"
- >
- <Button
- className="js-scm-account-add"
- onClick={[Function]}
+ <fieldset>
+ <legend>
+ my_profile.scm_accounts
+ </legend>
+ <div
+ className="spacer-bottom"
>
- add_verb
- </Button>
- </div>
+ <Button
+ className="js-scm-account-add"
+ onClick={[Function]}
+ >
+ add_verb
+ </Button>
+ </div>
+ </fieldset>
<p
className="note"
>
<div
className="modal-field"
>
- <label>
- my_profile.scm_accounts
- </label>
- <div
- className="spacer-bottom"
- >
- <Button
- className="js-scm-account-add"
- onClick={[Function]}
+ <fieldset>
+ <legend>
+ my_profile.scm_accounts
+ </legend>
+ <div
+ className="spacer-bottom"
>
- add_verb
- </Button>
- </div>
+ <Button
+ className="js-scm-account-add"
+ onClick={[Function]}
+ >
+ add_verb
+ </Button>
+ </div>
+ </fieldset>
<p
className="note"
>
refresh=Refresh
reload=Reload
remove=Remove
-remove_x=Remove {x}
+remove_x=Remove {0}
rename=Rename
replaces=Replaces
reset_verb=Reset
users.delete_user.help.link=Learn more
users.delete_user.documentation=Authentication
users.create_user=Create User
+users.create_user.scm_account_new=New SCM account
+users.create_user.scm_account_x=SCM account '{0}'
users.update_user=Update User
users.cannot_update_delegated_user=You cannot update the name and email of this user, as it is controlled by an external identity provider.
users.minimum_x_characters=Minimum {0} characters