diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index de9d4e5e..3928fd3f 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -3,7 +3,6 @@ on:
push:
branches:
- main
- # TODO: Add pull request after optimizing the total excecution time of the test suite.
# pull_request:
# paths:
# - 'packages/backend/**'
@@ -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,11 +132,12 @@ 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
run: yarn test
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
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/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/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/knexfile.js b/packages/e2e-tests/knexfile.js
index 6ed3bd37..66e54510 100644
--- a/packages/e2e-tests/knexfile.js
+++ b/packages/e2e-tests/knexfile.js
@@ -1,3 +1,5 @@
+import { knexSnakeCaseMappers } from 'objection';
+
const fileExtension = 'js';
const knexConfig = {
@@ -7,7 +9,7 @@ const knexConfig = {
user: process.env.POSTGRES_USERNAME,
port: process.env.POSTGRES_PORT,
password: process.env.POSTGRES_PASSWORD,
- database: process.env.POSTGRES_DATABASE
+ database: process.env.POSTGRES_DATABASE,
},
searchPath: ['public'],
pool: { min: 0, max: 20 },
diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json
index ddb5979d..3f495a54 100644
--- a/packages/e2e-tests/package.json
+++ b/packages/e2e-tests/package.json
@@ -26,7 +26,8 @@
},
"devDependencies": {
"@faker-js/faker": "^8.2.0",
- "@playwright/test": "^1.45.1"
+ "@playwright/test": "1.49.0",
+ "objection": "^3.1.5"
},
"dependencies": {
"axios": "^1.6.0",
diff --git a/packages/e2e-tests/playwright.config.js b/packages/e2e-tests/playwright.config.js
index ec034c39..3b44a97a 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 },
},
@@ -42,10 +42,15 @@ module.exports = defineConfig({
/* Configure projects for major browsers */
projects: [
+ {
+ name: 'db-restore',
+ testMatch: /.*\.teardown\.js/,
+ },
{
name: 'setup',
testMatch: /.*\.setup\.js/,
teardown: 'teardown',
+ dependencies: ['db-restore'],
},
{
name: 'teardown',
diff --git a/packages/e2e-tests/tests/admin/applications.spec.js b/packages/e2e-tests/tests/admin/applications.spec.js
index c487ae3f..d66d5917 100644
--- a/packages/e2e-tests/tests/admin/applications.spec.js
+++ b/packages/e2e-tests/tests/admin/applications.spec.js
@@ -43,7 +43,8 @@ test.describe('Admin Applications', () => {
await adminApplicationsPage.navigateTo();
});
- test('Admin should be able to toggle Application settings', async ({
+ // TODO skip until https://github.com/automatisch/automatisch/pull/2244
+ test.skip('Admin should be able to toggle Application settings', async ({
adminApplicationsPage,
adminApplicationSettingsPage,
page,
@@ -181,7 +182,7 @@ 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
@@ -221,6 +222,7 @@ test.describe('Admin Applications', () => {
await adminApplicationOAuthClientsPage.openAuthClientsTab();
await adminApplicationOAuthClientsPage.openFirstAuthClientCreateForm();
+
const authClientForm = page.getByTestId('auth-client-form');
await authClientForm.locator(page.getByTestId('switch')).check();
await authClientForm
@@ -232,6 +234,7 @@ test.describe('Admin Applications', () => {
await authClientForm
.locator(page.locator('[name="clientSecret"]'))
.fill('redditClientSecret');
+
await adminApplicationOAuthClientsPage.submitAuthClientForm();
await adminApplicationOAuthClientsPage.authClientShouldBeVisible(
'redditAuthClient'
diff --git a/packages/e2e-tests/tests/admin/manage-roles.spec.js b/packages/e2e-tests/tests/admin/manage-roles.spec.js
index 9198db3e..2419ee84 100644
--- a/packages/e2e-tests/tests/admin/manage-roles.spec.js
+++ b/packages/e2e-tests/tests/admin/manage-roles.spec.js
@@ -22,17 +22,14 @@ 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 =
@@ -55,14 +52,10 @@ 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 =
@@ -87,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',
});
@@ -169,16 +158,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('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 () => {
@@ -222,14 +208,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();
@@ -237,14 +219,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();
});
});
@@ -258,16 +236,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();
@@ -287,6 +262,7 @@ test.describe('Role management page', () => {
});
await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
});
+
await test.step('Delete this user', async () => {
await adminUsersPage.navigateTo();
const row = await adminUsersPage.findUserPageWithEmail(
@@ -294,14 +270,10 @@ test.describe('Role management page', () => {
);
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();
@@ -309,7 +281,6 @@ test.describe('Role management page', () => {
const modal = await adminRolesPage.clickDeleteRole(row);
await modal.deleteButton.click();
await expect(modal.deleteAlert).toHaveCount(1);
- await adminRolesPage.closeSnackbar();
});
});
});
@@ -327,16 +298,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 () => {
@@ -358,9 +326,7 @@ test('Accessibility of role management page', async ({
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();
@@ -416,10 +382,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 () => {
@@ -431,14 +397,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 8b5aaf5b..e62b6bb8 100644
--- a/packages/e2e-tests/tests/admin/manage-users.spec.js
+++ b/packages/e2e-tests/tests/admin/manage-users.spec.js
@@ -7,7 +7,7 @@ const { test, expect } = require('../../fixtures/index');
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 ({
@@ -36,7 +36,6 @@ test.describe('User management page', () => {
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
state: 'attached',
});
-
await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
await adminUsersPage.navigateTo();
});
@@ -62,7 +61,6 @@ test.describe('User management page', () => {
'snackbar-edit-user-success'
);
await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
await adminUsersPage.findUserPageWithEmail(user.email);
userRow = await adminUsersPage.getUserRowByEmail(user.email);
@@ -80,8 +78,6 @@ test.describe('User management page', () => {
'snackbar-delete-user-success'
);
await expect(snackbar.variant).toBe('success');
- await adminUsersPage.closeSnackbar();
- await expect(userRow).not.toBeVisible(false);
});
});
@@ -91,7 +87,6 @@ test.describe('User management page', () => {
}) => {
adminCreateUserPage.seed(9100);
const testUser = adminCreateUserPage.generateUser();
-
await test.step('Create the test user', async () => {
await adminUsersPage.navigateTo();
await adminUsersPage.createUserButton.click();
@@ -117,8 +112,6 @@ test.describe('User management page', () => {
);
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 () => {
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..066f6cf1 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,60 @@
+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;
+ const triggerStepId = flowSteps.find((step) => step.type === 'trigger').id;
+ const actionStepId = flowSteps.find((step) => step.type === 'action').id;
- await flowEditorPage.chooseAppAndEvent('Mattermost', 'Send a message to channel');
- await expect(flowEditorPage.continueButton).toHaveCount(1);
- await expect(flowEditorPage.continueButton).not.toBeEnabled();
+ 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 +64,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 +73,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 +89,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 fc0ce7d0..06841738 100644
--- a/packages/e2e-tests/tests/my-profile/profile-updates.spec.js
+++ b/packages/e2e-tests/tests/my-profile/profile-updates.spec.js
@@ -1,63 +1,48 @@
+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;
+ publicTest.beforeEach(async ({ adminCreateUserPage, loginPage, page }) => {
+ let addUserResponse;
+ const apiRequest = await request.newContext();
- adminCreateUserPage.seed(
- Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
+ adminCreateUserPage.seed(
+ Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
+ );
+ testUser = adminCreateUserPage.generateUser();
+
+ await publicTest.step('create new user', async () => {
+ const tokenJsonResponse = await getToken(apiRequest);
+ addUserResponse = await addUser(
+ apiRequest,
+ tokenJsonResponse.data.token,
+ {
+ fullName: testUser.fullName,
+ email: testUser.email,
+ }
);
- 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('accept invitation', async () => {
+ let acceptToken = addUserResponse.data.acceptInvitationUrl.split('=')[1];
+ await acceptInvitation(apiRequest, {
+ token: acceptToken,
+ password: LoginPage.defaultPassword,
});
+ });
- 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();
- await adminCreateUserPage.expectCreateUserSuccessAlertToBeVisible();
- });
-
- 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);
- });
-
- await publicTest.step('login as new Admin', async () => {
- await loginPage.login(testUser.email, LoginPage.defaultPassword);
- await expect(loginPage.loginButton).not.toBeVisible();
- await expect(page).toHaveURL('/flows');
- });
- }
- );
+ await publicTest.step('login as new Admin', async () => {
+ await loginPage.login(testUser.email, LoginPage.defaultPassword);
+ await expect(loginPage.loginButton).not.toBeVisible();
+ await expect(page).toHaveURL('/flows');
+ });
+ });
publicTest('user should be able to change own data', async ({ page }) => {
const myProfilePage = new MyProfilePage(page);
diff --git a/packages/e2e-tests/yarn.lock b/packages/e2e-tests/yarn.lock
index 4953ecb7..22f7c304 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==
@@ -101,6 +101,13 @@ acorn@^8.9.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
+ajv-formats@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
+ integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
+ dependencies:
+ ajv "^8.0.0"
+
ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@@ -111,6 +118,16 @@ ajv@^6.12.4:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
+ajv@^8.0.0, ajv@^8.17.1:
+ version "8.17.1"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
+ integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+ fast-uri "^3.0.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
@@ -226,6 +243,11 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"
+db-errors@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/db-errors/-/db-errors-0.2.3.tgz#a6a38952e00b20e790f2695a6446b3c65497ffa2"
+ integrity sha512-OOgqgDuCavHXjYSJoV2yGhv6SeG8nk42aoCSoyXLZUH7VwFG27rxbavU1z+VrZbZjphw5UkDQwUlD21MwZpUng==
+
debug@4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@@ -404,6 +426,11 @@ fast-levenshtein@^2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+fast-uri@^3.0.1:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241"
+ integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==
+
fastq@^1.6.0:
version "1.17.1"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
@@ -622,6 +649,11 @@ json-schema-traverse@^0.4.1:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+json-schema-traverse@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+ integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@@ -727,6 +759,15 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+objection@^3.1.5:
+ version "3.1.5"
+ resolved "https://registry.yarnpkg.com/objection/-/objection-3.1.5.tgz#53c32f6b6cba2958bc28cf723de96c2676da8286"
+ integrity sha512-Hx/ipAwXSuRBbOMWFKtRsAN0yITafqXtWB4OT4Z9wED7ty1h7bOnBdhLtcNus23GwLJqcMsRWdodL2p5GwlnfQ==
+ dependencies:
+ ajv "^8.17.1"
+ ajv-formats "^2.1.1"
+ db-errors "^0.2.3"
+
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -933,6 +974,11 @@ rechoir@^0.8.0:
dependencies:
resolve "^1.20.0"
+require-from-string@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+ integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
diff --git a/packages/web/src/components/PermissionCatalogField/index.ee.jsx b/packages/web/src/components/PermissionCatalogField/index.ee.jsx
index 21c89f81..1c8d6902 100644
--- a/packages/web/src/components/PermissionCatalogField/index.ee.jsx
+++ b/packages/web/src/components/PermissionCatalogField/index.ee.jsx
@@ -32,7 +32,7 @@ const PermissionCatalogField = ({
return ;
return (
-
+