2024-04-02 12:13:06 +02:00
|
|
|
import { Page, expect } from '@playwright/test';
|
|
|
|
|
|
2025-06-24 17:42:41 +02:00
|
|
|
export const BROWSERS = ['chromium', 'webkit', 'firefox'];
|
|
|
|
|
|
2025-04-09 16:00:29 +02:00
|
|
|
export const CONFIG = {
|
|
|
|
|
AI_FEATURE_ENABLED: true,
|
|
|
|
|
CRISP_WEBSITE_ID: null,
|
|
|
|
|
COLLABORATION_WS_URL: 'ws://localhost:4444/collaboration/ws/',
|
2025-07-04 10:56:24 +02:00
|
|
|
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY: true,
|
2025-04-09 16:00:29 +02:00
|
|
|
ENVIRONMENT: 'development',
|
|
|
|
|
FRONTEND_CSS_URL: null,
|
|
|
|
|
FRONTEND_HOMEPAGE_FEATURE_ENABLED: true,
|
2025-03-25 13:01:48 +01:00
|
|
|
FRONTEND_THEME: null,
|
2025-04-09 16:00:29 +02:00
|
|
|
MEDIA_BASE_URL: 'http://localhost:8083',
|
|
|
|
|
LANGUAGES: [
|
|
|
|
|
['en-us', 'English'],
|
|
|
|
|
['fr-fr', 'Français'],
|
|
|
|
|
['de-de', 'Deutsch'],
|
|
|
|
|
['nl-nl', 'Nederlands'],
|
2025-05-02 16:00:45 +02:00
|
|
|
['es-es', 'Español'],
|
2025-04-09 16:00:29 +02:00
|
|
|
],
|
|
|
|
|
LANGUAGE_CODE: 'en-us',
|
|
|
|
|
POSTHOG_KEY: {},
|
|
|
|
|
SENTRY_DSN: null,
|
2025-05-07 12:06:16 +02:00
|
|
|
theme_customization: {},
|
2025-03-25 13:01:48 +01:00
|
|
|
} as const;
|
2025-04-09 16:00:29 +02:00
|
|
|
|
2025-05-13 11:24:02 +02:00
|
|
|
export const overrideConfig = async (
|
|
|
|
|
page: Page,
|
|
|
|
|
newConfig: { [K in keyof typeof CONFIG]?: unknown },
|
|
|
|
|
) =>
|
|
|
|
|
await page.route('**/api/v1.0/config/', async (route) => {
|
|
|
|
|
const request = route.request();
|
|
|
|
|
if (request.method().includes('GET')) {
|
|
|
|
|
await route.fulfill({
|
|
|
|
|
json: {
|
|
|
|
|
...CONFIG,
|
|
|
|
|
...newConfig,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
await route.continue();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2025-02-03 11:02:29 +01:00
|
|
|
export const keyCloakSignIn = async (
|
|
|
|
|
page: Page,
|
|
|
|
|
browserName: string,
|
|
|
|
|
fromHome: boolean = true,
|
|
|
|
|
) => {
|
|
|
|
|
if (fromHome) {
|
2025-03-25 13:01:48 +01:00
|
|
|
await page.getByRole('button', { name: 'Start Writing' }).first().click();
|
2025-02-03 11:02:29 +01:00
|
|
|
}
|
|
|
|
|
|
2024-06-06 22:13:18 +02:00
|
|
|
const login = `user-e2e-${browserName}`;
|
|
|
|
|
const password = `password-e2e-${browserName}`;
|
|
|
|
|
|
2024-10-23 11:11:29 +02:00
|
|
|
await expect(
|
|
|
|
|
page.locator('.login-pf-page-header').getByText('impress'),
|
|
|
|
|
).toBeVisible();
|
2024-06-06 22:13:18 +02:00
|
|
|
|
2024-10-23 11:11:29 +02:00
|
|
|
if (await page.getByLabel('Restart login').isVisible()) {
|
|
|
|
|
await page.getByLabel('Restart login').click();
|
2024-04-02 12:13:06 +02:00
|
|
|
}
|
2024-10-23 11:11:29 +02:00
|
|
|
|
|
|
|
|
await page.getByRole('textbox', { name: 'username' }).fill(login);
|
|
|
|
|
await page.getByRole('textbox', { name: 'password' }).fill(password);
|
|
|
|
|
await page.click('input[type="submit"]', { force: true });
|
2024-04-02 12:13:06 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const randomName = (name: string, browserName: string, length: number) =>
|
|
|
|
|
Array.from({ length }, (_el, index) => {
|
|
|
|
|
return `${browserName}-${Math.floor(Math.random() * 10000)}-${index}-${name}`;
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-25 14:49:53 +02:00
|
|
|
export const createDoc = async (
|
2024-04-02 12:13:06 +02:00
|
|
|
page: Page,
|
2024-06-25 14:49:53 +02:00
|
|
|
docName: string,
|
2024-04-02 12:13:06 +02:00
|
|
|
browserName: string,
|
2025-01-29 14:06:38 +01:00
|
|
|
length: number = 1,
|
2025-03-17 15:13:02 +01:00
|
|
|
isChild: boolean = false,
|
2024-04-02 12:13:06 +02:00
|
|
|
) => {
|
2024-06-25 14:49:53 +02:00
|
|
|
const randomDocs = randomName(docName, browserName, length);
|
2024-04-02 12:13:06 +02:00
|
|
|
|
2024-06-25 14:49:53 +02:00
|
|
|
for (let i = 0; i < randomDocs.length; i++) {
|
2025-03-17 15:13:02 +01:00
|
|
|
if (!isChild) {
|
|
|
|
|
const header = page.locator('header').first();
|
|
|
|
|
await header.locator('h2').getByText('Docs').click();
|
|
|
|
|
}
|
2024-07-05 14:14:43 +02:00
|
|
|
|
|
|
|
|
await page
|
2024-10-01 15:55:19 +02:00
|
|
|
.getByRole('button', {
|
2025-03-17 15:13:02 +01:00
|
|
|
name: isChild ? 'New page' : 'New doc',
|
2024-07-05 14:14:43 +02:00
|
|
|
})
|
2024-10-01 15:55:19 +02:00
|
|
|
.click();
|
2024-04-15 22:34:19 +02:00
|
|
|
|
2025-03-14 14:27:56 +01:00
|
|
|
await page.waitForURL('**/docs/**', {
|
|
|
|
|
timeout: 10000,
|
|
|
|
|
waitUntil: 'networkidle',
|
|
|
|
|
});
|
|
|
|
|
|
2025-01-29 14:06:38 +01:00
|
|
|
const input = page.getByLabel('doc title input');
|
2025-04-23 11:43:50 +02:00
|
|
|
await expect(input).toBeVisible();
|
2025-01-29 14:06:38 +01:00
|
|
|
await expect(input).toHaveText('');
|
2024-11-25 12:10:16 +01:00
|
|
|
await input.click();
|
2025-03-14 14:27:56 +01:00
|
|
|
|
2024-11-25 12:10:16 +01:00
|
|
|
await input.fill(randomDocs[i]);
|
|
|
|
|
await input.blur();
|
2024-04-15 22:34:19 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-05 14:14:43 +02:00
|
|
|
return randomDocs;
|
2024-04-15 22:34:19 +02:00
|
|
|
};
|
|
|
|
|
|
2024-11-25 12:10:16 +01:00
|
|
|
export const verifyDocName = async (page: Page, docName: string) => {
|
2025-03-25 13:01:48 +01:00
|
|
|
await expect(
|
|
|
|
|
page.getByLabel('It is the card information about the document.'),
|
|
|
|
|
).toBeVisible({
|
|
|
|
|
timeout: 10000,
|
|
|
|
|
});
|
|
|
|
|
|
2025-03-14 14:27:56 +01:00
|
|
|
try {
|
2025-03-25 13:01:48 +01:00
|
|
|
await expect(
|
|
|
|
|
page.getByRole('textbox', { name: 'doc title input' }),
|
|
|
|
|
).toHaveText(docName);
|
2025-03-14 14:27:56 +01:00
|
|
|
} catch {
|
|
|
|
|
await expect(page.getByRole('heading', { name: docName })).toBeVisible();
|
|
|
|
|
}
|
2024-11-25 12:10:16 +01:00
|
|
|
};
|
|
|
|
|
|
2024-04-02 12:13:06 +02:00
|
|
|
export const addNewMember = async (
|
|
|
|
|
page: Page,
|
|
|
|
|
index: number,
|
2025-05-06 14:25:10 +02:00
|
|
|
role: 'Administrator' | 'Owner' | 'Editor' | 'Reader',
|
2025-03-21 15:30:31 +01:00
|
|
|
fillText: string = 'user ',
|
2024-04-02 12:13:06 +02:00
|
|
|
) => {
|
|
|
|
|
const responsePromiseSearchUser = page.waitForResponse(
|
|
|
|
|
(response) =>
|
2025-03-21 15:30:31 +01:00
|
|
|
response.url().includes(`/users/?q=${encodeURIComponent(fillText)}`) &&
|
2024-04-02 12:13:06 +02:00
|
|
|
response.status() === 200,
|
|
|
|
|
);
|
2024-05-31 17:16:31 +02:00
|
|
|
|
2024-12-16 10:21:28 +01:00
|
|
|
const inputSearch = page.getByRole('combobox', {
|
|
|
|
|
name: 'Quick search input',
|
|
|
|
|
});
|
2024-04-02 12:13:06 +02:00
|
|
|
|
|
|
|
|
// Select a new user
|
|
|
|
|
await inputSearch.fill(fillText);
|
|
|
|
|
|
|
|
|
|
// Intercept response
|
|
|
|
|
const responseSearchUser = await responsePromiseSearchUser;
|
2025-03-21 15:30:31 +01:00
|
|
|
const users = (await responseSearchUser.json()) as {
|
2024-05-31 17:16:31 +02:00
|
|
|
email: string;
|
2024-04-02 12:13:06 +02:00
|
|
|
}[];
|
|
|
|
|
|
|
|
|
|
// Choose user
|
2024-05-31 17:16:31 +02:00
|
|
|
await page.getByRole('option', { name: users[index].email }).click();
|
2024-04-02 12:13:06 +02:00
|
|
|
|
|
|
|
|
// Choose a role
|
2024-12-16 10:21:28 +01:00
|
|
|
await page.getByLabel('doc-role-dropdown').click();
|
2025-02-25 15:44:33 +01:00
|
|
|
await page.getByLabel(role).click();
|
2024-12-16 10:21:28 +01:00
|
|
|
await page.getByRole('button', { name: 'Invite' }).click();
|
2024-04-02 12:13:06 +02:00
|
|
|
|
2024-05-31 17:16:31 +02:00
|
|
|
return users[index].email;
|
2024-04-02 12:13:06 +02:00
|
|
|
};
|
2024-07-05 14:14:43 +02:00
|
|
|
|
2025-01-29 14:06:38 +01:00
|
|
|
export const getGridRow = async (page: Page, title: string) => {
|
|
|
|
|
const docsGrid = page.getByRole('grid');
|
|
|
|
|
await expect(docsGrid).toBeVisible();
|
|
|
|
|
await expect(page.getByTestId('grid-loader')).toBeHidden();
|
|
|
|
|
|
|
|
|
|
const rows = docsGrid.getByRole('row');
|
|
|
|
|
|
|
|
|
|
const row = rows.filter({
|
|
|
|
|
hasText: title,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await expect(row).toBeVisible();
|
|
|
|
|
|
|
|
|
|
return row;
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-05 14:14:43 +02:00
|
|
|
interface GoToGridDocOptions {
|
|
|
|
|
nthRow?: number;
|
|
|
|
|
title?: string;
|
|
|
|
|
}
|
|
|
|
|
export const goToGridDoc = async (
|
|
|
|
|
page: Page,
|
|
|
|
|
{ nthRow = 1, title }: GoToGridDocOptions = {},
|
|
|
|
|
) => {
|
|
|
|
|
const header = page.locator('header').first();
|
|
|
|
|
await header.locator('h2').getByText('Docs').click();
|
|
|
|
|
|
2024-11-19 16:12:12 +01:00
|
|
|
const docsGrid = page.getByTestId('docs-grid');
|
|
|
|
|
await expect(docsGrid).toBeVisible();
|
2025-01-29 14:06:38 +01:00
|
|
|
await expect(page.getByTestId('grid-loader')).toBeHidden();
|
2024-07-05 14:14:43 +02:00
|
|
|
|
2024-11-19 16:12:12 +01:00
|
|
|
const rows = docsGrid.getByRole('row');
|
2025-01-09 09:15:21 +01:00
|
|
|
|
2024-07-05 14:14:43 +02:00
|
|
|
const row = title
|
|
|
|
|
? rows.filter({
|
|
|
|
|
hasText: title,
|
|
|
|
|
})
|
|
|
|
|
: rows.nth(nthRow);
|
|
|
|
|
|
2024-11-19 16:12:12 +01:00
|
|
|
await expect(row).toBeVisible();
|
2024-07-05 14:14:43 +02:00
|
|
|
|
2024-11-19 16:12:12 +01:00
|
|
|
const docTitleContent = row.locator('[aria-describedby="doc-title"]').first();
|
|
|
|
|
const docTitle = await docTitleContent.textContent();
|
2024-07-05 14:14:43 +02:00
|
|
|
expect(docTitle).toBeDefined();
|
|
|
|
|
|
2024-10-08 17:03:42 +02:00
|
|
|
await row.getByRole('link').first().click();
|
2024-07-05 14:14:43 +02:00
|
|
|
|
|
|
|
|
return docTitle as string;
|
|
|
|
|
};
|
2024-07-17 15:30:52 +02:00
|
|
|
|
2025-05-19 09:03:00 +02:00
|
|
|
export const updateDocTitle = async (page: Page, title: string) => {
|
|
|
|
|
const input = page.getByLabel('doc title input');
|
|
|
|
|
await expect(input).toBeVisible();
|
|
|
|
|
await expect(input).toHaveText('');
|
|
|
|
|
await input.click();
|
|
|
|
|
await input.fill(title);
|
|
|
|
|
await input.click();
|
|
|
|
|
await verifyDocName(page, title);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const waitForResponseCreateDoc = (page: Page) => {
|
|
|
|
|
return page.waitForResponse(
|
|
|
|
|
(response) =>
|
|
|
|
|
response.url().includes('/documents/') &&
|
|
|
|
|
response.url().includes('/children/') &&
|
|
|
|
|
response.request().method() === 'POST',
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const mockedDocument = async (page: Page, data: object) => {
|
2024-07-17 15:30:52 +02:00
|
|
|
await page.route('**/documents/**/', async (route) => {
|
|
|
|
|
const request = route.request();
|
2024-10-03 17:21:04 +02:00
|
|
|
if (
|
|
|
|
|
request.method().includes('GET') &&
|
|
|
|
|
!request.url().includes('page=') &&
|
|
|
|
|
!request.url().includes('versions') &&
|
|
|
|
|
!request.url().includes('accesses') &&
|
|
|
|
|
!request.url().includes('invitations')
|
|
|
|
|
) {
|
2024-07-17 15:30:52 +02:00
|
|
|
await route.fulfill({
|
|
|
|
|
json: {
|
2024-08-16 14:51:42 +02:00
|
|
|
id: 'mocked-document-id',
|
2024-07-17 15:30:52 +02:00
|
|
|
content: '',
|
|
|
|
|
title: 'Mocked document',
|
2025-05-19 09:03:00 +02:00
|
|
|
path: '000000',
|
2024-07-17 15:30:52 +02:00
|
|
|
abilities: {
|
|
|
|
|
destroy: false, // Means not owner
|
2024-09-11 10:23:06 +02:00
|
|
|
link_configuration: false,
|
2024-07-17 15:30:52 +02:00
|
|
|
versions_destroy: false,
|
|
|
|
|
versions_list: true,
|
|
|
|
|
versions_retrieve: true,
|
2024-10-23 08:26:40 +02:00
|
|
|
accesses_manage: false, // Means not admin
|
2024-07-17 15:30:52 +02:00
|
|
|
update: false,
|
|
|
|
|
partial_update: false, // Means not editor
|
|
|
|
|
retrieve: true,
|
2025-05-19 09:03:00 +02:00
|
|
|
link_select_options: {
|
|
|
|
|
public: ['reader', 'editor'],
|
|
|
|
|
authenticated: ['reader', 'editor'],
|
|
|
|
|
restricted: null,
|
|
|
|
|
},
|
2024-07-17 15:30:52 +02:00
|
|
|
},
|
2024-09-11 10:23:06 +02:00
|
|
|
link_reach: 'restricted',
|
2025-05-19 09:03:00 +02:00
|
|
|
computed_link_reach: 'restricted',
|
|
|
|
|
computed_link_role: 'reader',
|
|
|
|
|
ancestors_link_reach: null,
|
|
|
|
|
ancestors_link_role: null,
|
2024-07-17 15:30:52 +02:00
|
|
|
created_at: '2021-09-01T09:00:00Z',
|
2025-05-19 09:03:00 +02:00
|
|
|
user_role: 'owner',
|
2025-03-17 15:12:12 +01:00
|
|
|
user_roles: ['owner'],
|
2025-05-19 09:03:00 +02:00
|
|
|
...data,
|
2024-07-17 15:30:52 +02:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
await route.continue();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
2024-10-03 17:21:04 +02:00
|
|
|
|
2025-03-17 15:12:12 +01:00
|
|
|
export const mockedListDocs = async (page: Page, data: object[] = []) => {
|
|
|
|
|
await page.route('**/documents/**/', async (route) => {
|
|
|
|
|
const request = route.request();
|
|
|
|
|
if (request.method().includes('GET') && request.url().includes('page=')) {
|
|
|
|
|
await route.fulfill({
|
|
|
|
|
json: {
|
|
|
|
|
count: data.length,
|
|
|
|
|
next: null,
|
|
|
|
|
previous: null,
|
|
|
|
|
results: data,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-03 17:21:04 +02:00
|
|
|
export const mockedInvitations = async (page: Page, json?: object) => {
|
|
|
|
|
await page.route('**/invitations/**/', async (route) => {
|
|
|
|
|
const request = route.request();
|
|
|
|
|
if (
|
|
|
|
|
request.method().includes('GET') &&
|
|
|
|
|
request.url().includes('invitations') &&
|
|
|
|
|
request.url().includes('page=')
|
|
|
|
|
) {
|
|
|
|
|
await route.fulfill({
|
|
|
|
|
json: {
|
|
|
|
|
count: 1,
|
|
|
|
|
next: null,
|
|
|
|
|
previous: null,
|
|
|
|
|
results: [
|
|
|
|
|
{
|
|
|
|
|
id: '120ec765-43af-4602-83eb-7f4e1224548a',
|
|
|
|
|
abilities: {
|
|
|
|
|
destroy: true,
|
|
|
|
|
update: true,
|
|
|
|
|
partial_update: true,
|
|
|
|
|
retrieve: true,
|
|
|
|
|
},
|
|
|
|
|
created_at: '2024-10-03T12:19:26.107687Z',
|
|
|
|
|
email: 'test@invitation.test',
|
|
|
|
|
document: '4888c328-8406-4412-9b0b-c0ba5b9e5fb6',
|
|
|
|
|
role: 'editor',
|
|
|
|
|
issuer: '7380f42f-02eb-4ad5-b8f0-037a0e66066d',
|
|
|
|
|
is_expired: false,
|
|
|
|
|
...json,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
await route.continue();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const mockedAccesses = async (page: Page, json?: object) => {
|
|
|
|
|
await page.route('**/accesses/**/', async (route) => {
|
|
|
|
|
const request = route.request();
|
|
|
|
|
if (
|
|
|
|
|
request.method().includes('GET') &&
|
|
|
|
|
request.url().includes('accesses') &&
|
|
|
|
|
request.url().includes('page=')
|
|
|
|
|
) {
|
|
|
|
|
await route.fulfill({
|
2025-05-19 09:03:00 +02:00
|
|
|
json: [
|
|
|
|
|
{
|
|
|
|
|
id: 'bc8bbbc5-a635-4f65-9817-fd1e9ec8ef87',
|
|
|
|
|
user: {
|
|
|
|
|
id: 'b4a21bb3-722e-426c-9f78-9d190eda641c',
|
|
|
|
|
email: 'test@accesses.test',
|
2024-10-03 17:21:04 +02:00
|
|
|
},
|
2025-05-19 09:03:00 +02:00
|
|
|
team: '',
|
|
|
|
|
max_ancestors_role: null,
|
|
|
|
|
max_role: 'reader',
|
|
|
|
|
role: 'reader',
|
|
|
|
|
document: {
|
|
|
|
|
id: 'mocked-document-id',
|
|
|
|
|
path: '000000',
|
|
|
|
|
depth: 1,
|
|
|
|
|
},
|
|
|
|
|
abilities: {
|
|
|
|
|
destroy: true,
|
|
|
|
|
update: true,
|
|
|
|
|
partial_update: true,
|
|
|
|
|
retrieve: true,
|
|
|
|
|
set_role_to: ['administrator', 'editor'],
|
|
|
|
|
},
|
|
|
|
|
...json,
|
|
|
|
|
},
|
|
|
|
|
],
|
2024-10-03 17:21:04 +02:00
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
await route.continue();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
2025-02-03 11:02:29 +01:00
|
|
|
|
|
|
|
|
export const expectLoginPage = async (page: Page) =>
|
|
|
|
|
await expect(
|
|
|
|
|
page.getByRole('heading', { name: 'Collaborative writing' }),
|
2025-06-24 17:42:41 +02:00
|
|
|
).toBeVisible({
|
|
|
|
|
timeout: 10000,
|
|
|
|
|
});
|