Compare commits

...

3 Commits

Author SHA1 Message Date
Jakub P.
bab87e9e67 chore: add missing dependency for knex method in e2e-tests knexfile 2024-12-12 20:23:16 +01:00
Jakub P.
7d793ce2a2 test: fix application connections check assertions 2024-12-12 20:07:35 +01:00
Jakub P.
f83deac469 test: improve tests execution time 2024-12-06 09:17:21 +01:00
20 changed files with 654 additions and 511 deletions

View File

@@ -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

View File

@@ -2,4 +2,5 @@ POSTGRES_DB=automatisch
POSTGRES_USER=automatisch_user
POSTGRES_PASSWORD=automatisch_password
POSTGRES_PORT=5432
POSTGRES_HOST=localhost
POSTGRES_HOST=localhost
BACKEND_APP_URL=http://localhost:3000

View File

@@ -20,44 +20,54 @@ export class AdminApplicationSettingsPage extends AuthenticatedPage {
}
async allowCustomConnections() {
await expect(this.disableConnectionsSwitch).not.toBeChecked();
await expect(this.allowCustomConnectionsSwitch).not.toBeChecked();
await this.allowCustomConnectionsSwitch.check();
await expect(this.allowCustomConnectionsSwitch).toBeChecked();
await this.saveButton.click();
}
async allowSharedConnections() {
await expect(this.disableConnectionsSwitch).not.toBeChecked();
await expect(this.allowSharedConnectionsSwitch).not.toBeChecked();
await this.allowSharedConnectionsSwitch.check();
await expect(this.allowSharedConnectionsSwitch).toBeChecked();
await this.saveButton.click();
}
async disallowConnections() {
await expect(this.disableConnectionsSwitch).not.toBeChecked();
await this.disableConnectionsSwitch.check();
await expect(this.disableConnectionsSwitch).toBeChecked();
await this.saveButton.click();
}
async disallowCustomConnections() {
await expect(this.disableConnectionsSwitch).toBeChecked();
await expect(this.allowCustomConnectionsSwitch).toBeChecked();
await this.allowCustomConnectionsSwitch.uncheck();
await expect(this.allowCustomConnectionsSwitch).not.toBeChecked();
await this.saveButton.click();
}
async disallowSharedConnections() {
await expect(this.disableConnectionsSwitch).toBeChecked();
await expect(this.allowSharedConnectionsSwitch).toBeChecked();
await this.allowSharedConnectionsSwitch.uncheck();
await expect(this.allowSharedConnectionsSwitch).not.toBeChecked();
await this.saveButton.click();
}
async allowConnections() {
await expect(this.disableConnectionsSwitch).toBeChecked();
await this.disableConnectionsSwitch.uncheck();
await expect(this.disableConnectionsSwitch).not.toBeChecked();
await this.saveButton.click();
}
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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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();
};

View File

@@ -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);
};

View File

@@ -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);
};

View File

@@ -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 },

View File

@@ -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",

View File

@@ -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 },
},

View File

@@ -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');
@@ -28,10 +30,11 @@ 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
page,
}) => {
await adminApplicationsPage.openApplication('Carbone');
await expect(page.url()).toContain('/admin-settings/apps/carbone/settings');
@@ -57,7 +60,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 +78,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 +98,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 +108,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 +134,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 +153,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 +171,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 +193,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 +208,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 +226,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 +243,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);

View File

@@ -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',
});

View File

@@ -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);
});
});
});

View File

@@ -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!'
);
});
});
});
});

View File

@@ -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 () => {

View File

@@ -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"

View File

@@ -30,7 +30,7 @@ const PermissionCatalogField = ({
if (isPermissionCatalogLoading) return <PermissionCatalogFieldLoader />;
return (
<TableContainer component={Paper}>
<TableContainer data-test="permissions-catalog" component={Paper}>
<Table>
<TableHead>
<TableRow>