diff --git a/packages/e2e-tests/fixtures/admin/create-user-page.js b/packages/e2e-tests/fixtures/admin/create-user-page.js
index 135b38fb..ddf0f6e6 100644
--- a/packages/e2e-tests/fixtures/admin/create-user-page.js
+++ b/packages/e2e-tests/fixtures/admin/create-user-page.js
@@ -1,3 +1,5 @@
+const { expect } = require('@playwright/test');
+
const { faker } = require('@faker-js/faker');
const { AuthenticatedPage } = require('../authenticated-page');
@@ -11,11 +13,17 @@ export class AdminCreateUserPage extends AuthenticatedPage {
super(page);
this.fullNameInput = page.getByTestId('full-name-input');
this.emailInput = page.getByTestId('email-input');
- this.roleInput = page.getByTestId('role.id-autocomplete');
+ this.roleInput = page.getByTestId('roleId-autocomplete');
this.createButton = page.getByTestId('create-button');
this.pageTitle = page.getByTestId('create-user-title');
- this.invitationEmailInfoAlert = page.getByTestId('invitation-email-info-alert');
- this.acceptInvitationLink = page.getByTestId('invitation-email-info-alert').getByRole('link');
+ this.invitationEmailInfoAlert = page.getByTestId(
+ 'invitation-email-info-alert'
+ );
+ this.acceptInvitationLink = page
+ .getByTestId('invitation-email-info-alert')
+ .getByRole('link');
+ this.createUserSuccessAlert = page.getByTestId('create-user-success-alert');
+ this.fieldError = page.locator('p[id$="-helper-text"]');
}
seed(seed) {
@@ -28,4 +36,8 @@ export class AdminCreateUserPage extends AuthenticatedPage {
email: faker.internet.email().toLowerCase(),
};
}
+
+ async expectCreateUserSuccessAlertToBeVisible() {
+ await expect(this.createUserSuccessAlert).toBeVisible();
+ }
}
diff --git a/packages/e2e-tests/tests/admin/manage-roles.spec.js b/packages/e2e-tests/tests/admin/manage-roles.spec.js
index 00299c5d..1e9a405f 100644
--- a/packages/e2e-tests/tests/admin/manage-roles.spec.js
+++ b/packages/e2e-tests/tests/admin/manage-roles.spec.js
@@ -35,9 +35,8 @@ test.describe('Role management page', () => {
await adminCreateRolePage.closeSnackbar();
});
- let roleRow = await test.step(
- 'Make sure role data is correct',
- async () => {
+ let roleRow =
+ await test.step('Make sure role data is correct', async () => {
const roleRow = await adminRolesPage.getRoleRowByName(
'Create Edit Test'
);
@@ -48,8 +47,7 @@ test.describe('Role management page', () => {
await expect(roleData.canEdit).toBe(true);
await expect(roleData.canDelete).toBe(true);
return roleRow;
- }
- );
+ });
await test.step('Edit the role', async () => {
await adminRolesPage.clickEditRole(roleRow);
@@ -67,9 +65,8 @@ test.describe('Role management page', () => {
await adminEditRolePage.closeSnackbar();
});
- roleRow = await test.step(
- 'Make sure changes reflected on roles page',
- async () => {
+ roleRow =
+ await test.step('Make sure changes reflected on roles page', async () => {
await adminRolesPage.isMounted();
const roleRow = await adminRolesPage.getRoleRowByName(
'Create Update Test'
@@ -81,8 +78,7 @@ test.describe('Role management page', () => {
await expect(roleData.canEdit).toBe(true);
await expect(roleData.canDelete).toBe(true);
return roleRow;
- }
- );
+ });
await test.step('Delete the role', async () => {
await adminRolesPage.clickDeleteRole(roleRow);
@@ -184,49 +180,39 @@ test.describe('Role management page', () => {
await expect(snackbar.variant).toBe('success');
await adminCreateRolePage.closeSnackbar();
});
- await test.step(
- 'Create a new user with the "Delete Role" role',
- async () => {
- await adminUsersPage.navigateTo();
- await adminUsersPage.createUserButton.click();
- await adminCreateUserPage.fullNameInput.fill('User Role Test');
- await adminCreateUserPage.emailInput.fill(
- 'user-role-test@automatisch.io'
- );
- await adminCreateUserPage.roleInput.click();
- await adminCreateUserPage.page
- .getByRole('option', { name: 'Delete Role', exact: true })
- .click();
- await adminCreateUserPage.createButton.click();
- await adminCreateUserPage.snackbar.waitFor({
- state: 'attached',
- });
- await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
- state: 'attached',
- });
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-create-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
- }
- );
- await test.step(
- 'Try to delete "Delete Role" role when new user has it',
- async () => {
- await adminRolesPage.navigateTo();
- const row = await adminRolesPage.getRoleRowByName('Delete Role');
- const modal = await adminRolesPage.clickDeleteRole(row);
- await modal.deleteButton.click();
- await adminRolesPage.snackbar.waitFor({
- state: 'attached',
- });
- const snackbar = await adminRolesPage.getSnackbarData('snackbar-delete-role-error');
- await expect(snackbar.variant).toBe('error');
- await adminRolesPage.closeSnackbar();
- await modal.close();
- }
- );
+ await test.step('Create a new user with the "Delete Role" role', async () => {
+ await adminUsersPage.navigateTo();
+ await adminUsersPage.createUserButton.click();
+ await adminCreateUserPage.fullNameInput.fill('User Role Test');
+ await adminCreateUserPage.emailInput.fill(
+ 'user-role-test@automatisch.io'
+ );
+ await adminCreateUserPage.roleInput.click();
+ await adminCreateUserPage.page
+ .getByRole('option', { name: 'Delete Role', exact: true })
+ .click();
+ await adminCreateUserPage.createButton.click();
+ await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
+ state: 'attached',
+ });
+ await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
+ });
+
+ await test.step('Try to delete "Delete Role" role when new user has it', async () => {
+ await adminRolesPage.navigateTo();
+ const row = await adminRolesPage.getRoleRowByName('Delete Role');
+ const modal = await adminRolesPage.clickDeleteRole(row);
+ await modal.deleteButton.click();
+ await adminRolesPage.snackbar.waitFor({
+ state: 'attached',
+ });
+ const snackbar = await adminRolesPage.getSnackbarData(
+ 'snackbar-delete-role-error'
+ );
+ await expect(snackbar.variant).toBe('error');
+ await adminRolesPage.closeSnackbar();
+ await modal.close();
+ });
await test.step('Change the role the user has', async () => {
await adminUsersPage.navigateTo();
await adminUsersPage.usersLoader.waitFor({
@@ -301,24 +287,16 @@ test.describe('Role management page', () => {
.getByRole('option', { name: 'Cannot Delete Role' })
.click();
await adminCreateUserPage.createButton.click();
- await adminCreateUserPage.snackbar.waitFor({
- state: 'attached',
- });
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
state: 'attached',
});
- const snackbar = await adminCreateUserPage.getSnackbarData(
- 'snackbar-create-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminCreateUserPage.closeSnackbar();
+ await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
});
await test.step('Delete this user', async () => {
await adminUsersPage.navigateTo();
const row = await adminUsersPage.findUserPageWithEmail(
'user-delete-role-test@automatisch.io'
);
- // await test.waitForTimeout(10000);
const modal = await adminUsersPage.clickDeleteUser(row);
await modal.deleteButton.click();
await adminUsersPage.snackbar.waitFor({
@@ -385,17 +363,10 @@ test('Accessibility of role management page', async ({
.getByRole('option', { name: 'Basic Test' })
.click();
await adminCreateUserPage.createButton.click();
- await adminCreateUserPage.snackbar.waitFor({
- state: 'attached',
- });
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
state: 'attached',
});
- const snackbar = await adminCreateUserPage.getSnackbarData(
- 'snackbar-create-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminCreateUserPage.closeSnackbar();
+ await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
});
await test.step('Logout and login to the basic role user', async () => {
@@ -409,42 +380,35 @@ test('Accessibility of role management page', async ({
await page.getByTestId('logout-item').click();
const acceptInvitationPage = new AcceptInvitation(page);
-
await acceptInvitationPage.open(acceptInvitatonToken);
-
await acceptInvitationPage.acceptInvitation('sample');
const loginPage = new LoginPage(page);
-
- // await loginPage.isMounted();
await loginPage.login('basic-role-test@automatisch.io', 'sample');
await expect(loginPage.loginButton).not.toBeVisible();
await expect(page).toHaveURL('/flows');
});
- await test.step(
- 'Navigate to the admin settings page and make sure it is blank',
- async () => {
- const pageUrl = new URL(page.url());
- const url = `${pageUrl.origin}/admin-settings/users`;
- await page.goto(url);
- await page.waitForTimeout(750);
- const isUnmounted = await page.evaluate(() => {
- // eslint-disable-next-line no-undef
- const root = document.querySelector('#root');
+ await test.step('Navigate to the admin settings page and make sure it is blank', async () => {
+ const pageUrl = new URL(page.url());
+ const url = `${pageUrl.origin}/admin-settings/users`;
+ await page.goto(url);
+ await page.waitForTimeout(750);
+ const isUnmounted = await page.evaluate(() => {
+ // eslint-disable-next-line no-undef
+ const root = document.querySelector('#root');
- if (root) {
- // We have react query devtools only in dev env.
- // In production, there is nothing in root.
- // That's why `<= 1`.
- return root.children.length <= 1;
- }
+ if (root) {
+ // We have react query devtools only in dev env.
+ // In production, there is nothing in root.
+ // That's why `<= 1`.
+ return root.children.length <= 1;
+ }
- return false;
- });
- await expect(isUnmounted).toBe(true);
- }
- );
+ return false;
+ });
+ await expect(isUnmounted).toBe(true);
+ });
await test.step('Log back into the admin account', async () => {
await page.goto('/');
diff --git a/packages/e2e-tests/tests/admin/manage-users.spec.js b/packages/e2e-tests/tests/admin/manage-users.spec.js
index d6fc1507..af7f7083 100644
--- a/packages/e2e-tests/tests/admin/manage-users.spec.js
+++ b/packages/e2e-tests/tests/admin/manage-users.spec.js
@@ -5,281 +5,221 @@ const { test, expect } = require('../../fixtures/index');
* otherwise tests will fail since users are only *soft*-deleted
*/
test.describe('User management page', () => {
-
test.beforeEach(async ({ adminUsersPage }) => {
await adminUsersPage.navigateTo();
await adminUsersPage.closeSnackbar();
});
- test(
- 'User creation and deletion process',
- async ({ adminCreateUserPage, adminEditUserPage, adminUsersPage }) => {
- adminCreateUserPage.seed(9000);
- const user = adminCreateUserPage.generateUser();
- await adminUsersPage.usersLoader.waitFor({
- state: 'detached' /* Note: state: 'visible' introduces flakiness
+ test('User creation and deletion process', async ({
+ adminCreateUserPage,
+ adminEditUserPage,
+ adminUsersPage,
+ }) => {
+ adminCreateUserPage.seed(9000);
+ const user = adminCreateUserPage.generateUser();
+ await adminUsersPage.usersLoader.waitFor({
+ state: 'detached' /* Note: state: 'visible' introduces flakiness
because visibility: hidden is used as part of the state transition in
notistack, see
https://github.com/iamhosseindhv/notistack/blob/122f47057eb7ce5a1abfe923316cf8475303e99a/src/transitions/Collapse/Collapse.tsx#L110
- */
+ */,
+ });
+ await test.step('Create a user', async () => {
+ await adminUsersPage.createUserButton.click();
+ await adminCreateUserPage.fullNameInput.fill(user.fullName);
+ await adminCreateUserPage.emailInput.fill(user.email);
+ await adminCreateUserPage.roleInput.click();
+ await adminCreateUserPage.page
+ .getByRole('option', { name: 'Admin' })
+ .click();
+ await adminCreateUserPage.createButton.click();
+ await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
+ state: 'attached',
});
- await test.step(
- 'Create a user',
- async () => {
- await adminUsersPage.createUserButton.click();
- await adminCreateUserPage.fullNameInput.fill(user.fullName);
- await adminCreateUserPage.emailInput.fill(user.email);
- await adminCreateUserPage.roleInput.click();
- await adminCreateUserPage.page.getByRole(
- 'option', { name: 'Admin' }
- ).click();
- await adminCreateUserPage.createButton.click();
- await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
- state: 'attached'
- });
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-create-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminUsersPage.navigateTo();
- await adminUsersPage.closeSnackbar();
- }
+ await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
+ await adminUsersPage.navigateTo();
+ });
+ await test.step('Check the user exists with the expected properties', async () => {
+ await adminUsersPage.findUserPageWithEmail(user.email);
+ const userRow = await adminUsersPage.getUserRowByEmail(user.email);
+ const data = await adminUsersPage.getRowData(userRow);
+ await expect(data.email).toBe(user.email);
+ await expect(data.fullName).toBe(user.fullName);
+ await expect(data.role).toBe('Admin');
+ });
+ await test.step('Edit user info and make sure the edit works correctly', async () => {
+ await adminUsersPage.findUserPageWithEmail(user.email);
+
+ let userRow = await adminUsersPage.getUserRowByEmail(user.email);
+ await adminUsersPage.clickEditUser(userRow);
+ await adminEditUserPage.waitForLoad(user.fullName);
+ const newUserInfo = adminEditUserPage.generateUser();
+ await adminEditUserPage.fullNameInput.fill(newUserInfo.fullName);
+ await adminEditUserPage.updateButton.click();
+
+ const snackbar = await adminUsersPage.getSnackbarData(
+ 'snackbar-edit-user-success'
);
- await test.step(
- 'Check the user exists with the expected properties',
- async () => {
- await adminUsersPage.findUserPageWithEmail(user.email);
- const userRow = await adminUsersPage.getUserRowByEmail(user.email);
- const data = await adminUsersPage.getRowData(userRow);
- await expect(data.email).toBe(user.email);
- await expect(data.fullName).toBe(user.fullName);
- await expect(data.role).toBe('Admin');
- }
+ await expect(snackbar.variant).toBe('success');
+ await adminUsersPage.closeSnackbar();
+
+ await adminUsersPage.findUserPageWithEmail(user.email);
+ userRow = await adminUsersPage.getUserRowByEmail(user.email);
+ const rowData = await adminUsersPage.getRowData(userRow);
+ await expect(rowData.fullName).toBe(newUserInfo.fullName);
+ });
+ await test.step('Delete user and check the page confirms this deletion', async () => {
+ await adminUsersPage.findUserPageWithEmail(user.email);
+ const userRow = await adminUsersPage.getUserRowByEmail(user.email);
+ await adminUsersPage.clickDeleteUser(userRow);
+ const modal = adminUsersPage.deleteUserModal;
+ await modal.deleteButton.click();
+
+ const snackbar = await adminUsersPage.getSnackbarData(
+ 'snackbar-delete-user-success'
);
- await test.step(
- 'Edit user info and make sure the edit works correctly',
- async () => {
- await adminUsersPage.findUserPageWithEmail(user.email);
+ await expect(snackbar.variant).toBe('success');
+ await adminUsersPage.closeSnackbar();
+ await expect(userRow).not.toBeVisible(false);
+ });
+ });
- let userRow = await adminUsersPage.getUserRowByEmail(user.email);
- await adminUsersPage.clickEditUser(userRow);
- await adminEditUserPage.waitForLoad(user.fullName);
- const newUserInfo = adminEditUserPage.generateUser();
- await adminEditUserPage.fullNameInput.fill(newUserInfo.fullName);
- await adminEditUserPage.updateButton.click();
+ test('Creating a user which has been deleted', async ({
+ adminCreateUserPage,
+ adminUsersPage,
+ }) => {
+ adminCreateUserPage.seed(9100);
+ const testUser = adminCreateUserPage.generateUser();
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-edit-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
-
- await adminUsersPage.findUserPageWithEmail(user.email);
- userRow = await adminUsersPage.getUserRowByEmail(user.email);
- const rowData = await adminUsersPage.getRowData(userRow);
- await expect(rowData.fullName).toBe(newUserInfo.fullName);
- }
- );
- await test.step(
- 'Delete user and check the page confirms this deletion',
- async () => {
- await adminUsersPage.findUserPageWithEmail(user.email);
- const userRow = await adminUsersPage.getUserRowByEmail(user.email);
- await adminUsersPage.clickDeleteUser(userRow);
- const modal = adminUsersPage.deleteUserModal;
- await modal.deleteButton.click();
-
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-delete-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
- await expect(userRow).not.toBeVisible(false);
- }
- );
+ await test.step('Create the test user', async () => {
+ await adminUsersPage.navigateTo();
+ await adminUsersPage.createUserButton.click();
+ await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
+ await adminCreateUserPage.emailInput.fill(testUser.email);
+ await adminCreateUserPage.roleInput.click();
+ await adminCreateUserPage.page
+ .getByRole('option', { name: 'Admin' })
+ .click();
+ await adminCreateUserPage.createButton.click();
+ await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
});
- test(
- 'Creating a user which has been deleted',
- async ({ adminCreateUserPage, adminUsersPage }) => {
- adminCreateUserPage.seed(9100);
- const testUser = adminCreateUserPage.generateUser();
-
- await test.step(
- 'Create the test user',
- async () => {
- await adminUsersPage.navigateTo();
- await adminUsersPage.createUserButton.click();
- await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
- await adminCreateUserPage.emailInput.fill(testUser.email);
- await adminCreateUserPage.roleInput.click();
- await adminCreateUserPage.page.getByRole(
- 'option', { name: 'Admin' }
- ).click();
- await adminCreateUserPage.createButton.click();
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-create-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
- }
+ await test.step('Delete the created user', async () => {
+ await adminUsersPage.navigateTo();
+ await adminUsersPage.findUserPageWithEmail(testUser.email);
+ const userRow = await adminUsersPage.getUserRowByEmail(testUser.email);
+ await adminUsersPage.clickDeleteUser(userRow);
+ const modal = adminUsersPage.deleteUserModal;
+ await modal.deleteButton.click();
+ const snackbar = await adminUsersPage.getSnackbarData(
+ 'snackbar-delete-user-success'
);
+ await expect(snackbar).not.toBeNull();
+ await expect(snackbar.variant).toBe('success');
+ await adminUsersPage.closeSnackbar();
+ await expect(userRow).not.toBeVisible(false);
+ });
- await test.step(
- 'Delete the created user',
- async () => {
- await adminUsersPage.navigateTo();
- await adminUsersPage.findUserPageWithEmail(testUser.email);
- const userRow = await adminUsersPage.getUserRowByEmail(testUser.email);
- await adminUsersPage.clickDeleteUser(userRow);
- const modal = adminUsersPage.deleteUserModal;
- await modal.deleteButton.click();
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-delete-user-success'
- );
- await expect(snackbar).not.toBeNull();
- await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
- await expect(userRow).not.toBeVisible(false);
- }
- );
+ await test.step('Create the user again', async () => {
+ await adminUsersPage.createUserButton.click();
+ await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
+ await adminCreateUserPage.emailInput.fill(testUser.email);
+ await adminCreateUserPage.roleInput.click();
+ await adminCreateUserPage.page
+ .getByRole('option', { name: 'Admin' })
+ .click();
+ await adminCreateUserPage.createButton.click();
+ await expect(adminCreateUserPage.fieldError).toHaveCount(1);
+ });
+ });
- await test.step(
- 'Create the user again',
- async () => {
- await adminUsersPage.createUserButton.click();
- await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
- await adminCreateUserPage.emailInput.fill(testUser.email);
- await adminCreateUserPage.roleInput.click();
- await adminCreateUserPage.page.getByRole(
- 'option', { name: 'Admin' }
- ).click();
- await adminCreateUserPage.createButton.click();
- const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
- await expect(snackbar.variant).toBe('error');
- await adminUsersPage.closeSnackbar();
- }
- );
- }
- );
+ test('Creating a user which already exists', async ({
+ adminCreateUserPage,
+ adminUsersPage,
+ page,
+ }) => {
+ adminCreateUserPage.seed(9200);
+ const testUser = adminCreateUserPage.generateUser();
- test(
- 'Creating a user which already exists',
- async ({ adminCreateUserPage, adminUsersPage, page }) => {
- adminCreateUserPage.seed(9200);
- const testUser = adminCreateUserPage.generateUser();
+ await test.step('Create the test user', async () => {
+ await adminUsersPage.createUserButton.click();
+ await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
+ await adminCreateUserPage.emailInput.fill(testUser.email);
+ await adminCreateUserPage.roleInput.click();
+ await adminCreateUserPage.page
+ .getByRole('option', { name: 'Admin' })
+ .click();
+ await adminCreateUserPage.createButton.click();
+ await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
+ });
- await test.step(
- 'Create the test user',
- async () => {
- await adminUsersPage.createUserButton.click();
- await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
- await adminCreateUserPage.emailInput.fill(testUser.email);
- await adminCreateUserPage.roleInput.click();
- await adminCreateUserPage.page.getByRole(
- 'option', { name: 'Admin' }
- ).click();
- await adminCreateUserPage.createButton.click();
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-create-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
- }
- );
+ await test.step('Create the user again', async () => {
+ await adminUsersPage.navigateTo();
+ await adminUsersPage.createUserButton.click();
+ await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
+ await adminCreateUserPage.emailInput.fill(testUser.email);
+ const createUserPageUrl = page.url();
+ await adminCreateUserPage.roleInput.click();
+ await adminCreateUserPage.page
+ .getByRole('option', { name: 'Admin' })
+ .click();
+ await adminCreateUserPage.createButton.click();
- await test.step(
- 'Create the user again',
- async () => {
- await adminUsersPage.navigateTo();
- await adminUsersPage.createUserButton.click();
- await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
- await adminCreateUserPage.emailInput.fill(testUser.email);
- const createUserPageUrl = page.url();
- await adminCreateUserPage.roleInput.click();
- await adminCreateUserPage.page.getByRole(
- 'option', { name: 'Admin' }
- ).click();
- await adminCreateUserPage.createButton.click();
+ await expect(page.url()).toBe(createUserPageUrl);
+ await expect(adminCreateUserPage.fieldError).toHaveCount(1);
+ });
+ });
- await expect(page.url()).toBe(createUserPageUrl);
- const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
- await expect(snackbar.variant).toBe('error');
- await adminUsersPage.closeSnackbar();
- }
- );
- }
- );
+ test('Editing a user to have the same email as another user should not be allowed', async ({
+ adminCreateUserPage,
+ adminEditUserPage,
+ adminUsersPage,
+ page,
+ }) => {
+ adminCreateUserPage.seed(9300);
+ const user1 = adminCreateUserPage.generateUser();
+ const user2 = adminCreateUserPage.generateUser();
+ await test.step('Create the first user', async () => {
+ await adminUsersPage.navigateTo();
+ await adminUsersPage.createUserButton.click();
+ await adminCreateUserPage.fullNameInput.fill(user1.fullName);
+ await adminCreateUserPage.emailInput.fill(user1.email);
+ await adminCreateUserPage.roleInput.click();
+ await adminCreateUserPage.page
+ .getByRole('option', { name: 'Admin' })
+ .click();
+ await adminCreateUserPage.createButton.click();
+ await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
+ });
- test(
- 'Editing a user to have the same email as another user should not be allowed',
- async ({
- adminCreateUserPage, adminEditUserPage, adminUsersPage, page
- }) => {
- adminCreateUserPage.seed(9300);
- const user1 = adminCreateUserPage.generateUser();
- const user2 = adminCreateUserPage.generateUser();
- await test.step(
- 'Create the first user',
- async () => {
- await adminUsersPage.navigateTo();
- await adminUsersPage.createUserButton.click();
- await adminCreateUserPage.fullNameInput.fill(user1.fullName);
- await adminCreateUserPage.emailInput.fill(user1.email);
- await adminCreateUserPage.roleInput.click();
- await adminCreateUserPage.page.getByRole(
- 'option', { name: 'Admin' }
- ).click();
- await adminCreateUserPage.createButton.click();
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-create-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
- }
- );
+ await test.step('Create the second user', async () => {
+ await adminUsersPage.navigateTo();
+ await adminUsersPage.createUserButton.click();
+ await adminCreateUserPage.fullNameInput.fill(user2.fullName);
+ await adminCreateUserPage.emailInput.fill(user2.email);
+ await adminCreateUserPage.roleInput.click();
+ await adminCreateUserPage.page
+ .getByRole('option', { name: 'Admin' })
+ .click();
+ await adminCreateUserPage.createButton.click();
+ await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
+ });
- await test.step(
- 'Create the second user',
- async () => {
- await adminUsersPage.navigateTo();
- await adminUsersPage.createUserButton.click();
- await adminCreateUserPage.fullNameInput.fill(user2.fullName);
- await adminCreateUserPage.emailInput.fill(user2.email);
- await adminCreateUserPage.roleInput.click();
- await adminCreateUserPage.page.getByRole(
- 'option', { name: 'Admin' }
- ).click();
- await adminCreateUserPage.createButton.click();
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-create-user-success'
- );
- await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
- }
- );
+ await test.step('Try editing the second user to have the email of the first user', async () => {
+ await adminUsersPage.navigateTo();
+ await adminUsersPage.findUserPageWithEmail(user2.email);
+ let userRow = await adminUsersPage.getUserRowByEmail(user2.email);
+ await adminUsersPage.clickEditUser(userRow);
+ await adminEditUserPage.waitForLoad(user2.fullName);
+ await adminEditUserPage.emailInput.fill(user1.email);
+ const editPageUrl = page.url();
+ await adminEditUserPage.updateButton.click();
- await test.step(
- 'Try editing the second user to have the email of the first user',
- async () => {
- await adminUsersPage.navigateTo();
- await adminUsersPage.findUserPageWithEmail(user2.email);
- let userRow = await adminUsersPage.getUserRowByEmail(user2.email);
- await adminUsersPage.clickEditUser(userRow);
- await adminEditUserPage.waitForLoad(user2.fullName);
- await adminEditUserPage.emailInput.fill(user1.email);
- const editPageUrl = page.url();
- await adminEditUserPage.updateButton.click();
-
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-error'
- );
- await expect(snackbar.variant).toBe('error');
- await adminUsersPage.closeSnackbar();
- await expect(page.url()).toBe(editPageUrl);
- }
- );
- }
- );
+ const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
+ await expect(snackbar.variant).toBe('error');
+ await adminUsersPage.closeSnackbar();
+ await expect(page.url()).toBe(editPageUrl);
+ });
+ });
});
diff --git a/packages/e2e-tests/tests/flow-editor/create-flow.spec.js b/packages/e2e-tests/tests/flow-editor/create-flow.spec.js
index 98114c27..6f46454a 100644
--- a/packages/e2e-tests/tests/flow-editor/create-flow.spec.js
+++ b/packages/e2e-tests/tests/flow-editor/create-flow.spec.js
@@ -7,198 +7,191 @@ test('Ensure creating a new flow works', async ({ page }) => {
);
});
-test(
- 'Create a new flow with a Scheduler step then an Ntfy step',
- async ({ flowEditorPage, page }) => {
- await test.step('create flow', async () => {
- await test.step('navigate to new flow page', async () => {
- await page.getByTestId('create-flow-button').click();
- await page.waitForURL(
- /\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/
- );
+test('Create a new flow with a Scheduler step then an Ntfy step', async ({
+ flowEditorPage,
+ page,
+}) => {
+ await test.step('create flow', async () => {
+ await test.step('navigate to new flow page', async () => {
+ await page.getByTestId('create-flow-button').click();
+ await page.waitForURL(
+ /\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/
+ );
+ });
+
+ await test.step('has two steps by default', async () => {
+ await expect(page.getByTestId('flow-step')).toHaveCount(2);
+ });
+ });
+
+ await test.step('setup Scheduler trigger', async () => {
+ await test.step('choose app and event substep', async () => {
+ await test.step('choose application', async () => {
+ await flowEditorPage.appAutocomplete.click();
+ await page.getByRole('option', { name: 'Scheduler' }).click();
});
-
- await test.step('has two steps by default', async () => {
- await expect(page.getByTestId('flow-step')).toHaveCount(2);
+
+ await test.step('choose and event', async () => {
+ await expect(flowEditorPage.eventAutocomplete).toBeVisible();
+ await flowEditorPage.eventAutocomplete.click();
+ await page.getByRole('option', { name: 'Every hour' }).click();
+ });
+
+ await test.step('continue to next step', async () => {
+ await flowEditorPage.continueButton.click();
+ });
+
+ await test.step('collapses the substep', async () => {
+ await expect(flowEditorPage.appAutocomplete).not.toBeVisible();
+ await expect(flowEditorPage.eventAutocomplete).not.toBeVisible();
});
});
- await test.step('setup Scheduler trigger', async () => {
- await test.step('choose app and event substep', async () => {
- await test.step('choose application', async () => {
- await flowEditorPage.appAutocomplete.click();
- await page
- .getByRole('option', { name: 'Scheduler' })
- .click();
- });
-
- await test.step('choose and event', async () => {
- await expect(flowEditorPage.eventAutocomplete).toBeVisible();
- await flowEditorPage.eventAutocomplete.click();
- await page
- .getByRole('option', { name: 'Every hour' })
- .click();
- });
-
- await test.step('continue to next step', async () => {
- await flowEditorPage.continueButton.click();
- });
-
- await test.step('collapses the substep', async () => {
- await expect(flowEditorPage.appAutocomplete).not.toBeVisible();
- await expect(flowEditorPage.eventAutocomplete).not.toBeVisible();
- });
+ await test.step('set up a trigger', async () => {
+ await test.step('choose "yes" in "trigger on weekends?"', async () => {
+ await expect(flowEditorPage.trigger).toBeVisible();
+ await flowEditorPage.trigger.click();
+ await page.getByRole('option', { name: 'Yes' }).click();
});
- await test.step('set up a trigger', async () => {
- await test.step('choose "yes" in "trigger on weekends?"', async () => {
- await expect(flowEditorPage.trigger).toBeVisible();
- await flowEditorPage.trigger.click();
- await page.getByRole('option', { name: 'Yes' }).click();
- });
-
- await test.step('continue to next step', async () => {
- await flowEditorPage.continueButton.click();
- });
-
- await test.step('collapses the substep', async () => {
- await expect(flowEditorPage.trigger).not.toBeVisible();
- });
+ await test.step('continue to next step', async () => {
+ await flowEditorPage.continueButton.click();
});
- await test.step('test trigger', async () => {
- await test.step('show sample output', async () => {
- await expect(flowEditorPage.testOutput).not.toBeVisible();
- await flowEditorPage.continueButton.click();
- await expect(flowEditorPage.testOutput).toBeVisible();
- await flowEditorPage.screenshot({
- path: 'Scheduler trigger test output.png',
- });
- await flowEditorPage.continueButton.click();
- });
+ await test.step('collapses the substep', async () => {
+ await expect(flowEditorPage.trigger).not.toBeVisible();
});
});
- await test.step('arrange Ntfy action', async () => {
- await test.step('choose app and event substep', async () => {
- await test.step('choose application', async () => {
- await flowEditorPage.appAutocomplete.click();
- await page.getByRole('option', { name: 'Ntfy' }).click();
- });
-
- await test.step('choose an event', async () => {
- await expect(flowEditorPage.eventAutocomplete).toBeVisible();
- await flowEditorPage.eventAutocomplete.click();
- await page
- .getByRole('option', { name: 'Send message' })
- .click();
- });
-
- await test.step('continue to next step', async () => {
- await flowEditorPage.continueButton.click();
- });
-
- await test.step('collapses the substep', async () => {
- await expect(flowEditorPage.appAutocomplete).not.toBeVisible();
- await expect(flowEditorPage.eventAutocomplete).not.toBeVisible();
- });
- });
-
- await test.step('choose connection substep', async () => {
- await test.step('choose connection list item', async () => {
- await flowEditorPage.connectionAutocomplete.click();
- await page.getByRole('option').first().click();
- });
-
- await test.step('continue to next step', async () => {
- await flowEditorPage.continueButton.click();
- });
-
- await test.step('collapses the substep', async () => {
- await expect(flowEditorPage.connectionAutocomplete).not.toBeVisible();
- });
- });
-
- await test.step('set up action substep', async () => {
- await test.step('fill topic and message body', async () => {
- await page
- .getByTestId('parameters.topic-power-input')
- .locator('[contenteditable]')
- .fill('Topic');
- await page
- .getByTestId('parameters.message-power-input')
- .locator('[contenteditable]')
- .fill('Message body');
- });
-
- await test.step('continue to next step', async () => {
- await flowEditorPage.continueButton.click();
- });
-
- await test.step('collapses the substep', async () => {
- await expect(flowEditorPage.connectionAutocomplete).not.toBeVisible();
- });
- });
-
- await test.step('test trigger substep', async () => {
- await test.step('show sample output', async () => {
- await expect(flowEditorPage.testOutput).not.toBeVisible();
- await page
- .getByTestId('flow-substep-continue-button')
- .first()
- .click();
- await expect(flowEditorPage.testOutput).toBeVisible();
- await flowEditorPage.screenshot({
- path: 'Ntfy action test output.png',
- });
- await flowEditorPage.continueButton.click();
- });
- });
- });
-
- await test.step('publish and unpublish', async () => {
- await test.step('publish flow', async () => {
- await expect(flowEditorPage.unpublishFlowButton).not.toBeVisible();
- await expect(flowEditorPage.publishFlowButton).toBeVisible();
- await flowEditorPage.publishFlowButton.click();
- await expect(flowEditorPage.publishFlowButton).not.toBeVisible();
- });
-
- await test.step('shows read-only sticky snackbar', async () => {
- await expect(flowEditorPage.infoSnackbar).toBeVisible();
+ await test.step('test trigger', async () => {
+ await test.step('show sample output', async () => {
+ await expect(flowEditorPage.testOutput).not.toBeVisible();
+ await flowEditorPage.continueButton.click();
+ await expect(flowEditorPage.testOutput).toBeVisible();
await flowEditorPage.screenshot({
- path: 'Published flow.png',
+ path: 'Scheduler trigger test output.png',
});
+ await flowEditorPage.continueButton.click();
});
-
- await test.step('unpublish from snackbar', async () => {
+ });
+ });
+
+ await test.step('arrange Ntfy action', async () => {
+ await test.step('choose app and event substep', async () => {
+ await test.step('choose application', async () => {
+ await flowEditorPage.appAutocomplete.click();
+ await page.getByRole('option', { name: 'Ntfy' }).click();
+ });
+
+ await test.step('choose an event', async () => {
+ await expect(flowEditorPage.eventAutocomplete).toBeVisible();
+ await flowEditorPage.eventAutocomplete.click();
+ await page.getByRole('option', { name: 'Send message' }).click();
+ });
+
+ await test.step('continue to next step', async () => {
+ await flowEditorPage.continueButton.click();
+ });
+
+ await test.step('collapses the substep', async () => {
+ await expect(flowEditorPage.appAutocomplete).not.toBeVisible();
+ await expect(flowEditorPage.eventAutocomplete).not.toBeVisible();
+ });
+ });
+
+ await test.step('choose connection substep', async () => {
+ await test.step('choose connection list item', async () => {
+ await flowEditorPage.connectionAutocomplete.click();
await page
- .getByTestId('unpublish-flow-from-snackbar')
+ .getByRole('option')
+ .filter({ hasText: 'Add new connection' })
.click();
- await expect(flowEditorPage.infoSnackbar).not.toBeVisible();
});
-
- await test.step('publish once again', async () => {
- await expect(flowEditorPage.publishFlowButton).toBeVisible();
- await flowEditorPage.publishFlowButton.click();
- await expect(flowEditorPage.publishFlowButton).not.toBeVisible();
+
+ await test.step('continue to next step', async () => {
+ await page.getByTestId('create-connection-button').click();
});
-
- await test.step('unpublish from layout top bar', async () => {
- await expect(flowEditorPage.unpublishFlowButton).toBeVisible();
- await flowEditorPage.unpublishFlowButton.click();
- await expect(flowEditorPage.unpublishFlowButton).not.toBeVisible();
+
+ await test.step('collapses the substep', async () => {
+ await flowEditorPage.continueButton.click();
+ await expect(flowEditorPage.connectionAutocomplete).not.toBeVisible();
+ });
+ });
+
+ await test.step('set up action substep', async () => {
+ await test.step('fill topic and message body', async () => {
+ await page
+ .getByTestId('parameters.topic-power-input')
+ .locator('[contenteditable]')
+ .fill('Topic');
+ await page
+ .getByTestId('parameters.message-power-input')
+ .locator('[contenteditable]')
+ .fill('Message body');
+ });
+
+ await test.step('continue to next step', async () => {
+ await flowEditorPage.continueButton.click();
+ });
+
+ await test.step('collapses the substep', async () => {
+ await expect(flowEditorPage.connectionAutocomplete).not.toBeVisible();
+ });
+ });
+
+ await test.step('test trigger substep', async () => {
+ await test.step('show sample output', async () => {
+ await expect(flowEditorPage.testOutput).not.toBeVisible();
+ await page.getByTestId('flow-substep-continue-button').first().click();
+ await expect(flowEditorPage.testOutput).toBeVisible();
await flowEditorPage.screenshot({
- path: 'Unpublished flow.png',
+ path: 'Ntfy action test output.png',
});
+ await flowEditorPage.continueButton.click();
});
});
-
- await test.step('in layout', async () => {
- await test.step('can go back to flows page', async () => {
- await page.getByTestId('editor-go-back-button').click();
- await expect(page).toHaveURL('/flows');
+ });
+
+ await test.step('publish and unpublish', async () => {
+ await test.step('publish flow', async () => {
+ await expect(flowEditorPage.unpublishFlowButton).not.toBeVisible();
+ await expect(flowEditorPage.publishFlowButton).toBeVisible();
+ await flowEditorPage.publishFlowButton.click();
+ await expect(flowEditorPage.publishFlowButton).not.toBeVisible();
+ });
+
+ await test.step('shows read-only sticky snackbar', async () => {
+ await expect(flowEditorPage.infoSnackbar).toBeVisible();
+ await flowEditorPage.screenshot({
+ path: 'Published flow.png',
});
});
- }
-);
\ No newline at end of file
+
+ await test.step('unpublish from snackbar', async () => {
+ await page.getByTestId('unpublish-flow-from-snackbar').click();
+ await expect(flowEditorPage.infoSnackbar).not.toBeVisible();
+ });
+
+ await test.step('publish once again', async () => {
+ await expect(flowEditorPage.publishFlowButton).toBeVisible();
+ await flowEditorPage.publishFlowButton.click();
+ await expect(flowEditorPage.publishFlowButton).not.toBeVisible();
+ });
+
+ await test.step('unpublish from layout top bar', async () => {
+ await expect(flowEditorPage.unpublishFlowButton).toBeVisible();
+ await flowEditorPage.unpublishFlowButton.click();
+ await expect(flowEditorPage.unpublishFlowButton).not.toBeVisible();
+ await flowEditorPage.screenshot({
+ path: 'Unpublished flow.png',
+ });
+ });
+ });
+
+ await test.step('in layout', async () => {
+ await test.step('can go back to flows page', async () => {
+ await page.getByTestId('editor-go-back-button').click();
+ await expect(page).toHaveURL('/flows');
+ });
+ });
+});
diff --git a/packages/e2e-tests/tests/my-profile/profile-updates.spec.js b/packages/e2e-tests/tests/my-profile/profile-updates.spec.js
index d77962e4..fc0ce7d0 100644
--- a/packages/e2e-tests/tests/my-profile/profile-updates.spec.js
+++ b/packages/e2e-tests/tests/my-profile/profile-updates.spec.js
@@ -33,10 +33,7 @@ publicTest.describe('My Profile', () => {
.getByRole('option', { name: 'Admin' })
.click();
await adminCreateUserPage.createButton.click();
- const snackbar = await adminUsersPage.getSnackbarData(
- 'snackbar-create-user-success'
- );
- await expect(snackbar.variant).toBe('success');
+ await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
});
await publicTest.step('copy invitation link', async () => {
diff --git a/packages/web/src/components/Container/index.jsx b/packages/web/src/components/Container/index.jsx
index ffafaa14..ef75335b 100644
--- a/packages/web/src/components/Container/index.jsx
+++ b/packages/web/src/components/Container/index.jsx
@@ -1,10 +1,19 @@
import * as React from 'react';
+import PropTypes from 'prop-types';
import MuiContainer from '@mui/material/Container';
-export default function Container(props) {
- return ;
+export default function Container({ maxWidth = 'lg', ...props }) {
+ return ;
}
-Container.defaultProps = {
- maxWidth: 'lg',
+Container.propTypes = {
+ maxWidth: PropTypes.oneOf([
+ 'xs',
+ 'sm',
+ 'md',
+ 'lg',
+ 'xl',
+ false,
+ PropTypes.string,
+ ]),
};
diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json
index c9422557..a5fa024a 100644
--- a/packages/web/src/locales/en.json
+++ b/packages/web/src/locales/en.json
@@ -226,10 +226,11 @@
"userForm.email": "Email",
"userForm.role": "Role",
"userForm.password": "Password",
+ "userForm.mandatoryInput": "{inputName} is required.",
+ "userForm.validateEmail": "Email must be valid.",
"createUser.submit": "Create",
"createUser.successfullyCreated": "The user has been created.",
"createUser.invitationEmailInfo": "Invitation email will be sent if SMTP credentials are valid. Otherwise, you can share the invitation link manually: ",
- "createUser.error": "Error while creating the user.",
"editUserPage.title": "Edit user",
"editUser.status": "Status",
"editUser.submit": "Update",
@@ -250,8 +251,10 @@
"createRolePage.title": "Create role",
"roleForm.name": "Name",
"roleForm.description": "Description",
+ "roleForm.mandatoryInput": "{inputName} is required.",
"createRole.submit": "Create",
"createRole.successfullyCreated": "The role has been created.",
+ "createRole.permissionsError": "Permissions are invalid.",
"editRole.submit": "Update",
"editRole.successfullyUpdated": "The role has been updated.",
"roleList.name": "Name",
diff --git a/packages/web/src/pages/CreateRole/index.ee.jsx b/packages/web/src/pages/CreateRole/index.ee.jsx
index 99a66901..88ac1d82 100644
--- a/packages/web/src/pages/CreateRole/index.ee.jsx
+++ b/packages/web/src/pages/CreateRole/index.ee.jsx
@@ -1,10 +1,14 @@
import LoadingButton from '@mui/lab/LoadingButton';
import Grid from '@mui/material/Grid';
import Stack from '@mui/material/Stack';
+import Alert from '@mui/material/Alert';
+import AlertTitle from '@mui/material/AlertTitle';
import PermissionCatalogField from 'components/PermissionCatalogField/index.ee';
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
import * as React from 'react';
import { useNavigate } from 'react-router-dom';
+import { yupResolver } from '@hookform/resolvers/yup';
+import * as yup from 'yup';
import Container from 'components/Container';
import Form from 'components/Form';
@@ -19,6 +23,40 @@ import useFormatMessage from 'hooks/useFormatMessage';
import useAdminCreateRole from 'hooks/useAdminCreateRole';
import usePermissionCatalog from 'hooks/usePermissionCatalog.ee';
+const getValidationSchema = (formatMessage) => {
+ const getMandatoryFieldMessage = (fieldTranslationId) =>
+ formatMessage('roleForm.mandatoryInput', {
+ inputName: formatMessage(fieldTranslationId),
+ });
+
+ return yup.object().shape({
+ name: yup
+ .string()
+ .trim()
+ .required(getMandatoryFieldMessage('roleForm.name')),
+ description: yup.string().trim(),
+ });
+};
+
+const getPermissionsErrorMessage = (error) => {
+ const errors = error?.response?.data?.errors;
+
+ if (errors) {
+ const permissionsErrors = Object.keys(errors)
+ .filter((key) => key.startsWith('permissions'))
+ .reduce((obj, key) => {
+ obj[key] = errors[key];
+ return obj;
+ }, {});
+
+ if (Object.keys(permissionsErrors).length > 0) {
+ return JSON.stringify(permissionsErrors, null, 2);
+ }
+ }
+
+ return null;
+};
+
export default function CreateRole() {
const navigate = useNavigate();
const formatMessage = useFormatMessage();
@@ -27,6 +65,7 @@ export default function CreateRole() {
useAdminCreateRole();
const { data: permissionCatalogData, isLoading: isPermissionCatalogLoading } =
usePermissionCatalog();
+ const [permissionError, setPermissionError] = React.useState(null);
const defaultValues = React.useMemo(
() => ({
@@ -44,6 +83,7 @@ export default function CreateRole() {
const handleRoleCreation = async (roleData) => {
try {
+ setPermissionError(null);
const permissions = getPermissions(roleData.computedPermissions);
await createRole({
@@ -61,16 +101,13 @@ export default function CreateRole() {
navigate(URLS.ROLES);
} catch (error) {
- const errors = Object.values(error.response.data.errors);
-
- for (const [errorMessage] of errors) {
- enqueueSnackbar(errorMessage, {
- variant: 'error',
- SnackbarProps: {
- 'data-test': 'snackbar-error',
- },
- });
+ const permissionError = getPermissionsErrorMessage(error);
+ if (permissionError) {
+ setPermissionError(permissionError);
}
+
+ const errors = error?.response?.data?.errors;
+ throw errors || error;
}
};
@@ -84,39 +121,67 @@ export default function CreateRole() {
-
diff --git a/packages/web/src/pages/CreateUser/index.jsx b/packages/web/src/pages/CreateUser/index.jsx
index ad96ba96..5eb04376 100644
--- a/packages/web/src/pages/CreateUser/index.jsx
+++ b/packages/web/src/pages/CreateUser/index.jsx
@@ -3,9 +3,10 @@ import Grid from '@mui/material/Grid';
import Stack from '@mui/material/Stack';
import Alert from '@mui/material/Alert';
import MuiTextField from '@mui/material/TextField';
-import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
import * as React from 'react';
import { useQueryClient } from '@tanstack/react-query';
+import * as yup from 'yup';
+import { yupResolver } from '@hookform/resolvers/yup';
import Can from 'components/Can';
import Container from 'components/Container';
@@ -16,50 +17,70 @@ import TextField from 'components/TextField';
import useFormatMessage from 'hooks/useFormatMessage';
import useRoles from 'hooks/useRoles.ee';
import useAdminCreateUser from 'hooks/useAdminCreateUser';
+import useCurrentUserAbility from 'hooks/useCurrentUserAbility';
function generateRoleOptions(roles) {
return roles?.map(({ name: label, id: value }) => ({ label, value }));
}
+const getValidationSchema = (formatMessage, canUpdateRole) => {
+ const getMandatoryFieldMessage = (fieldTranslationId) =>
+ formatMessage('userForm.mandatoryInput', {
+ inputName: formatMessage(fieldTranslationId),
+ });
+
+ return yup.object().shape({
+ fullName: yup
+ .string()
+ .trim()
+ .required(getMandatoryFieldMessage('userForm.fullName')),
+ email: yup
+ .string()
+ .trim()
+ .email(formatMessage('userForm.validateEmail'))
+ .required(getMandatoryFieldMessage('userForm.email')),
+ ...(canUpdateRole
+ ? {
+ roleId: yup
+ .string()
+ .required(getMandatoryFieldMessage('userForm.role')),
+ }
+ : {}),
+ });
+};
+
+const defaultValues = {
+ fullName: '',
+ email: '',
+ roleId: '',
+};
+
export default function CreateUser() {
const formatMessage = useFormatMessage();
const {
mutateAsync: createUser,
isPending: isCreateUserPending,
data: createdUser,
+ isSuccess: createUserSuccess,
} = useAdminCreateUser();
const { data: rolesData, loading: isRolesLoading } = useRoles();
const roles = rolesData?.data;
- const enqueueSnackbar = useEnqueueSnackbar();
const queryClient = useQueryClient();
+ const currentUserAbility = useCurrentUserAbility();
+ const canUpdateRole = currentUserAbility.can('update', 'Role');
const handleUserCreation = async (userData) => {
try {
await createUser({
fullName: userData.fullName,
email: userData.email,
- roleId: userData.role?.id,
+ roleId: userData.roleId,
});
queryClient.invalidateQueries({ queryKey: ['admin', 'users'] });
-
- enqueueSnackbar(formatMessage('createUser.successfullyCreated'), {
- variant: 'success',
- persist: true,
- SnackbarProps: {
- 'data-test': 'snackbar-create-user-success',
- },
- });
} catch (error) {
- enqueueSnackbar(formatMessage('createUser.error'), {
- variant: 'error',
- persist: true,
- SnackbarProps: {
- 'data-test': 'snackbar-error',
- },
- });
-
- throw new Error('Failed while creating!');
+ const errors = error?.response?.data?.errors;
+ throw errors || error;
}
};
@@ -73,74 +94,111 @@ export default function CreateUser() {
-
+ {formatMessage('createUser.submit')}
+
+
+ )}
+ />