diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index de9d4e5e..30d36c76 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -3,13 +3,12 @@ on: push: branches: - main - # TODO: Add pull request after optimizing the total excecution time of the test suite. - # pull_request: - # paths: - # - 'packages/backend/**' - # - 'packages/e2e-tests/**' - # - 'packages/web/**' - # - '!packages/backend/src/apps/**' + pull_request: + paths: + - 'packages/backend/**' + - 'packages/e2e-tests/**' + - 'packages/web/**' + - '!packages/backend/src/apps/**' workflow_dispatch: env: @@ -56,27 +55,44 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: - node-version: 18 - - name: Install web dependencies - run: yarn - working-directory: ./packages/web + node-version: '18' + cache: 'yarn' + cache-dependency-path: | + packages/backend/yarn.lock + packages/web/yarn.lock + packages/e2e-tests/yarn.lock - name: Install backend dependencies - run: yarn + run: yarn --frozen-lockfile working-directory: ./packages/backend + - name: Install web dependencies + run: yarn --frozen-lockfile + working-directory: ./packages/web - name: Install e2e-tests dependencies - run: yarn + run: yarn --frozen-lockfile working-directory: ./packages/e2e-tests + - name: Get installed Playwright version + id: playwright-version + run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package.json').devDependencies['@playwright/test'])")" >> $GITHUB_ENV + working-directory: ./packages/e2e-tests + - name: Cache playwright binaries + uses: actions/cache@v3 + id: playwright-cache + with: + path: | + ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }} - name: Install Playwright Browsers run: yarn playwright install --with-deps working-directory: ./packages/e2e-tests + if: steps.playwright-cache.outputs.cache-hit != 'true' - name: Build Automatisch web run: yarn build - working-directory: ./packages/web env: # Keep this until we clean up warnings in build processes CI: false + working-directory: ./packages/web - name: Migrate database working-directory: ./packages/backend run: yarn db:migrate @@ -116,6 +132,7 @@ jobs: env: LOGIN_EMAIL: user@automatisch.io LOGIN_PASSWORD: sample + BACKEND_APP_URL: http://localhost:3000 BASE_URL: http://localhost:3000 GITHUB_CLIENT_ID: 1c0417daf898adfbd99a GITHUB_CLIENT_SECRET: 3328fa814dd582ccd03dbe785cfd683fb8da92b3 diff --git a/packages/e2e-tests/.env-example b/packages/e2e-tests/.env-example index b7a2b62f..0cf8775e 100644 --- a/packages/e2e-tests/.env-example +++ b/packages/e2e-tests/.env-example @@ -2,4 +2,5 @@ POSTGRES_DB=automatisch POSTGRES_USER=automatisch_user POSTGRES_PASSWORD=automatisch_password POSTGRES_PORT=5432 -POSTGRES_HOST=localhost \ No newline at end of file +POSTGRES_HOST=localhost +BACKEND_APP_URL=http://localhost:3000 \ No newline at end of file diff --git a/packages/e2e-tests/fixtures/admin/application-settings-page.js b/packages/e2e-tests/fixtures/admin/application-settings-page.js index 57858ccb..da421b24 100644 --- a/packages/e2e-tests/fixtures/admin/application-settings-page.js +++ b/packages/e2e-tests/fixtures/admin/application-settings-page.js @@ -56,8 +56,10 @@ export class AdminApplicationSettingsPage extends AuthenticatedPage { } async expectSuccessSnackbarToBeVisible() { - await expect(this.successSnackbar).toHaveCount(1); - await this.successSnackbar.click(); - await expect(this.successSnackbar).toHaveCount(0); + const snackbars = await this.successSnackbar.all(); + for (const snackbar of snackbars) { + await expect(await snackbar.getAttribute('data-snackbar-variant')).toBe('success'); + await snackbar.click(); + } } } diff --git a/packages/e2e-tests/fixtures/admin/create-role-page.js b/packages/e2e-tests/fixtures/admin/create-role-page.js index 3426e520..fe0ecde0 100644 --- a/packages/e2e-tests/fixtures/admin/create-role-page.js +++ b/packages/e2e-tests/fixtures/admin/create-role-page.js @@ -1,3 +1,5 @@ +import { expect } from '@playwright/test'; + const { AuthenticatedPage } = require('../authenticated-page'); const { RoleConditionsModal } = require('./role-conditions-modal'); @@ -16,6 +18,7 @@ export class AdminCreateRolePage extends AuthenticatedPage { this.executionRow = page.getByTestId('Execution-permission-row'); this.flowRow = page.getByTestId('Flow-permission-row'); this.pageTitle = page.getByTestId('create-role-title'); + this.permissionsCatalog = page.getByTestId('permissions-catalog'); } /** @@ -104,4 +107,8 @@ export class AdminCreateRolePage extends AuthenticatedPage { throw new Error(`${subject} does not have action ${action}`); } } + + async waitForPermissionsCatalogToVisible() { + await expect(this.permissionsCatalog).toBeVisible(); + } } diff --git a/packages/e2e-tests/fixtures/admin/create-user-page.js b/packages/e2e-tests/fixtures/admin/create-user-page.js index 135b38fb..e59ba2c5 100644 --- a/packages/e2e-tests/fixtures/admin/create-user-page.js +++ b/packages/e2e-tests/fixtures/admin/create-user-page.js @@ -14,8 +14,12 @@ export class AdminCreateUserPage extends AuthenticatedPage { this.roleInput = page.getByTestId('role.id-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'); } seed(seed) { diff --git a/packages/e2e-tests/fixtures/admin/users-page.js b/packages/e2e-tests/fixtures/admin/users-page.js index af6dbac3..6b7f6263 100644 --- a/packages/e2e-tests/fixtures/admin/users-page.js +++ b/packages/e2e-tests/fixtures/admin/users-page.js @@ -95,7 +95,6 @@ export class AdminUsersPage extends AuthenticatedPage { }); } const rowLocator = await this.getUserRowByEmail(email); - console.log('rowLocator.count', email, await rowLocator.count()); if ((await rowLocator.count()) === 1) { return rowLocator; } diff --git a/packages/e2e-tests/fixtures/base-page.js b/packages/e2e-tests/fixtures/base-page.js index 1b057899..ade03437 100644 --- a/packages/e2e-tests/fixtures/base-page.js +++ b/packages/e2e-tests/fixtures/base-page.js @@ -51,10 +51,20 @@ export class BasePage { }; } + async closeSnackbar() { + await this.snackbar.click(); + } + + async closeSnackbarAndWaitUntilDetached() { + const snackbar = await this.snackbar; + await snackbar.click(); + await snackbar.waitFor({ state: 'detached' }); + } + /** * Closes all snackbars, should be replaced later */ - async closeSnackbar() { + async closeAllSnackbars() { const snackbars = await this.snackbar.all(); for (const snackbar of snackbars) { await snackbar.click(); diff --git a/packages/e2e-tests/helpers/auth-api-helper.js b/packages/e2e-tests/helpers/auth-api-helper.js new file mode 100644 index 00000000..f8067ede --- /dev/null +++ b/packages/e2e-tests/helpers/auth-api-helper.js @@ -0,0 +1,16 @@ +const { expect } = require('../fixtures/index'); + +export const getToken = async (apiRequest) => { + const tokenResponse = await apiRequest.post( + `${process.env.BACKEND_APP_URL}/api/v1/access-tokens`, + { + data: { + email: process.env.LOGIN_EMAIL, + password: process.env.LOGIN_PASSWORD, + }, + } + ); + await expect(tokenResponse.status()).toBe(200); + + return await tokenResponse.json(); +}; diff --git a/packages/e2e-tests/helpers/flow-api-helper.js b/packages/e2e-tests/helpers/flow-api-helper.js new file mode 100644 index 00000000..525274a8 --- /dev/null +++ b/packages/e2e-tests/helpers/flow-api-helper.js @@ -0,0 +1,69 @@ +const { expect } = require('../fixtures/index'); + +export const createFlow = async (request, token) => { + const response = await request.post( + `${process.env.BACKEND_APP_URL}/api/v1/flows`, + { headers: { Authorization: token } } + ); + await expect(response.status()).toBe(201); + return await response.json(); +}; + +export const getFlow = async (request, token, flowId) => { + const response = await request.get( + `${process.env.BACKEND_APP_URL}/api/v1/flows/${flowId}`, + { headers: { Authorization: token } } + ); + await expect(response.status()).toBe(200); + return await response.json(); +}; + +export const updateFlowName = async (request, token, flowId) => { + const updateFlowNameResponse = await request.patch( + `${process.env.BACKEND_APP_URL}/api/v1/flows/${flowId}`, + { + headers: { Authorization: token }, + data: { name: flowId }, + } + ); + await expect(updateFlowNameResponse.status()).toBe(200); +}; + +export const updateFlowStep = async (request, token, stepId, requestBody) => { + const updateTriggerStepResponse = await request.patch( + `${process.env.BACKEND_APP_URL}/api/v1/steps/${stepId}`, + { + headers: { Authorization: token }, + data: requestBody, + } + ); + await expect(updateTriggerStepResponse.status()).toBe(200); + return await updateTriggerStepResponse.json(); +}; + +export const testStep = async (request, token, stepId) => { + const testTriggerStepResponse = await request.post( + `${process.env.BACKEND_APP_URL}/api/v1/steps/${stepId}/test`, + { + headers: { Authorization: token }, + } + ); + await expect(testTriggerStepResponse.status()).toBe(200); +}; + +export const publishFlow = async (request, token, flowId) => { + const publishFlowResponse = await request.patch( + `${process.env.BACKEND_APP_URL}/api/v1/flows/${flowId}/status`, + { + headers: { Authorization: token }, + data: { active: true }, + } + ); + await expect(publishFlowResponse.status()).toBe(200); + return publishFlowResponse.json(); +}; + +export const triggerFlow = async (request, url) => { + const triggerFlowResponse = await request.get(url); + await expect(triggerFlowResponse.status()).toBe(204); +}; diff --git a/packages/e2e-tests/helpers/user-api-helper.js b/packages/e2e-tests/helpers/user-api-helper.js new file mode 100644 index 00000000..57d96e75 --- /dev/null +++ b/packages/e2e-tests/helpers/user-api-helper.js @@ -0,0 +1,24 @@ +const { expect } = require('../fixtures/index'); + +export const addUser = async (apiRequest, token, request) => { + const addUserResponse = await apiRequest.post( + `${process.env.BACKEND_APP_URL}/api/v1/admin/users`, + { + headers: { Authorization: token }, + data: request, + } + ); + await expect(addUserResponse.status()).toBe(201); + + return await addUserResponse.json(); +}; + +export const acceptInvitation = async (apiRequest, request) => { + const acceptInvitationResponse = await apiRequest.post( + `${process.env.BACKEND_APP_URL}/api/v1/users/invitation`, + { + data: request, + } + ); + await expect(acceptInvitationResponse.status()).toBe(204); +}; diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index ddb5979d..4ad6bd10 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -26,7 +26,7 @@ }, "devDependencies": { "@faker-js/faker": "^8.2.0", - "@playwright/test": "^1.45.1" + "@playwright/test": "1.49.0" }, "dependencies": { "axios": "^1.6.0", @@ -40,4 +40,4 @@ "pg": "^8.12.0", "prettier": "^2.5.1" } -} +} \ No newline at end of file diff --git a/packages/e2e-tests/playwright.config.js b/packages/e2e-tests/playwright.config.js index ec034c39..6ba4656c 100644 --- a/packages/e2e-tests/playwright.config.js +++ b/packages/e2e-tests/playwright.config.js @@ -15,9 +15,9 @@ module.exports = defineConfig({ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, - retries: 0, + retries: process.env.CI ? 1 : 0, /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, + workers: undefined, /* Timeout threshold for each test */ timeout: 30000, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ @@ -30,7 +30,7 @@ module.exports = defineConfig({ baseURL: process.env.BASE_URL || 'http://localhost:3001', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'retain-on-failure', + trace: 'on-first-retry', testIdAttribute: 'data-test', viewport: { width: 1280, height: 720 }, }, diff --git a/packages/e2e-tests/tests/admin/applications.spec.js b/packages/e2e-tests/tests/admin/applications.spec.js index 2fad49b9..94a2d3a1 100644 --- a/packages/e2e-tests/tests/admin/applications.spec.js +++ b/packages/e2e-tests/tests/admin/applications.spec.js @@ -5,16 +5,18 @@ test.describe('Admin Applications', () => { test.beforeAll(async () => { const deleteAppAuthClients = { text: 'DELETE FROM app_auth_clients WHERE app_key in ($1, $2, $3, $4, $5)', - values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit'] + values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit'], }; const deleteAppConfigs = { text: 'DELETE FROM app_configs WHERE key in ($1, $2, $3, $4, $5)', - values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit'] + values: ['carbone', 'spotify', 'deepl', 'mailchimp', 'reddit'], }; try { - const deleteAppAuthClientsResult = await pgPool.query(deleteAppAuthClients); + const deleteAppAuthClientsResult = await pgPool.query( + deleteAppAuthClients + ); expect(deleteAppAuthClientsResult.command).toBe('DELETE'); const deleteAppConfigsResult = await pgPool.query(deleteAppConfigs); expect(deleteAppConfigsResult.command).toBe('DELETE'); @@ -31,7 +33,7 @@ test.describe('Admin Applications', () => { test('Admin should be able to toggle Application settings', async ({ adminApplicationsPage, adminApplicationSettingsPage, - page + page, }) => { await adminApplicationsPage.openApplication('Carbone'); await expect(page.url()).toContain('/admin-settings/apps/carbone/settings'); @@ -57,7 +59,7 @@ test.describe('Admin Applications', () => { adminApplicationsPage, adminApplicationSettingsPage, flowEditorPage, - page + page, }) => { await adminApplicationsPage.openApplication('Spotify'); await expect(page.url()).toContain('/admin-settings/apps/spotify/settings'); @@ -75,11 +77,15 @@ test.describe('Admin Applications', () => { const triggerStep = flowEditorPage.flowStep.last(); await triggerStep.click(); - await flowEditorPage.chooseAppAndEvent("Spotify", "Create Playlist"); + await flowEditorPage.chooseAppAndEvent('Spotify', 'Create Playlist'); await flowEditorPage.connectionAutocomplete.click(); - const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' }); - const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' }); + const newConnectionOption = page + .getByRole('option') + .filter({ hasText: 'Add new connection' }); + const newSharedConnectionOption = page + .getByRole('option') + .filter({ hasText: 'Add new shared connection' }); await expect(newConnectionOption).toBeEnabled(); await expect(newConnectionOption).toHaveCount(1); @@ -91,7 +97,7 @@ test.describe('Admin Applications', () => { adminApplicationSettingsPage, adminApplicationAuthClientsPage, flowEditorPage, - page + page, }) => { await adminApplicationsPage.openApplication('Reddit'); await expect(page.url()).toContain('/admin-settings/apps/reddit/settings'); @@ -101,13 +107,21 @@ test.describe('Admin Applications', () => { await adminApplicationAuthClientsPage.openAuthClientsTab(); await adminApplicationAuthClientsPage.openFirstAuthClientCreateForm(); - const authClientForm = page.getByTestId("auth-client-form"); + const authClientForm = page.getByTestId('auth-client-form'); await authClientForm.locator(page.getByTestId('switch')).check(); - await authClientForm.locator(page.locator('[name="name"]')).fill('redditAuthClient'); - await authClientForm.locator(page.locator('[name="clientId"]')).fill('redditClientId'); - await authClientForm.locator(page.locator('[name="clientSecret"]')).fill('redditClientSecret'); + await authClientForm + .locator(page.locator('[name="name"]')) + .fill('redditAuthClient'); + await authClientForm + .locator(page.locator('[name="clientId"]')) + .fill('redditClientId'); + await authClientForm + .locator(page.locator('[name="clientSecret"]')) + .fill('redditClientSecret'); await adminApplicationAuthClientsPage.submitAuthClientForm(); - await adminApplicationAuthClientsPage.authClientShouldBeVisible('redditAuthClient'); + await adminApplicationAuthClientsPage.authClientShouldBeVisible( + 'redditAuthClient' + ); await page.goto('/'); await page.getByTestId('create-flow-button').click(); @@ -119,11 +133,15 @@ test.describe('Admin Applications', () => { const triggerStep = flowEditorPage.flowStep.last(); await triggerStep.click(); - await flowEditorPage.chooseAppAndEvent("Reddit", "Create link post"); + await flowEditorPage.chooseAppAndEvent('Reddit', 'Create link post'); await flowEditorPage.connectionAutocomplete.click(); - const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' }); - const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' }); + const newConnectionOption = page + .getByRole('option') + .filter({ hasText: 'Add new connection' }); + const newSharedConnectionOption = page + .getByRole('option') + .filter({ hasText: 'Add new shared connection' }); await expect(newConnectionOption).toHaveCount(0); await expect(newSharedConnectionOption).toBeEnabled(); @@ -134,7 +152,7 @@ test.describe('Admin Applications', () => { adminApplicationsPage, adminApplicationSettingsPage, flowEditorPage, - page + page, }) => { await adminApplicationsPage.openApplication('DeepL'); await expect(page.url()).toContain('/admin-settings/apps/deepl/settings'); @@ -152,12 +170,18 @@ test.describe('Admin Applications', () => { const triggerStep = flowEditorPage.flowStep.last(); await triggerStep.click(); - await flowEditorPage.chooseAppAndEvent("DeepL", "Translate text"); + await flowEditorPage.chooseAppAndEvent('DeepL', 'Translate text'); await flowEditorPage.connectionAutocomplete.click(); - const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' }); - const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' }); - const noConnectionsOption = page.locator('.MuiAutocomplete-noOptions').filter({ hasText: 'No options' }); + const newConnectionOption = page + .getByRole('option') + .filter({ hasText: 'Add new connection' }); + const newSharedConnectionOption = page + .getByRole('option') + .filter({ hasText: 'Add new shared connection' }); + const noConnectionsOption = page + .locator('.MuiAutocomplete-noOptions') + .filter({ hasText: 'No options' }); await expect(noConnectionsOption).toHaveCount(1); await expect(newConnectionOption).toHaveCount(0); @@ -168,11 +192,11 @@ test.describe('Admin Applications', () => { adminApplicationsPage, adminApplicationSettingsPage, flowEditorPage, - page + page, }) => { const queryUser = { text: 'SELECT * FROM users WHERE email = $1', - values: [process.env.LOGIN_EMAIL] + values: [process.env.LOGIN_EMAIL], }; try { @@ -183,14 +207,16 @@ test.describe('Admin Applications', () => { text: 'INSERT INTO connections (key, data, user_id, verified, draft) VALUES ($1, $2, $3, $4, $5)', values: [ 'mailchimp', - "U2FsdGVkX1+cAtdHwLiuRL4DaK/T1aljeeKyPMmtWK0AmAIsKhYwQiuyQCYJO3mdZ31z73hqF2Y+yj2Kn2/IIpLRqCxB2sC0rCDCZyolzOZ290YcBXSzYRzRUxhoOcZEtwYDKsy8AHygKK/tkj9uv9k6wOe1LjipNik4VmRhKjEYizzjLrJpbeU1oY+qW0GBpPYomFTeNf+MejSSmsUYyYJ8+E/4GeEfaonvsTSwMT7AId98Lck6Vy4wrfgpm7sZZ8xU15/HqXZNc8UCo2iTdw45xj/Oov9+brX4WUASFPG8aYrK8dl/EdaOvr89P8uIofbSNZ25GjJvVF5ymarrPkTZ7djjJXchzpwBY+7GTJfs3funR/vIk0Hq95jgOFFP1liZyqTXSa49ojG3hzojRQ==", + 'U2FsdGVkX1+cAtdHwLiuRL4DaK/T1aljeeKyPMmtWK0AmAIsKhYwQiuyQCYJO3mdZ31z73hqF2Y+yj2Kn2/IIpLRqCxB2sC0rCDCZyolzOZ290YcBXSzYRzRUxhoOcZEtwYDKsy8AHygKK/tkj9uv9k6wOe1LjipNik4VmRhKjEYizzjLrJpbeU1oY+qW0GBpPYomFTeNf+MejSSmsUYyYJ8+E/4GeEfaonvsTSwMT7AId98Lck6Vy4wrfgpm7sZZ8xU15/HqXZNc8UCo2iTdw45xj/Oov9+brX4WUASFPG8aYrK8dl/EdaOvr89P8uIofbSNZ25GjJvVF5ymarrPkTZ7djjJXchzpwBY+7GTJfs3funR/vIk0Hq95jgOFFP1liZyqTXSa49ojG3hzojRQ==', queryUserResult.rows[0].id, 'true', - 'false' + 'false', ], }; - const createMailchimpConnectionResult = await pgPool.query(createMailchimpConnection); + const createMailchimpConnectionResult = await pgPool.query( + createMailchimpConnection + ); expect(createMailchimpConnectionResult.rowCount).toBe(1); expect(createMailchimpConnectionResult.command).toBe('INSERT'); } catch (err) { @@ -199,7 +225,9 @@ test.describe('Admin Applications', () => { } await adminApplicationsPage.openApplication('Mailchimp'); - await expect(page.url()).toContain('/admin-settings/apps/mailchimp/settings'); + await expect(page.url()).toContain( + '/admin-settings/apps/mailchimp/settings' + ); await adminApplicationSettingsPage.disallowConnections(); await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible(); @@ -214,14 +242,22 @@ test.describe('Admin Applications', () => { const triggerStep = flowEditorPage.flowStep.last(); await triggerStep.click(); - await flowEditorPage.chooseAppAndEvent("Mailchimp", "Create campaign"); + await flowEditorPage.chooseAppAndEvent('Mailchimp', 'Create campaign'); await flowEditorPage.connectionAutocomplete.click(); await expect(page.getByRole('option').first()).toHaveText('Unnamed'); - const existingConnection = page.getByRole('option').filter({ hasText: 'Unnamed' }); - const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' }); - const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' }); - const noConnectionsOption = page.locator('.MuiAutocomplete-noOptions').filter({ hasText: 'No options' }); + const existingConnection = page + .getByRole('option') + .filter({ hasText: 'Unnamed' }); + const newConnectionOption = page + .getByRole('option') + .filter({ hasText: 'Add new connection' }); + const newSharedConnectionOption = page + .getByRole('option') + .filter({ hasText: 'Add new shared connection' }); + const noConnectionsOption = page + .locator('.MuiAutocomplete-noOptions') + .filter({ hasText: 'No options' }); await expect(await existingConnection.count()).toBeGreaterThan(0); await expect(noConnectionsOption).toHaveCount(0); diff --git a/packages/e2e-tests/tests/admin/manage-roles.spec.js b/packages/e2e-tests/tests/admin/manage-roles.spec.js index 00299c5d..fd6b9385 100644 --- a/packages/e2e-tests/tests/admin/manage-roles.spec.js +++ b/packages/e2e-tests/tests/admin/manage-roles.spec.js @@ -22,22 +22,18 @@ test.describe('Role management page', () => { await adminRolesPage.navigateTo(); await adminRolesPage.createRoleButton.click(); await adminCreateRolePage.isMounted(); + await adminCreateRolePage.waitForPermissionsCatalogToVisible(); await adminCreateRolePage.nameInput.fill('Create Edit Test'); await adminCreateRolePage.descriptionInput.fill('Test description'); await adminCreateRolePage.createButton.click(); - await adminCreateRolePage.snackbar.waitFor({ - state: 'attached', - }); const snackbar = await adminCreateRolePage.getSnackbarData( 'snackbar-create-role-success' ); await expect(snackbar.variant).toBe('success'); - 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 +44,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); @@ -57,19 +52,14 @@ test.describe('Role management page', () => { await adminEditRolePage.nameInput.fill('Create Update Test'); await adminEditRolePage.descriptionInput.fill('Update test description'); await adminEditRolePage.updateButton.click(); - await adminEditRolePage.snackbar.waitFor({ - state: 'attached', - }); const snackbar = await adminEditRolePage.getSnackbarData( 'snackbar-edit-role-success' ); await expect(snackbar.variant).toBe('success'); - 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 +71,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); @@ -91,14 +80,10 @@ test.describe('Role management page', () => { state: 'attached', }); await deleteModal.deleteButton.click(); - await adminRolesPage.snackbar.waitFor({ - state: 'attached', - }); const snackbar = await adminRolesPage.getSnackbarData( 'snackbar-delete-role-success' ); await expect(snackbar.variant).toBe('success'); - await adminRolesPage.closeSnackbar(); await deleteModal.modal.waitFor({ state: 'detached', }); @@ -173,60 +158,45 @@ test.describe('Role management page', () => { await test.step('Create a new role', async () => { await adminRolesPage.createRoleButton.click(); await adminCreateRolePage.isMounted(); + await adminCreateRolePage.waitForPermissionsCatalogToVisible(); await adminCreateRolePage.nameInput.fill('Delete Role'); await adminCreateRolePage.createButton.click(); - await adminCreateRolePage.snackbar.waitFor({ - state: 'attached', - }); const snackbar = await adminCreateRolePage.getSnackbarData( 'snackbar-create-role-success' ); 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', + }); + const snackbar = await adminUsersPage.getSnackbarData( + 'snackbar-create-user-success' + ); + await expect(snackbar.variant).toBe('success'); + }); + 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(); + const snackbar = await adminRolesPage.getSnackbarData( + 'snackbar-delete-role-error' + ); + await expect(snackbar.variant).toBe('error'); + await modal.close(); + }); await test.step('Change the role the user has', async () => { await adminUsersPage.navigateTo(); await adminUsersPage.usersLoader.waitFor({ @@ -241,14 +211,10 @@ test.describe('Role management page', () => { .getByRole('option', { name: 'Admin' }) .click(); await adminEditUserPage.updateButton.click(); - await adminEditUserPage.snackbar.waitFor({ - state: 'attached', - }); const snackbar = await adminEditUserPage.getSnackbarData( 'snackbar-edit-user-success' ); await expect(snackbar.variant).toBe('success'); - await adminEditUserPage.closeSnackbar(); }); await test.step('Delete the original role', async () => { await adminRolesPage.navigateTo(); @@ -256,14 +222,10 @@ test.describe('Role management page', () => { const modal = await adminRolesPage.clickDeleteRole(row); await expect(modal.modal).toBeVisible(); await modal.deleteButton.click(); - await adminRolesPage.snackbar.waitFor({ - state: 'attached', - }); const snackbar = await adminRolesPage.getSnackbarData( 'snackbar-delete-role-success' ); await expect(snackbar.variant).toBe('success'); - await adminRolesPage.closeSnackbar(); }); }); @@ -277,16 +239,13 @@ test.describe('Role management page', () => { await test.step('Create a new role', async () => { await adminRolesPage.createRoleButton.click(); await adminCreateRolePage.isMounted(); + await adminCreateRolePage.waitForPermissionsCatalogToVisible(); await adminCreateRolePage.nameInput.fill('Cannot Delete Role'); await adminCreateRolePage.createButton.click(); - await adminCreateRolePage.snackbar.waitFor({ - state: 'attached', - }); const snackbar = await adminCreateRolePage.getSnackbarData( 'snackbar-create-role-success' ); await expect(snackbar.variant).toBe('success'); - await adminCreateRolePage.closeSnackbar(); }); await test.step('Create a new user with this role', async () => { await adminUsersPage.navigateTo(); @@ -301,9 +260,6 @@ 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', }); @@ -311,40 +267,34 @@ test.describe('Role management page', () => { 'snackbar-create-user-success' ); await expect(snackbar.variant).toBe('success'); - await adminCreateUserPage.closeSnackbar(); }); 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({ - state: 'attached', - }); const snackbar = await adminUsersPage.getSnackbarData( 'snackbar-delete-user-success' ); await expect(snackbar.variant).toBe('success'); - await adminUsersPage.closeSnackbar(); }); await test.step('Try deleting this role', async () => { await adminRolesPage.navigateTo(); const row = await adminRolesPage.getRoleRowByName('Cannot 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'); /* * TODO: await snackbar - make assertions based on product * decisions const snackbar = await adminRolesPage.getSnackbarData(); await expect(snackbar.variant).toBe('...'); */ - await adminRolesPage.closeSnackbar(); }); }); }); @@ -362,16 +312,13 @@ test('Accessibility of role management page', async ({ await adminRolesPage.navigateTo(); await adminRolesPage.createRoleButton.click(); await adminCreateRolePage.isMounted(); + await adminCreateRolePage.waitForPermissionsCatalogToVisible(); await adminCreateRolePage.nameInput.fill('Basic Test'); await adminCreateRolePage.createButton.click(); - await adminCreateRolePage.snackbar.waitFor({ - state: 'attached', - }); const snackbar = await adminCreateRolePage.getSnackbarData( 'snackbar-create-role-success' ); await expect(snackbar.variant).toBe('success'); - await adminCreateRolePage.closeSnackbar(); }); await test.step('Create a new user with the basic role', async () => { @@ -385,9 +332,6 @@ 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', }); @@ -395,56 +339,46 @@ test('Accessibility of role management page', async ({ 'snackbar-create-user-success' ); await expect(snackbar.variant).toBe('success'); - await adminCreateUserPage.closeSnackbar(); }); await test.step('Logout and login to the basic role user', async () => { const acceptInvitationLink = await adminCreateUserPage.acceptInvitationLink; - console.log(acceptInvitationLink); const acceptInvitationUrl = await acceptInvitationLink.textContent(); - console.log(acceptInvitationUrl); const acceptInvitatonToken = acceptInvitationUrl.split('?token=')[1]; await page.getByTestId('profile-menu-button').click(); 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('/'); @@ -465,10 +399,10 @@ test('Accessibility of role management page', async ({ await adminEditUserPage.roleInput.click(); await adminEditUserPage.page.getByRole('option', { name: 'Admin' }).click(); await adminEditUserPage.updateButton.click(); - await adminEditUserPage.snackbar.waitFor({ - state: 'attached', - }); - await adminEditUserPage.closeSnackbar(); + const snackbar = await adminEditUserPage.getSnackbarData( + 'snackbar-edit-user-success' + ); + await expect(snackbar.variant).toBe('success'); }); await test.step('Delete the role', async () => { @@ -480,14 +414,10 @@ test('Accessibility of role management page', async ({ state: 'attached', }); await deleteModal.deleteButton.click(); - await adminRolesPage.snackbar.waitFor({ - state: 'attached', - }); const snackbar = await adminRolesPage.getSnackbarData( 'snackbar-delete-role-success' ); await expect(snackbar.variant).toBe('success'); - await adminRolesPage.closeSnackbar(); await deleteModal.modal.waitFor({ state: 'detached', }); diff --git a/packages/e2e-tests/tests/admin/manage-users.spec.js b/packages/e2e-tests/tests/admin/manage-users.spec.js index d6fc1507..7c3ab62d 100644 --- a/packages/e2e-tests/tests/admin/manage-users.spec.js +++ b/packages/e2e-tests/tests/admin/manage-users.spec.js @@ -5,281 +5,235 @@ 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(); + await adminUsersPage.closeAllSnackbars(); }); - 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(); - } + const snackbar = await adminUsersPage.getSnackbarData( + 'snackbar-create-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.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( - 'Edit user info and make sure the edit works correctly', - async () => { - await adminUsersPage.findUserPageWithEmail(user.email); + await expect(snackbar.variant).toBe('success'); - 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(); + 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-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); - } + const snackbar = await adminUsersPage.getSnackbarData( + 'snackbar-delete-user-success' ); - 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(); + await expect(snackbar.variant).toBe('success'); + await expect(userRow).not.toBeVisible(false); + }); + }); - const snackbar = await adminUsersPage.getSnackbarData( - 'snackbar-delete-user-success' - ); - await expect(snackbar.variant).toBe('success'); - await adminUsersPage.closeSnackbar(); - await expect(userRow).not.toBeVisible(false); - } + 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'); }); - 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 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(); + const snackbar = await adminUsersPage.getSnackbarData('snackbar-error'); + await expect(snackbar.variant).toBe('error'); + }); + }); + + 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(); + const snackbar = await adminUsersPage.getSnackbarData( + 'snackbar-create-user-success' ); + await expect(snackbar.variant).toBe('success'); + }); - 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(); - } + 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); + const snackbar = await adminUsersPage.getSnackbarData('snackbar-error'); + await expect(snackbar.variant).toBe('error'); + }); + }); + + 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.closeAllSnackbars(); + }); - 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(); - 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(); + const snackbar = await adminUsersPage.getSnackbarData( + 'snackbar-create-user-success' ); + await expect(snackbar.variant).toBe('success'); + }); - 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('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 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(); - 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(); - 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(); - - 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 expect(page.url()).toBe(editPageUrl); + }); + }); }); diff --git a/packages/e2e-tests/tests/connections/enabled-pop-up-reminder.spec.js b/packages/e2e-tests/tests/connections/enabled-pop-up-reminder.spec.js index 32d30525..3b599a56 100644 --- a/packages/e2e-tests/tests/connections/enabled-pop-up-reminder.spec.js +++ b/packages/e2e-tests/tests/connections/enabled-pop-up-reminder.spec.js @@ -1,24 +1,55 @@ +const { request } = require('@playwright/test'); const { test, expect } = require('../../fixtures/index'); -const {AddMattermostConnectionModal} = require('../../fixtures/apps/mattermost/add-mattermost-connection-modal'); +const { + AddMattermostConnectionModal, +} = require('../../fixtures/apps/mattermost/add-mattermost-connection-modal'); +const { createFlow, updateFlowName, getFlow, updateFlowStep, testStep } = require('../../helpers/flow-api-helper'); +const { getToken } = require('../../helpers/auth-api-helper'); test.describe('Pop-up message on connections', () => { test.beforeEach(async ({ flowEditorPage, page }) => { - 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 expect(page.getByTestId('flow-step')).toHaveCount(2); + const apiRequest = await request.newContext(); + const tokenJsonResponse = await getToken(apiRequest); + const token = tokenJsonResponse.data.token; - await flowEditorPage.flowName.click(); - await flowEditorPage.flowNameInput.fill('PopupFlow'); - await flowEditorPage.createWebhookTrigger(true); + let flow = await createFlow(apiRequest, token); + const flowId = flow.data.id; + await updateFlowName(apiRequest, token, flowId); + flow = await getFlow(apiRequest, token, flowId); + const flowSteps = flow.data.steps; - await flowEditorPage.chooseAppAndEvent('Mattermost', 'Send a message to channel'); - await expect(flowEditorPage.continueButton).toHaveCount(1); - await expect(flowEditorPage.continueButton).not.toBeEnabled(); + const triggerStepId = flowSteps.find((step) => step.type === 'trigger').id; + const actionStepId = flowSteps.find((step) => step.type === 'action').id; + + const triggerStep = await updateFlowStep(apiRequest, token, triggerStepId, { + appKey: 'webhook', + key: 'catchRawWebhook', + parameters: { + workSynchronously: false, + }, + }); + await apiRequest.get(triggerStep.data.webhookUrl); + await testStep(apiRequest, token, triggerStepId); + + await updateFlowStep(apiRequest, token, actionStepId, { + appKey: 'mattermost', + key: 'sendMessageToChannel', + }); + await testStep(apiRequest, token, actionStepId); + + await page.reload(); + + const flowRow = await page.getByTestId('flow-row').filter({ + hasText: flowId, + }); + await flowRow.click(); + const flowTriggerStep = await page.getByTestId('flow-step').nth(1); + await flowTriggerStep.click(); + await page.getByText('Choose connection').click(); await flowEditorPage.connectionAutocomplete.click(); - await flowEditorPage.addNewConnectionItem.click(); }); + await flowEditorPage.addNewConnectionItem.click(); + }); test('should show error to remind to enable pop-up on connection create', async ({ page, @@ -28,7 +59,7 @@ test.describe('Pop-up message on connections', () => { // Inject script to override window.open await page.evaluate(() => { // eslint-disable-next-line no-undef - window.open = function() { + window.open = function () { console.log('Popup blocked!'); return null; }; @@ -37,8 +68,10 @@ test.describe('Pop-up message on connections', () => { await addMattermostConnectionModal.fillConnectionForm(); await addMattermostConnectionModal.submitConnectionForm(); - await expect(page.getByTestId("add-connection-error")).toHaveCount(1); - await expect(page.getByTestId("add-connection-error")).toHaveText('Make sure pop-ups are enabled in your browser.'); + await expect(page.getByTestId('add-connection-error')).toHaveCount(1); + await expect(page.getByTestId('add-connection-error')).toHaveText( + 'Make sure pop-ups are enabled in your browser.' + ); }); test('should not show pop-up error if pop-ups are enabled on connection create', async ({ @@ -51,13 +84,15 @@ test.describe('Pop-up message on connections', () => { await addMattermostConnectionModal.submitConnectionForm(); const popup = await popupPromise; - await expect(popup.url()).toContain("mattermost"); - await expect(page.getByTestId("add-connection-error")).toHaveCount(0); + await expect(popup.url()).toContain('mattermost'); + await expect(page.getByTestId('add-connection-error')).toHaveCount(0); await test.step('Should show error on failed credentials verification', async () => { await popup.close(); - await expect(page.getByTestId("add-connection-error")).toHaveCount(1); - await expect(page.getByTestId("add-connection-error")).toHaveText('Error occured while verifying credentials!'); + await expect(page.getByTestId('add-connection-error')).toHaveCount(1); + await expect(page.getByTestId('add-connection-error')).toHaveText( + 'Error occured while verifying credentials!' + ); }); }); -}); \ No newline at end of file +}); 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..d53c3a08 100644 --- a/packages/e2e-tests/tests/my-profile/profile-updates.spec.js +++ b/packages/e2e-tests/tests/my-profile/profile-updates.spec.js @@ -1,57 +1,38 @@ +const { request } = require('@playwright/test'); const { publicTest, expect } = require('../../fixtures/index'); -const { AdminUsersPage } = require('../../fixtures/admin/users-page'); const { MyProfilePage } = require('../../fixtures/my-profile-page'); const { LoginPage } = require('../../fixtures/login-page'); +const { addUser, acceptInvitation } = require('../../helpers/user-api-helper'); +const { getToken } = require('../../helpers/auth-api-helper'); publicTest.describe('My Profile', () => { let testUser; publicTest.beforeEach( - async ({ acceptInvitationPage, adminCreateUserPage, loginPage, page }) => { - let acceptInvitationLink; + async ({ adminCreateUserPage, loginPage, page }) => { + let addUserResponse; + const apiRequest = await request.newContext(); adminCreateUserPage.seed( Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER) ); testUser = adminCreateUserPage.generateUser(); - const adminUsersPage = new AdminUsersPage(page); - const myProfilePage = new MyProfilePage(page); - - await publicTest.step('login as Admin', async () => { - await loginPage.login(); - await expect(loginPage.page).toHaveURL('/flows'); - }); - await publicTest.step('create new 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' + const tokenJsonResponse = await getToken(apiRequest); + addUserResponse = await addUser( + apiRequest, + tokenJsonResponse.data.token, + { + fullName: testUser.fullName, + email: testUser.email, + } ); - await expect(snackbar.variant).toBe('success'); - }); - - await publicTest.step('copy invitation link', async () => { - const invitationMessage = - await adminCreateUserPage.acceptInvitationLink; - acceptInvitationLink = await invitationMessage.getAttribute('href'); - }); - - await publicTest.step('logout', async () => { - await myProfilePage.logout(); }); await publicTest.step('accept invitation', async () => { - await page.goto(acceptInvitationLink); - await acceptInvitationPage.acceptInvitation(LoginPage.defaultPassword); + let acceptToken = addUserResponse.data.acceptInvitationUrl.split('=')[1]; + await acceptInvitation(apiRequest, {token:acceptToken, password:LoginPage.defaultPassword}); }); await publicTest.step('login as new Admin', async () => { diff --git a/packages/e2e-tests/yarn.lock b/packages/e2e-tests/yarn.lock index 4953ecb7..f86c39e7 100644 --- a/packages/e2e-tests/yarn.lock +++ b/packages/e2e-tests/yarn.lock @@ -79,7 +79,7 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@playwright/test@^1.45.1": +"@playwright/test@1.49.0": version "1.49.0" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.0.tgz#74227385b58317ee076b86b56d0e1e1b25cff01e" integrity sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw== diff --git a/packages/web/src/components/PermissionCatalogField/index.ee.jsx b/packages/web/src/components/PermissionCatalogField/index.ee.jsx index 5f2bf909..24a86d38 100644 --- a/packages/web/src/components/PermissionCatalogField/index.ee.jsx +++ b/packages/web/src/components/PermissionCatalogField/index.ee.jsx @@ -30,7 +30,7 @@ const PermissionCatalogField = ({ if (isPermissionCatalogLoading) return ; return ( - +