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: push:
branches: branches:
- main - main
# TODO: Add pull request after optimizing the total excecution time of the test suite. pull_request:
# pull_request: paths:
# paths: - 'packages/backend/**'
# - 'packages/backend/**' - 'packages/e2e-tests/**'
# - 'packages/e2e-tests/**' - 'packages/web/**'
# - 'packages/web/**' - '!packages/backend/src/apps/**'
# - '!packages/backend/src/apps/**'
workflow_dispatch: workflow_dispatch:
env: env:
@@ -56,27 +55,44 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: '18'
- name: Install web dependencies cache: 'yarn'
run: yarn cache-dependency-path: |
working-directory: ./packages/web packages/backend/yarn.lock
packages/web/yarn.lock
packages/e2e-tests/yarn.lock
- name: Install backend dependencies - name: Install backend dependencies
run: yarn run: yarn --frozen-lockfile
working-directory: ./packages/backend working-directory: ./packages/backend
- name: Install web dependencies
run: yarn --frozen-lockfile
working-directory: ./packages/web
- name: Install e2e-tests dependencies - name: Install e2e-tests dependencies
run: yarn run: yarn --frozen-lockfile
working-directory: ./packages/e2e-tests 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 - name: Install Playwright Browsers
run: yarn playwright install --with-deps run: yarn playwright install --with-deps
working-directory: ./packages/e2e-tests working-directory: ./packages/e2e-tests
if: steps.playwright-cache.outputs.cache-hit != 'true'
- name: Build Automatisch web - name: Build Automatisch web
run: yarn build run: yarn build
working-directory: ./packages/web
env: env:
# Keep this until we clean up warnings in build processes # Keep this until we clean up warnings in build processes
CI: false CI: false
working-directory: ./packages/web
- name: Migrate database - name: Migrate database
working-directory: ./packages/backend working-directory: ./packages/backend
run: yarn db:migrate run: yarn db:migrate
@@ -116,6 +132,7 @@ jobs:
env: env:
LOGIN_EMAIL: user@automatisch.io LOGIN_EMAIL: user@automatisch.io
LOGIN_PASSWORD: sample LOGIN_PASSWORD: sample
BACKEND_APP_URL: http://localhost:3000
BASE_URL: http://localhost:3000 BASE_URL: http://localhost:3000
GITHUB_CLIENT_ID: 1c0417daf898adfbd99a GITHUB_CLIENT_ID: 1c0417daf898adfbd99a
GITHUB_CLIENT_SECRET: 3328fa814dd582ccd03dbe785cfd683fb8da92b3 GITHUB_CLIENT_SECRET: 3328fa814dd582ccd03dbe785cfd683fb8da92b3

View File

@@ -2,4 +2,5 @@ POSTGRES_DB=automatisch
POSTGRES_USER=automatisch_user POSTGRES_USER=automatisch_user
POSTGRES_PASSWORD=automatisch_password POSTGRES_PASSWORD=automatisch_password
POSTGRES_PORT=5432 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() { async allowCustomConnections() {
await expect(this.disableConnectionsSwitch).not.toBeChecked(); await expect(this.allowCustomConnectionsSwitch).not.toBeChecked();
await this.allowCustomConnectionsSwitch.check(); await this.allowCustomConnectionsSwitch.check();
await expect(this.allowCustomConnectionsSwitch).toBeChecked();
await this.saveButton.click(); await this.saveButton.click();
} }
async allowSharedConnections() { async allowSharedConnections() {
await expect(this.disableConnectionsSwitch).not.toBeChecked(); await expect(this.allowSharedConnectionsSwitch).not.toBeChecked();
await this.allowSharedConnectionsSwitch.check(); await this.allowSharedConnectionsSwitch.check();
await expect(this.allowSharedConnectionsSwitch).toBeChecked();
await this.saveButton.click(); await this.saveButton.click();
} }
async disallowConnections() { async disallowConnections() {
await expect(this.disableConnectionsSwitch).not.toBeChecked(); await expect(this.disableConnectionsSwitch).not.toBeChecked();
await this.disableConnectionsSwitch.check(); await this.disableConnectionsSwitch.check();
await expect(this.disableConnectionsSwitch).toBeChecked();
await this.saveButton.click(); await this.saveButton.click();
} }
async disallowCustomConnections() { async disallowCustomConnections() {
await expect(this.disableConnectionsSwitch).toBeChecked(); await expect(this.allowCustomConnectionsSwitch).toBeChecked();
await this.allowCustomConnectionsSwitch.uncheck(); await this.allowCustomConnectionsSwitch.uncheck();
await expect(this.allowCustomConnectionsSwitch).not.toBeChecked();
await this.saveButton.click(); await this.saveButton.click();
} }
async disallowSharedConnections() { async disallowSharedConnections() {
await expect(this.disableConnectionsSwitch).toBeChecked(); await expect(this.allowSharedConnectionsSwitch).toBeChecked();
await this.allowSharedConnectionsSwitch.uncheck(); await this.allowSharedConnectionsSwitch.uncheck();
await expect(this.allowSharedConnectionsSwitch).not.toBeChecked();
await this.saveButton.click(); await this.saveButton.click();
} }
async allowConnections() { async allowConnections() {
await expect(this.disableConnectionsSwitch).toBeChecked(); await expect(this.disableConnectionsSwitch).toBeChecked();
await this.disableConnectionsSwitch.uncheck(); await this.disableConnectionsSwitch.uncheck();
await expect(this.disableConnectionsSwitch).not.toBeChecked();
await this.saveButton.click(); await this.saveButton.click();
} }
async expectSuccessSnackbarToBeVisible() { async expectSuccessSnackbarToBeVisible() {
await expect(this.successSnackbar).toHaveCount(1); const snackbars = await this.successSnackbar.all();
await this.successSnackbar.click(); for (const snackbar of snackbars) {
await expect(this.successSnackbar).toHaveCount(0); 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 { AuthenticatedPage } = require('../authenticated-page');
const { RoleConditionsModal } = require('./role-conditions-modal'); const { RoleConditionsModal } = require('./role-conditions-modal');
@@ -16,6 +18,7 @@ export class AdminCreateRolePage extends AuthenticatedPage {
this.executionRow = page.getByTestId('Execution-permission-row'); this.executionRow = page.getByTestId('Execution-permission-row');
this.flowRow = page.getByTestId('Flow-permission-row'); this.flowRow = page.getByTestId('Flow-permission-row');
this.pageTitle = page.getByTestId('create-role-title'); 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}`); 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.roleInput = page.getByTestId('role.id-autocomplete');
this.createButton = page.getByTestId('create-button'); this.createButton = page.getByTestId('create-button');
this.pageTitle = page.getByTestId('create-user-title'); this.pageTitle = page.getByTestId('create-user-title');
this.invitationEmailInfoAlert = page.getByTestId('invitation-email-info-alert'); this.invitationEmailInfoAlert = page.getByTestId(
this.acceptInvitationLink = page.getByTestId('invitation-email-info-alert').getByRole('link'); 'invitation-email-info-alert'
);
this.acceptInvitationLink = page
.getByTestId('invitation-email-info-alert')
.getByRole('link');
} }
seed(seed) { seed(seed) {

View File

@@ -95,7 +95,6 @@ export class AdminUsersPage extends AuthenticatedPage {
}); });
} }
const rowLocator = await this.getUserRowByEmail(email); const rowLocator = await this.getUserRowByEmail(email);
console.log('rowLocator.count', email, await rowLocator.count());
if ((await rowLocator.count()) === 1) { if ((await rowLocator.count()) === 1) {
return rowLocator; 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 * Closes all snackbars, should be replaced later
*/ */
async closeSnackbar() { async closeAllSnackbars() {
const snackbars = await this.snackbar.all(); const snackbars = await this.snackbar.all();
for (const snackbar of snackbars) { for (const snackbar of snackbars) {
await snackbar.click(); 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 fileExtension = 'js';
const knexConfig = { const knexConfig = {
@@ -7,7 +9,7 @@ const knexConfig = {
user: process.env.POSTGRES_USERNAME, user: process.env.POSTGRES_USERNAME,
port: process.env.POSTGRES_PORT, port: process.env.POSTGRES_PORT,
password: process.env.POSTGRES_PASSWORD, password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE database: process.env.POSTGRES_DATABASE,
}, },
searchPath: ['public'], searchPath: ['public'],
pool: { min: 0, max: 20 }, pool: { min: 0, max: 20 },

View File

@@ -26,7 +26,8 @@
}, },
"devDependencies": { "devDependencies": {
"@faker-js/faker": "^8.2.0", "@faker-js/faker": "^8.2.0",
"@playwright/test": "^1.45.1" "@playwright/test": "1.49.0",
"objection": "^3.1.5"
}, },
"dependencies": { "dependencies": {
"axios": "^1.6.0", "axios": "^1.6.0",

View File

@@ -15,9 +15,9 @@ module.exports = defineConfig({
fullyParallel: true, fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */ /* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI, forbidOnly: !!process.env.CI,
retries: 0, retries: process.env.CI ? 1 : 0,
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined, workers: undefined,
/* Timeout threshold for each test */ /* Timeout threshold for each test */
timeout: 30000, timeout: 30000,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* 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', baseURL: process.env.BASE_URL || 'http://localhost:3001',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ /* 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', testIdAttribute: 'data-test',
viewport: { width: 1280, height: 720 }, viewport: { width: 1280, height: 720 },
}, },

View File

@@ -5,16 +5,18 @@ test.describe('Admin Applications', () => {
test.beforeAll(async () => { test.beforeAll(async () => {
const deleteAppAuthClients = { const deleteAppAuthClients = {
text: 'DELETE FROM app_auth_clients WHERE app_key in ($1, $2, $3, $4, $5)', 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 = { const deleteAppConfigs = {
text: 'DELETE FROM app_configs WHERE key in ($1, $2, $3, $4, $5)', 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 { try {
const deleteAppAuthClientsResult = await pgPool.query(deleteAppAuthClients); const deleteAppAuthClientsResult = await pgPool.query(
deleteAppAuthClients
);
expect(deleteAppAuthClientsResult.command).toBe('DELETE'); expect(deleteAppAuthClientsResult.command).toBe('DELETE');
const deleteAppConfigsResult = await pgPool.query(deleteAppConfigs); const deleteAppConfigsResult = await pgPool.query(deleteAppConfigs);
expect(deleteAppConfigsResult.command).toBe('DELETE'); expect(deleteAppConfigsResult.command).toBe('DELETE');
@@ -28,10 +30,11 @@ test.describe('Admin Applications', () => {
await adminApplicationsPage.navigateTo(); 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, adminApplicationsPage,
adminApplicationSettingsPage, adminApplicationSettingsPage,
page page,
}) => { }) => {
await adminApplicationsPage.openApplication('Carbone'); await adminApplicationsPage.openApplication('Carbone');
await expect(page.url()).toContain('/admin-settings/apps/carbone/settings'); await expect(page.url()).toContain('/admin-settings/apps/carbone/settings');
@@ -57,7 +60,7 @@ test.describe('Admin Applications', () => {
adminApplicationsPage, adminApplicationsPage,
adminApplicationSettingsPage, adminApplicationSettingsPage,
flowEditorPage, flowEditorPage,
page page,
}) => { }) => {
await adminApplicationsPage.openApplication('Spotify'); await adminApplicationsPage.openApplication('Spotify');
await expect(page.url()).toContain('/admin-settings/apps/spotify/settings'); await expect(page.url()).toContain('/admin-settings/apps/spotify/settings');
@@ -75,11 +78,15 @@ test.describe('Admin Applications', () => {
const triggerStep = flowEditorPage.flowStep.last(); const triggerStep = flowEditorPage.flowStep.last();
await triggerStep.click(); await triggerStep.click();
await flowEditorPage.chooseAppAndEvent("Spotify", "Create Playlist"); await flowEditorPage.chooseAppAndEvent('Spotify', 'Create Playlist');
await flowEditorPage.connectionAutocomplete.click(); await flowEditorPage.connectionAutocomplete.click();
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' }); const newConnectionOption = page
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' }); .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).toBeEnabled();
await expect(newConnectionOption).toHaveCount(1); await expect(newConnectionOption).toHaveCount(1);
@@ -91,7 +98,7 @@ test.describe('Admin Applications', () => {
adminApplicationSettingsPage, adminApplicationSettingsPage,
adminApplicationAuthClientsPage, adminApplicationAuthClientsPage,
flowEditorPage, flowEditorPage,
page page,
}) => { }) => {
await adminApplicationsPage.openApplication('Reddit'); await adminApplicationsPage.openApplication('Reddit');
await expect(page.url()).toContain('/admin-settings/apps/reddit/settings'); await expect(page.url()).toContain('/admin-settings/apps/reddit/settings');
@@ -101,13 +108,21 @@ test.describe('Admin Applications', () => {
await adminApplicationAuthClientsPage.openAuthClientsTab(); await adminApplicationAuthClientsPage.openAuthClientsTab();
await adminApplicationAuthClientsPage.openFirstAuthClientCreateForm(); 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.getByTestId('switch')).check();
await authClientForm.locator(page.locator('[name="name"]')).fill('redditAuthClient'); await authClientForm
await authClientForm.locator(page.locator('[name="clientId"]')).fill('redditClientId'); .locator(page.locator('[name="name"]'))
await authClientForm.locator(page.locator('[name="clientSecret"]')).fill('redditClientSecret'); .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.submitAuthClientForm();
await adminApplicationAuthClientsPage.authClientShouldBeVisible('redditAuthClient'); await adminApplicationAuthClientsPage.authClientShouldBeVisible(
'redditAuthClient'
);
await page.goto('/'); await page.goto('/');
await page.getByTestId('create-flow-button').click(); await page.getByTestId('create-flow-button').click();
@@ -119,11 +134,15 @@ test.describe('Admin Applications', () => {
const triggerStep = flowEditorPage.flowStep.last(); const triggerStep = flowEditorPage.flowStep.last();
await triggerStep.click(); await triggerStep.click();
await flowEditorPage.chooseAppAndEvent("Reddit", "Create link post"); await flowEditorPage.chooseAppAndEvent('Reddit', 'Create link post');
await flowEditorPage.connectionAutocomplete.click(); await flowEditorPage.connectionAutocomplete.click();
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' }); const newConnectionOption = page
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' }); .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(newConnectionOption).toHaveCount(0);
await expect(newSharedConnectionOption).toBeEnabled(); await expect(newSharedConnectionOption).toBeEnabled();
@@ -134,7 +153,7 @@ test.describe('Admin Applications', () => {
adminApplicationsPage, adminApplicationsPage,
adminApplicationSettingsPage, adminApplicationSettingsPage,
flowEditorPage, flowEditorPage,
page page,
}) => { }) => {
await adminApplicationsPage.openApplication('DeepL'); await adminApplicationsPage.openApplication('DeepL');
await expect(page.url()).toContain('/admin-settings/apps/deepl/settings'); await expect(page.url()).toContain('/admin-settings/apps/deepl/settings');
@@ -152,12 +171,18 @@ test.describe('Admin Applications', () => {
const triggerStep = flowEditorPage.flowStep.last(); const triggerStep = flowEditorPage.flowStep.last();
await triggerStep.click(); await triggerStep.click();
await flowEditorPage.chooseAppAndEvent("DeepL", "Translate text"); await flowEditorPage.chooseAppAndEvent('DeepL', 'Translate text');
await flowEditorPage.connectionAutocomplete.click(); await flowEditorPage.connectionAutocomplete.click();
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' }); const newConnectionOption = page
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' }); .getByRole('option')
const noConnectionsOption = page.locator('.MuiAutocomplete-noOptions').filter({ hasText: 'No options' }); .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(noConnectionsOption).toHaveCount(1);
await expect(newConnectionOption).toHaveCount(0); await expect(newConnectionOption).toHaveCount(0);
@@ -168,11 +193,11 @@ test.describe('Admin Applications', () => {
adminApplicationsPage, adminApplicationsPage,
adminApplicationSettingsPage, adminApplicationSettingsPage,
flowEditorPage, flowEditorPage,
page page,
}) => { }) => {
const queryUser = { const queryUser = {
text: 'SELECT * FROM users WHERE email = $1', text: 'SELECT * FROM users WHERE email = $1',
values: [process.env.LOGIN_EMAIL] values: [process.env.LOGIN_EMAIL],
}; };
try { 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)', text: 'INSERT INTO connections (key, data, user_id, verified, draft) VALUES ($1, $2, $3, $4, $5)',
values: [ values: [
'mailchimp', '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, queryUserResult.rows[0].id,
'true', 'true',
'false' 'false',
], ],
}; };
const createMailchimpConnectionResult = await pgPool.query(createMailchimpConnection); const createMailchimpConnectionResult = await pgPool.query(
createMailchimpConnection
);
expect(createMailchimpConnectionResult.rowCount).toBe(1); expect(createMailchimpConnectionResult.rowCount).toBe(1);
expect(createMailchimpConnectionResult.command).toBe('INSERT'); expect(createMailchimpConnectionResult.command).toBe('INSERT');
} catch (err) { } catch (err) {
@@ -199,7 +226,9 @@ test.describe('Admin Applications', () => {
} }
await adminApplicationsPage.openApplication('Mailchimp'); 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.disallowConnections();
await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible(); await adminApplicationSettingsPage.expectSuccessSnackbarToBeVisible();
@@ -214,14 +243,22 @@ test.describe('Admin Applications', () => {
const triggerStep = flowEditorPage.flowStep.last(); const triggerStep = flowEditorPage.flowStep.last();
await triggerStep.click(); await triggerStep.click();
await flowEditorPage.chooseAppAndEvent("Mailchimp", "Create campaign"); await flowEditorPage.chooseAppAndEvent('Mailchimp', 'Create campaign');
await flowEditorPage.connectionAutocomplete.click(); await flowEditorPage.connectionAutocomplete.click();
await expect(page.getByRole('option').first()).toHaveText('Unnamed'); await expect(page.getByRole('option').first()).toHaveText('Unnamed');
const existingConnection = page.getByRole('option').filter({ hasText: 'Unnamed' }); const existingConnection = page
const newConnectionOption = page.getByRole('option').filter({ hasText: 'Add new connection' }); .getByRole('option')
const newSharedConnectionOption = page.getByRole('option').filter({ hasText: 'Add new shared connection' }); .filter({ hasText: 'Unnamed' });
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(await existingConnection.count()).toBeGreaterThan(0); await expect(await existingConnection.count()).toBeGreaterThan(0);
await expect(noConnectionsOption).toHaveCount(0); await expect(noConnectionsOption).toHaveCount(0);

View File

@@ -22,22 +22,18 @@ test.describe('Role management page', () => {
await adminRolesPage.navigateTo(); await adminRolesPage.navigateTo();
await adminRolesPage.createRoleButton.click(); await adminRolesPage.createRoleButton.click();
await adminCreateRolePage.isMounted(); await adminCreateRolePage.isMounted();
await adminCreateRolePage.waitForPermissionsCatalogToVisible();
await adminCreateRolePage.nameInput.fill('Create Edit Test'); await adminCreateRolePage.nameInput.fill('Create Edit Test');
await adminCreateRolePage.descriptionInput.fill('Test description'); await adminCreateRolePage.descriptionInput.fill('Test description');
await adminCreateRolePage.createButton.click(); await adminCreateRolePage.createButton.click();
await adminCreateRolePage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminCreateRolePage.getSnackbarData( const snackbar = await adminCreateRolePage.getSnackbarData(
'snackbar-create-role-success' 'snackbar-create-role-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminCreateRolePage.closeSnackbar();
}); });
let roleRow = await test.step( let roleRow =
'Make sure role data is correct', await test.step('Make sure role data is correct', async () => {
async () => {
const roleRow = await adminRolesPage.getRoleRowByName( const roleRow = await adminRolesPage.getRoleRowByName(
'Create Edit Test' 'Create Edit Test'
); );
@@ -48,8 +44,7 @@ test.describe('Role management page', () => {
await expect(roleData.canEdit).toBe(true); await expect(roleData.canEdit).toBe(true);
await expect(roleData.canDelete).toBe(true); await expect(roleData.canDelete).toBe(true);
return roleRow; return roleRow;
} });
);
await test.step('Edit the role', async () => { await test.step('Edit the role', async () => {
await adminRolesPage.clickEditRole(roleRow); await adminRolesPage.clickEditRole(roleRow);
@@ -57,19 +52,14 @@ test.describe('Role management page', () => {
await adminEditRolePage.nameInput.fill('Create Update Test'); await adminEditRolePage.nameInput.fill('Create Update Test');
await adminEditRolePage.descriptionInput.fill('Update test description'); await adminEditRolePage.descriptionInput.fill('Update test description');
await adminEditRolePage.updateButton.click(); await adminEditRolePage.updateButton.click();
await adminEditRolePage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminEditRolePage.getSnackbarData( const snackbar = await adminEditRolePage.getSnackbarData(
'snackbar-edit-role-success' 'snackbar-edit-role-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminEditRolePage.closeSnackbar();
}); });
roleRow = await test.step( roleRow =
'Make sure changes reflected on roles page', await test.step('Make sure changes reflected on roles page', async () => {
async () => {
await adminRolesPage.isMounted(); await adminRolesPage.isMounted();
const roleRow = await adminRolesPage.getRoleRowByName( const roleRow = await adminRolesPage.getRoleRowByName(
'Create Update Test' 'Create Update Test'
@@ -81,8 +71,7 @@ test.describe('Role management page', () => {
await expect(roleData.canEdit).toBe(true); await expect(roleData.canEdit).toBe(true);
await expect(roleData.canDelete).toBe(true); await expect(roleData.canDelete).toBe(true);
return roleRow; return roleRow;
} });
);
await test.step('Delete the role', async () => { await test.step('Delete the role', async () => {
await adminRolesPage.clickDeleteRole(roleRow); await adminRolesPage.clickDeleteRole(roleRow);
@@ -91,14 +80,10 @@ test.describe('Role management page', () => {
state: 'attached', state: 'attached',
}); });
await deleteModal.deleteButton.click(); await deleteModal.deleteButton.click();
await adminRolesPage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminRolesPage.getSnackbarData( const snackbar = await adminRolesPage.getSnackbarData(
'snackbar-delete-role-success' 'snackbar-delete-role-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminRolesPage.closeSnackbar();
await deleteModal.modal.waitFor({ await deleteModal.modal.waitFor({
state: 'detached', state: 'detached',
}); });
@@ -173,60 +158,45 @@ test.describe('Role management page', () => {
await test.step('Create a new role', async () => { await test.step('Create a new role', async () => {
await adminRolesPage.createRoleButton.click(); await adminRolesPage.createRoleButton.click();
await adminCreateRolePage.isMounted(); await adminCreateRolePage.isMounted();
await adminCreateRolePage.waitForPermissionsCatalogToVisible();
await adminCreateRolePage.nameInput.fill('Delete Role'); await adminCreateRolePage.nameInput.fill('Delete Role');
await adminCreateRolePage.createButton.click(); await adminCreateRolePage.createButton.click();
await adminCreateRolePage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminCreateRolePage.getSnackbarData( const snackbar = await adminCreateRolePage.getSnackbarData(
'snackbar-create-role-success' 'snackbar-create-role-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminCreateRolePage.closeSnackbar();
}); });
await test.step( await test.step('Create a new user with the "Delete Role" role', async () => {
'Create a new user with the "Delete Role" role', await adminUsersPage.navigateTo();
async () => { await adminUsersPage.createUserButton.click();
await adminUsersPage.navigateTo(); await adminCreateUserPage.fullNameInput.fill('User Role Test');
await adminUsersPage.createUserButton.click(); await adminCreateUserPage.emailInput.fill(
await adminCreateUserPage.fullNameInput.fill('User Role Test'); 'user-role-test@automatisch.io'
await adminCreateUserPage.emailInput.fill( );
'user-role-test@automatisch.io' await adminCreateUserPage.roleInput.click();
); await adminCreateUserPage.page
await adminCreateUserPage.roleInput.click(); .getByRole('option', { name: 'Delete Role', exact: true })
await adminCreateUserPage.page .click();
.getByRole('option', { name: 'Delete Role', exact: true }) await adminCreateUserPage.createButton.click();
.click(); await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
await adminCreateUserPage.createButton.click(); state: 'attached',
await adminCreateUserPage.snackbar.waitFor({ });
state: 'attached', const snackbar = await adminUsersPage.getSnackbarData(
}); 'snackbar-create-user-success'
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({ );
state: 'attached', await expect(snackbar.variant).toBe('success');
}); });
const snackbar = await adminUsersPage.getSnackbarData( await test.step('Try to delete "Delete Role" role when new user has it', async () => {
'snackbar-create-user-success' await adminRolesPage.navigateTo();
); const row = await adminRolesPage.getRoleRowByName('Delete Role');
await expect(snackbar.variant).toBe('success'); const modal = await adminRolesPage.clickDeleteRole(row);
await adminUsersPage.closeSnackbar(); await modal.deleteButton.click();
} const snackbar = await adminRolesPage.getSnackbarData(
); 'snackbar-delete-role-error'
await test.step( );
'Try to delete "Delete Role" role when new user has it', await expect(snackbar.variant).toBe('error');
async () => { await modal.close();
await adminRolesPage.navigateTo(); });
const row = await adminRolesPage.getRoleRowByName('Delete Role');
const modal = await adminRolesPage.clickDeleteRole(row);
await modal.deleteButton.click();
await adminRolesPage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminRolesPage.getSnackbarData('snackbar-delete-role-error');
await expect(snackbar.variant).toBe('error');
await adminRolesPage.closeSnackbar();
await modal.close();
}
);
await test.step('Change the role the user has', async () => { await test.step('Change the role the user has', async () => {
await adminUsersPage.navigateTo(); await adminUsersPage.navigateTo();
await adminUsersPage.usersLoader.waitFor({ await adminUsersPage.usersLoader.waitFor({
@@ -241,14 +211,10 @@ test.describe('Role management page', () => {
.getByRole('option', { name: 'Admin' }) .getByRole('option', { name: 'Admin' })
.click(); .click();
await adminEditUserPage.updateButton.click(); await adminEditUserPage.updateButton.click();
await adminEditUserPage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminEditUserPage.getSnackbarData( const snackbar = await adminEditUserPage.getSnackbarData(
'snackbar-edit-user-success' 'snackbar-edit-user-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminEditUserPage.closeSnackbar();
}); });
await test.step('Delete the original role', async () => { await test.step('Delete the original role', async () => {
await adminRolesPage.navigateTo(); await adminRolesPage.navigateTo();
@@ -256,14 +222,10 @@ test.describe('Role management page', () => {
const modal = await adminRolesPage.clickDeleteRole(row); const modal = await adminRolesPage.clickDeleteRole(row);
await expect(modal.modal).toBeVisible(); await expect(modal.modal).toBeVisible();
await modal.deleteButton.click(); await modal.deleteButton.click();
await adminRolesPage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminRolesPage.getSnackbarData( const snackbar = await adminRolesPage.getSnackbarData(
'snackbar-delete-role-success' 'snackbar-delete-role-success'
); );
await expect(snackbar.variant).toBe('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 test.step('Create a new role', async () => {
await adminRolesPage.createRoleButton.click(); await adminRolesPage.createRoleButton.click();
await adminCreateRolePage.isMounted(); await adminCreateRolePage.isMounted();
await adminCreateRolePage.waitForPermissionsCatalogToVisible();
await adminCreateRolePage.nameInput.fill('Cannot Delete Role'); await adminCreateRolePage.nameInput.fill('Cannot Delete Role');
await adminCreateRolePage.createButton.click(); await adminCreateRolePage.createButton.click();
await adminCreateRolePage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminCreateRolePage.getSnackbarData( const snackbar = await adminCreateRolePage.getSnackbarData(
'snackbar-create-role-success' 'snackbar-create-role-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminCreateRolePage.closeSnackbar();
}); });
await test.step('Create a new user with this role', async () => { await test.step('Create a new user with this role', async () => {
await adminUsersPage.navigateTo(); await adminUsersPage.navigateTo();
@@ -301,9 +260,6 @@ test.describe('Role management page', () => {
.getByRole('option', { name: 'Cannot Delete Role' }) .getByRole('option', { name: 'Cannot Delete Role' })
.click(); .click();
await adminCreateUserPage.createButton.click(); await adminCreateUserPage.createButton.click();
await adminCreateUserPage.snackbar.waitFor({
state: 'attached',
});
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({ await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
state: 'attached', state: 'attached',
}); });
@@ -311,40 +267,34 @@ test.describe('Role management page', () => {
'snackbar-create-user-success' 'snackbar-create-user-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminCreateUserPage.closeSnackbar();
}); });
await test.step('Delete this user', async () => { await test.step('Delete this user', async () => {
await adminUsersPage.navigateTo(); await adminUsersPage.navigateTo();
const row = await adminUsersPage.findUserPageWithEmail( const row = await adminUsersPage.findUserPageWithEmail(
'user-delete-role-test@automatisch.io' 'user-delete-role-test@automatisch.io'
); );
// await test.waitForTimeout(10000);
const modal = await adminUsersPage.clickDeleteUser(row); const modal = await adminUsersPage.clickDeleteUser(row);
await modal.deleteButton.click(); await modal.deleteButton.click();
await adminUsersPage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminUsersPage.getSnackbarData( const snackbar = await adminUsersPage.getSnackbarData(
'snackbar-delete-user-success' 'snackbar-delete-user-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminUsersPage.closeSnackbar();
}); });
await test.step('Try deleting this role', async () => { await test.step('Try deleting this role', async () => {
await adminRolesPage.navigateTo(); await adminRolesPage.navigateTo();
const row = await adminRolesPage.getRoleRowByName('Cannot Delete Role'); const row = await adminRolesPage.getRoleRowByName('Cannot Delete Role');
const modal = await adminRolesPage.clickDeleteRole(row); const modal = await adminRolesPage.clickDeleteRole(row);
await modal.deleteButton.click(); await modal.deleteButton.click();
await adminRolesPage.snackbar.waitFor({ const snackbar = await adminRolesPage.getSnackbarData(
state: 'attached', 'snackbar-delete-role-error'
}); );
await expect(snackbar.variant).toBe('error');
/* /*
* TODO: await snackbar - make assertions based on product * TODO: await snackbar - make assertions based on product
* decisions * decisions
const snackbar = await adminRolesPage.getSnackbarData(); const snackbar = await adminRolesPage.getSnackbarData();
await expect(snackbar.variant).toBe('...'); await expect(snackbar.variant).toBe('...');
*/ */
await adminRolesPage.closeSnackbar();
}); });
}); });
}); });
@@ -362,16 +312,13 @@ test('Accessibility of role management page', async ({
await adminRolesPage.navigateTo(); await adminRolesPage.navigateTo();
await adminRolesPage.createRoleButton.click(); await adminRolesPage.createRoleButton.click();
await adminCreateRolePage.isMounted(); await adminCreateRolePage.isMounted();
await adminCreateRolePage.waitForPermissionsCatalogToVisible();
await adminCreateRolePage.nameInput.fill('Basic Test'); await adminCreateRolePage.nameInput.fill('Basic Test');
await adminCreateRolePage.createButton.click(); await adminCreateRolePage.createButton.click();
await adminCreateRolePage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminCreateRolePage.getSnackbarData( const snackbar = await adminCreateRolePage.getSnackbarData(
'snackbar-create-role-success' 'snackbar-create-role-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminCreateRolePage.closeSnackbar();
}); });
await test.step('Create a new user with the basic role', async () => { 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' }) .getByRole('option', { name: 'Basic Test' })
.click(); .click();
await adminCreateUserPage.createButton.click(); await adminCreateUserPage.createButton.click();
await adminCreateUserPage.snackbar.waitFor({
state: 'attached',
});
await adminCreateUserPage.invitationEmailInfoAlert.waitFor({ await adminCreateUserPage.invitationEmailInfoAlert.waitFor({
state: 'attached', state: 'attached',
}); });
@@ -395,56 +339,46 @@ test('Accessibility of role management page', async ({
'snackbar-create-user-success' 'snackbar-create-user-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminCreateUserPage.closeSnackbar();
}); });
await test.step('Logout and login to the basic role user', async () => { await test.step('Logout and login to the basic role user', async () => {
const acceptInvitationLink = await adminCreateUserPage.acceptInvitationLink; const acceptInvitationLink = await adminCreateUserPage.acceptInvitationLink;
console.log(acceptInvitationLink);
const acceptInvitationUrl = await acceptInvitationLink.textContent(); const acceptInvitationUrl = await acceptInvitationLink.textContent();
console.log(acceptInvitationUrl);
const acceptInvitatonToken = acceptInvitationUrl.split('?token=')[1]; const acceptInvitatonToken = acceptInvitationUrl.split('?token=')[1];
await page.getByTestId('profile-menu-button').click(); await page.getByTestId('profile-menu-button').click();
await page.getByTestId('logout-item').click(); await page.getByTestId('logout-item').click();
const acceptInvitationPage = new AcceptInvitation(page); const acceptInvitationPage = new AcceptInvitation(page);
await acceptInvitationPage.open(acceptInvitatonToken); await acceptInvitationPage.open(acceptInvitatonToken);
await acceptInvitationPage.acceptInvitation('sample'); await acceptInvitationPage.acceptInvitation('sample');
const loginPage = new LoginPage(page); const loginPage = new LoginPage(page);
// await loginPage.isMounted();
await loginPage.login('basic-role-test@automatisch.io', 'sample'); await loginPage.login('basic-role-test@automatisch.io', 'sample');
await expect(loginPage.loginButton).not.toBeVisible(); await expect(loginPage.loginButton).not.toBeVisible();
await expect(page).toHaveURL('/flows'); await expect(page).toHaveURL('/flows');
}); });
await test.step( await test.step('Navigate to the admin settings page and make sure it is blank', async () => {
'Navigate to the admin settings page and make sure it is blank', const pageUrl = new URL(page.url());
async () => { const url = `${pageUrl.origin}/admin-settings/users`;
const pageUrl = new URL(page.url()); await page.goto(url);
const url = `${pageUrl.origin}/admin-settings/users`; await page.waitForTimeout(750);
await page.goto(url); const isUnmounted = await page.evaluate(() => {
await page.waitForTimeout(750); // eslint-disable-next-line no-undef
const isUnmounted = await page.evaluate(() => { const root = document.querySelector('#root');
// eslint-disable-next-line no-undef
const root = document.querySelector('#root');
if (root) { if (root) {
// We have react query devtools only in dev env. // We have react query devtools only in dev env.
// In production, there is nothing in root. // In production, there is nothing in root.
// That's why `<= 1`. // That's why `<= 1`.
return root.children.length <= 1; return root.children.length <= 1;
} }
return false; return false;
}); });
await expect(isUnmounted).toBe(true); await expect(isUnmounted).toBe(true);
} });
);
await test.step('Log back into the admin account', async () => { await test.step('Log back into the admin account', async () => {
await page.goto('/'); await page.goto('/');
@@ -465,10 +399,10 @@ test('Accessibility of role management page', async ({
await adminEditUserPage.roleInput.click(); await adminEditUserPage.roleInput.click();
await adminEditUserPage.page.getByRole('option', { name: 'Admin' }).click(); await adminEditUserPage.page.getByRole('option', { name: 'Admin' }).click();
await adminEditUserPage.updateButton.click(); await adminEditUserPage.updateButton.click();
await adminEditUserPage.snackbar.waitFor({ const snackbar = await adminEditUserPage.getSnackbarData(
state: 'attached', 'snackbar-edit-user-success'
}); );
await adminEditUserPage.closeSnackbar(); await expect(snackbar.variant).toBe('success');
}); });
await test.step('Delete the role', async () => { await test.step('Delete the role', async () => {
@@ -480,14 +414,10 @@ test('Accessibility of role management page', async ({
state: 'attached', state: 'attached',
}); });
await deleteModal.deleteButton.click(); await deleteModal.deleteButton.click();
await adminRolesPage.snackbar.waitFor({
state: 'attached',
});
const snackbar = await adminRolesPage.getSnackbarData( const snackbar = await adminRolesPage.getSnackbarData(
'snackbar-delete-role-success' 'snackbar-delete-role-success'
); );
await expect(snackbar.variant).toBe('success'); await expect(snackbar.variant).toBe('success');
await adminRolesPage.closeSnackbar();
await deleteModal.modal.waitFor({ await deleteModal.modal.waitFor({
state: 'detached', state: 'detached',
}); });

View File

@@ -5,281 +5,235 @@ const { test, expect } = require('../../fixtures/index');
* otherwise tests will fail since users are only *soft*-deleted * otherwise tests will fail since users are only *soft*-deleted
*/ */
test.describe('User management page', () => { test.describe('User management page', () => {
test.beforeEach(async ({ adminUsersPage }) => { test.beforeEach(async ({ adminUsersPage }) => {
await adminUsersPage.navigateTo(); await adminUsersPage.navigateTo();
await adminUsersPage.closeSnackbar(); await adminUsersPage.closeAllSnackbars();
}); });
test( test('User creation and deletion process', async ({
'User creation and deletion process', adminCreateUserPage,
async ({ adminCreateUserPage, adminEditUserPage, adminUsersPage }) => { adminEditUserPage,
adminCreateUserPage.seed(9000); adminUsersPage,
const user = adminCreateUserPage.generateUser(); }) => {
await adminUsersPage.usersLoader.waitFor({ adminCreateUserPage.seed(9000);
state: 'detached' /* Note: state: 'visible' introduces flakiness 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 because visibility: hidden is used as part of the state transition in
notistack, see notistack, see
https://github.com/iamhosseindhv/notistack/blob/122f47057eb7ce5a1abfe923316cf8475303e99a/src/transitions/Collapse/Collapse.tsx#L110 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( const snackbar = await adminUsersPage.getSnackbarData(
'snackbar-create-user-success' 'snackbar-create-user-success'
);
await expect(snackbar.variant).toBe('success');
await adminUsersPage.navigateTo();
await adminUsersPage.closeSnackbar();
}
); );
await test.step( await expect(snackbar.variant).toBe('success');
'Check the user exists with the expected properties', await adminUsersPage.navigateTo();
async () => { });
await adminUsersPage.findUserPageWithEmail(user.email); await test.step('Check the user exists with the expected properties', async () => {
const userRow = await adminUsersPage.getUserRowByEmail(user.email); await adminUsersPage.findUserPageWithEmail(user.email);
const data = await adminUsersPage.getRowData(userRow); const userRow = await adminUsersPage.getUserRowByEmail(user.email);
await expect(data.email).toBe(user.email); const data = await adminUsersPage.getRowData(userRow);
await expect(data.fullName).toBe(user.fullName); await expect(data.email).toBe(user.email);
await expect(data.role).toBe('Admin'); 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( await expect(snackbar.variant).toBe('success');
'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.findUserPageWithEmail(user.email);
await adminUsersPage.clickEditUser(userRow); userRow = await adminUsersPage.getUserRowByEmail(user.email);
await adminEditUserPage.waitForLoad(user.fullName); const rowData = await adminUsersPage.getRowData(userRow);
const newUserInfo = adminEditUserPage.generateUser(); await expect(rowData.fullName).toBe(newUserInfo.fullName);
await adminEditUserPage.fullNameInput.fill(newUserInfo.fullName); });
await adminEditUserPage.updateButton.click(); 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( const snackbar = await adminUsersPage.getSnackbarData(
'snackbar-edit-user-success' 'snackbar-delete-user-success'
);
await expect(snackbar.variant).toBe('success');
await adminUsersPage.closeSnackbar();
await adminUsersPage.findUserPageWithEmail(user.email);
userRow = await adminUsersPage.getUserRowByEmail(user.email);
const rowData = await adminUsersPage.getRowData(userRow);
await expect(rowData.fullName).toBe(newUserInfo.fullName);
}
); );
await test.step( await expect(snackbar.variant).toBe('success');
'Delete user and check the page confirms this deletion', await expect(userRow).not.toBeVisible(false);
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( test('Creating a user which has been deleted', async ({
'snackbar-delete-user-success' adminCreateUserPage,
); adminUsersPage,
await expect(snackbar.variant).toBe('success'); }) => {
await adminUsersPage.closeSnackbar(); adminCreateUserPage.seed(9100);
await expect(userRow).not.toBeVisible(false); 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( await test.step('Delete the created user', async () => {
'Creating a user which has been deleted', await adminUsersPage.navigateTo();
async ({ adminCreateUserPage, adminUsersPage }) => { await adminUsersPage.findUserPageWithEmail(testUser.email);
adminCreateUserPage.seed(9100); const userRow = await adminUsersPage.getUserRowByEmail(testUser.email);
const testUser = adminCreateUserPage.generateUser(); await adminUsersPage.clickDeleteUser(userRow);
const modal = adminUsersPage.deleteUserModal;
await test.step( await modal.deleteButton.click();
'Create the test user', const snackbar = await adminUsersPage.getSnackbarData(
async () => { 'snackbar-delete-user-success'
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 expect(snackbar).not.toBeNull();
await expect(snackbar.variant).toBe('success');
await expect(userRow).not.toBeVisible(false);
});
await test.step( await test.step('Create the user again', async () => {
'Delete the created user', await adminUsersPage.createUserButton.click();
async () => { await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
await adminUsersPage.navigateTo(); await adminCreateUserPage.emailInput.fill(testUser.email);
await adminUsersPage.findUserPageWithEmail(testUser.email); await adminCreateUserPage.roleInput.click();
const userRow = await adminUsersPage.getUserRowByEmail(testUser.email); await adminCreateUserPage.page
await adminUsersPage.clickDeleteUser(userRow); .getByRole('option', { name: 'Admin' })
const modal = adminUsersPage.deleteUserModal; .click();
await modal.deleteButton.click(); await adminCreateUserPage.createButton.click();
const snackbar = await adminUsersPage.getSnackbarData( const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
'snackbar-delete-user-success' await expect(snackbar.variant).toBe('error');
); });
await expect(snackbar).not.toBeNull(); });
await expect(snackbar.variant).toBe('success');
await adminUsersPage.closeSnackbar(); test('Creating a user which already exists', async ({
await expect(userRow).not.toBeVisible(false); 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( await test.step('Create the user again', async () => {
'Create the user again', await adminUsersPage.navigateTo();
async () => { await adminUsersPage.createUserButton.click();
await adminUsersPage.createUserButton.click(); await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
await adminCreateUserPage.fullNameInput.fill(testUser.fullName); await adminCreateUserPage.emailInput.fill(testUser.email);
await adminCreateUserPage.emailInput.fill(testUser.email); const createUserPageUrl = page.url();
await adminCreateUserPage.roleInput.click(); await adminCreateUserPage.roleInput.click();
await adminCreateUserPage.page.getByRole( await adminCreateUserPage.page
'option', { name: 'Admin' } .getByRole('option', { name: 'Admin' })
).click(); .click();
await adminCreateUserPage.createButton.click(); await adminCreateUserPage.createButton.click();
const snackbar = await adminUsersPage.getSnackbarData('snackbar-error');
await expect(snackbar.variant).toBe('error'); await expect(page.url()).toBe(createUserPageUrl);
await adminUsersPage.closeSnackbar(); 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( await test.step('Create the second user', async () => {
'Creating a user which already exists', await adminUsersPage.navigateTo();
async ({ adminCreateUserPage, adminUsersPage, page }) => { await adminUsersPage.createUserButton.click();
adminCreateUserPage.seed(9200); await adminCreateUserPage.fullNameInput.fill(user2.fullName);
const testUser = adminCreateUserPage.generateUser(); await adminCreateUserPage.emailInput.fill(user2.email);
await adminCreateUserPage.roleInput.click();
await test.step( await adminCreateUserPage.page
'Create the test user', .getByRole('option', { name: 'Admin' })
async () => { .click();
await adminUsersPage.createUserButton.click(); await adminCreateUserPage.createButton.click();
await adminCreateUserPage.fullNameInput.fill(testUser.fullName); const snackbar = await adminUsersPage.getSnackbarData(
await adminCreateUserPage.emailInput.fill(testUser.email); 'snackbar-create-user-success'
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 expect(snackbar.variant).toBe('success');
});
await test.step( await test.step('Try editing the second user to have the email of the first user', async () => {
'Create the user again', await adminUsersPage.navigateTo();
async () => { await adminUsersPage.findUserPageWithEmail(user2.email);
await adminUsersPage.navigateTo(); let userRow = await adminUsersPage.getUserRowByEmail(user2.email);
await adminUsersPage.createUserButton.click(); await adminUsersPage.clickEditUser(userRow);
await adminCreateUserPage.fullNameInput.fill(testUser.fullName); await adminEditUserPage.waitForLoad(user2.fullName);
await adminCreateUserPage.emailInput.fill(testUser.email); await adminEditUserPage.emailInput.fill(user1.email);
const createUserPageUrl = page.url(); const editPageUrl = page.url();
await adminCreateUserPage.roleInput.click(); await adminEditUserPage.updateButton.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');
const snackbar = await adminUsersPage.getSnackbarData('snackbar-error'); await expect(snackbar.variant).toBe('error');
await expect(snackbar.variant).toBe('error'); await expect(page.url()).toBe(editPageUrl);
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);
}
);
}
);
}); });

View File

@@ -1,24 +1,55 @@
const { request } = require('@playwright/test');
const { test, expect } = require('../../fixtures/index'); 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.describe('Pop-up message on connections', () => {
test.beforeEach(async ({ flowEditorPage, page }) => { test.beforeEach(async ({ flowEditorPage, page }) => {
await page.getByTestId('create-flow-button').click(); const apiRequest = await request.newContext();
await page.waitForURL( const tokenJsonResponse = await getToken(apiRequest);
/\/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}/ const token = tokenJsonResponse.data.token;
);
await expect(page.getByTestId('flow-step')).toHaveCount(2);
await flowEditorPage.flowName.click(); let flow = await createFlow(apiRequest, token);
await flowEditorPage.flowNameInput.fill('PopupFlow'); const flowId = flow.data.id;
await flowEditorPage.createWebhookTrigger(true); 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'); const triggerStepId = flowSteps.find((step) => step.type === 'trigger').id;
await expect(flowEditorPage.continueButton).toHaveCount(1); const actionStepId = flowSteps.find((step) => step.type === 'action').id;
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.connectionAutocomplete.click();
await flowEditorPage.addNewConnectionItem.click(); }); await flowEditorPage.addNewConnectionItem.click();
});
test('should show error to remind to enable pop-up on connection create', async ({ test('should show error to remind to enable pop-up on connection create', async ({
page, page,
@@ -28,7 +59,7 @@ test.describe('Pop-up message on connections', () => {
// Inject script to override window.open // Inject script to override window.open
await page.evaluate(() => { await page.evaluate(() => {
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
window.open = function() { window.open = function () {
console.log('Popup blocked!'); console.log('Popup blocked!');
return null; return null;
}; };
@@ -37,8 +68,10 @@ test.describe('Pop-up message on connections', () => {
await addMattermostConnectionModal.fillConnectionForm(); await addMattermostConnectionModal.fillConnectionForm();
await addMattermostConnectionModal.submitConnectionForm(); await addMattermostConnectionModal.submitConnectionForm();
await expect(page.getByTestId("add-connection-error")).toHaveCount(1); 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')).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 ({ 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(); await addMattermostConnectionModal.submitConnectionForm();
const popup = await popupPromise; const popup = await popupPromise;
await expect(popup.url()).toContain("mattermost"); await expect(popup.url()).toContain('mattermost');
await expect(page.getByTestId("add-connection-error")).toHaveCount(0); await expect(page.getByTestId('add-connection-error')).toHaveCount(0);
await test.step('Should show error on failed credentials verification', async () => { await test.step('Should show error on failed credentials verification', async () => {
await popup.close(); await popup.close();
await expect(page.getByTestId("add-connection-error")).toHaveCount(1); 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')).toHaveText(
'Error occured while verifying credentials!'
);
}); });
}); });
}); });

View File

@@ -1,57 +1,38 @@
const { request } = require('@playwright/test');
const { publicTest, expect } = require('../../fixtures/index'); const { publicTest, expect } = require('../../fixtures/index');
const { AdminUsersPage } = require('../../fixtures/admin/users-page');
const { MyProfilePage } = require('../../fixtures/my-profile-page'); const { MyProfilePage } = require('../../fixtures/my-profile-page');
const { LoginPage } = require('../../fixtures/login-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', () => { publicTest.describe('My Profile', () => {
let testUser; let testUser;
publicTest.beforeEach( publicTest.beforeEach(
async ({ acceptInvitationPage, adminCreateUserPage, loginPage, page }) => { async ({ adminCreateUserPage, loginPage, page }) => {
let acceptInvitationLink; let addUserResponse;
const apiRequest = await request.newContext();
adminCreateUserPage.seed( adminCreateUserPage.seed(
Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER) Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
); );
testUser = adminCreateUserPage.generateUser(); 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 publicTest.step('create new user', async () => {
await adminUsersPage.navigateTo(); const tokenJsonResponse = await getToken(apiRequest);
await adminUsersPage.createUserButton.click(); addUserResponse = await addUser(
await adminCreateUserPage.fullNameInput.fill(testUser.fullName); apiRequest,
await adminCreateUserPage.emailInput.fill(testUser.email); tokenJsonResponse.data.token,
await adminCreateUserPage.roleInput.click(); {
await adminCreateUserPage.page fullName: testUser.fullName,
.getByRole('option', { name: 'Admin' }) email: testUser.email,
.click(); }
await adminCreateUserPage.createButton.click();
const snackbar = await adminUsersPage.getSnackbarData(
'snackbar-create-user-success'
); );
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 publicTest.step('accept invitation', async () => {
await page.goto(acceptInvitationLink); let acceptToken = addUserResponse.data.acceptInvitationUrl.split('=')[1];
await acceptInvitationPage.acceptInvitation(LoginPage.defaultPassword); await acceptInvitation(apiRequest, {token:acceptToken, password:LoginPage.defaultPassword});
}); });
await publicTest.step('login as new Admin', async () => { await publicTest.step('login as new Admin', async () => {

View File

@@ -79,7 +79,7 @@
"@nodelib/fs.scandir" "2.1.5" "@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0" fastq "^1.6.0"
"@playwright/test@^1.45.1": "@playwright/test@1.49.0":
version "1.49.0" version "1.49.0"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.0.tgz#74227385b58317ee076b86b56d0e1e1b25cff01e" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.0.tgz#74227385b58317ee076b86b56d0e1e1b25cff01e"
integrity sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw== 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" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== 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: ajv@^6.12.4:
version "6.12.6" version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" 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" json-schema-traverse "^0.4.1"
uri-js "^4.2.2" 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: ansi-regex@^5.0.1:
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 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" shebang-command "^2.0.0"
which "^2.0.1" 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: debug@4.3.4:
version "4.3.4" version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 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" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== 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: fastq@^1.6.0:
version "1.17.1" version "1.17.1"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" 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" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 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: json-stable-stringify-without-jsonify@^1.0.1:
version "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" 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" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== 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: once@^1.3.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -933,6 +974,11 @@ rechoir@^0.8.0:
dependencies: dependencies:
resolve "^1.20.0" 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: resolve-from@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 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 />; if (isPermissionCatalogLoading) return <PermissionCatalogFieldLoader />;
return ( return (
<TableContainer component={Paper}> <TableContainer data-test="permissions-catalog" component={Paper}>
<Table> <Table>
<TableHead> <TableHead>
<TableRow> <TableRow>