mirror of
https://github.com/suitenumerique/docs
synced 2026-04-21 13:37:20 +00:00
✅(e2e) e2e instances compatibility
We want to be able to run our e2e tests on any instance of Docs, to do so we need to make some adjustments to our tests and configuration. We will use environment variables to configure the tests.
This commit is contained in:
parent
883d65136a
commit
a01c5f97ca
34 changed files with 1119 additions and 789 deletions
|
|
@ -9,6 +9,7 @@ and this project adheres to
|
|||
### Added
|
||||
|
||||
- 🚸(frontend) allow opening "@page" links with ctrl/command/middle-mouse click
|
||||
- ✅ E2E - Any instance friendly #2142
|
||||
|
||||
## [v4.8.5] - 2026-04-03
|
||||
|
||||
|
|
|
|||
22
src/frontend/apps/e2e/.env
Normal file
22
src/frontend/apps/e2e/.env
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
PORT=3000
|
||||
BASE_URL=http://localhost:3000
|
||||
BASE_API_URL=http://localhost:8071/api/v1.0
|
||||
COLLABORATION_WS_URL=ws://localhost:4444/collaboration/ws/
|
||||
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY=true
|
||||
MEDIA_BASE_URL=http://localhost:8083
|
||||
CUSTOM_SIGN_IN=false
|
||||
IS_INSTANCE=false
|
||||
SIGN_IN_EL_LOGIN_PAGE='.login-pf #kc-header-wrapper'
|
||||
SIGN_IN_EL_TRIGGER=Start Writing
|
||||
FIRST_NAME=E2E
|
||||
SIGN_IN_USERNAME_CHROMIUM=user.test@chromium.test
|
||||
USERNAME_CHROMIUM=E2E Chromium
|
||||
SIGN_IN_USERNAME_WEBKIT=user.test@webkit.test
|
||||
USERNAME_WEBKIT=E2E Webkit
|
||||
SIGN_IN_USERNAME_FIREFOX=user.test@firefox.test
|
||||
USERNAME_FIREFOX=E2E Firefox
|
||||
# To test server to server API calls
|
||||
SERVER_TO_SERVER_API_TOKENS='server-api-token'
|
||||
SUB_CHROMIUM=user.test@chromium.test
|
||||
SUB_WEBKIT=user.test@webkit.test
|
||||
SUB_FIREFOX=user.test@firefox.test
|
||||
29
src/frontend/apps/e2e/.env.example
Normal file
29
src/frontend/apps/e2e/.env.example
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
PORT=3000
|
||||
BASE_URL=http://localhost:3000
|
||||
BASE_API_URL=http://localhost:8071/api/v1.0
|
||||
COLLABORATION_WS_URL=ws://localhost:4444/collaboration/ws/
|
||||
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY=true
|
||||
MEDIA_BASE_URL=http://localhost:8083
|
||||
IS_INSTANCE=false
|
||||
CUSTOM_SIGN_IN=false
|
||||
SIGN_IN_EL_LOGIN_PAGE='.login-pf #kc-header-wrapper'
|
||||
SIGN_IN_EL_TRIGGER=Start Writing
|
||||
FIRST_NAME=E2E
|
||||
SIGN_IN_USERNAME_CHROMIUM=user.test@chromium.test
|
||||
USERNAME_CHROMIUM=E2E Chromium
|
||||
SIGN_IN_USERNAME_WEBKIT=user.test@webkit.test
|
||||
USERNAME_WEBKIT=E2E Webkit
|
||||
SIGN_IN_USERNAME_FIREFOX=user.test@firefox.test
|
||||
USERNAME_FIREFOX=E2E Firefox
|
||||
# Used only on instance with custom sign in
|
||||
SIGN_IN_EL_USERNAME_INPUT=
|
||||
SIGN_IN_EL_USERNAME_VALIDATION=
|
||||
SIGN_IN_EL_PASSWORD_INPUT=
|
||||
SIGN_IN_PASSWORD_CHROMIUM=
|
||||
SIGN_IN_PASSWORD_WEBKIT=
|
||||
SIGN_IN_PASSWORD_FIREFOX=
|
||||
# To test server to server API calls
|
||||
SERVER_TO_SERVER_API_TOKENS='server-api-token'
|
||||
SUB_CHROMIUM=user.test@chromium.test
|
||||
SUB_WEBKIT=user.test@webkit.test
|
||||
SUB_FIREFOX=user.test@firefox.test
|
||||
1
src/frontend/apps/e2e/.gitignore
vendored
1
src/frontend/apps/e2e/.gitignore
vendored
|
|
@ -5,3 +5,4 @@ blob-report/
|
|||
playwright/.auth/
|
||||
playwright/.cache/
|
||||
screenshots/
|
||||
.env.local
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
|||
import { FullConfig, FullProject, chromium, expect } from '@playwright/test';
|
||||
|
||||
import { keyCloakSignIn } from './utils-common';
|
||||
import { SignIn } from './utils-signin';
|
||||
|
||||
const saveStorageState = async (
|
||||
browserConfig: FullProject<unknown, unknown>,
|
||||
|
|
@ -22,7 +22,7 @@ const saveStorageState = async (
|
|||
await page.content();
|
||||
await expect(page.getByText('Docs').first()).toBeVisible();
|
||||
|
||||
await keyCloakSignIn(page, browserName);
|
||||
await SignIn(page, browserName);
|
||||
|
||||
await expect(
|
||||
page.locator('header').first().getByRole('button', {
|
||||
|
|
|
|||
|
|
@ -5,23 +5,25 @@ import { expect, test } from '@playwright/test';
|
|||
import { CONFIG, createDoc, overrideConfig } from './utils-common';
|
||||
|
||||
test.describe('Config', () => {
|
||||
test('it checks that sentry is trying to init from config endpoint', async ({
|
||||
page,
|
||||
}) => {
|
||||
await overrideConfig(page, {
|
||||
SENTRY_DSN: 'https://sentry.io/123',
|
||||
if (process.env.IS_INSTANCE !== 'true') {
|
||||
test('it checks that sentry is trying to init from config endpoint', async ({
|
||||
page,
|
||||
}) => {
|
||||
await overrideConfig(page, {
|
||||
SENTRY_DSN: 'https://sentry.io/123',
|
||||
});
|
||||
|
||||
const invalidMsg = 'Invalid Sentry Dsn: https://sentry.io/123';
|
||||
const consoleMessage = page.waitForEvent('console', {
|
||||
timeout: 5000,
|
||||
predicate: (msg) => msg.text().includes(invalidMsg),
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
expect((await consoleMessage).text()).toContain(invalidMsg);
|
||||
});
|
||||
|
||||
const invalidMsg = 'Invalid Sentry Dsn: https://sentry.io/123';
|
||||
const consoleMessage = page.waitForEvent('console', {
|
||||
timeout: 5000,
|
||||
predicate: (msg) => msg.text().includes(invalidMsg),
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
expect((await consoleMessage).text()).toContain(invalidMsg);
|
||||
});
|
||||
}
|
||||
|
||||
test('it checks that media server is configured from config endpoint', async ({
|
||||
page,
|
||||
|
|
@ -55,7 +57,7 @@ test.describe('Config', () => {
|
|||
|
||||
// Check src of image
|
||||
expect(await image.getAttribute('src')).toMatch(
|
||||
/http:\/\/localhost:8083\/media\/.*\/attachments\/.*.png/,
|
||||
new RegExp(`${process.env.MEDIA_BASE_URL}/media/.*?/attachments/.*?.png`),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -71,9 +73,9 @@ test.describe('Config', () => {
|
|||
.click();
|
||||
|
||||
const webSocket = await page.waitForEvent('websocket', (webSocket) => {
|
||||
return webSocket.url().includes('ws://localhost:4444/collaboration/ws/');
|
||||
return webSocket.url().includes(`${process.env.COLLABORATION_WS_URL}`);
|
||||
});
|
||||
expect(webSocket.url()).toContain('ws://localhost:4444/collaboration/ws/');
|
||||
expect(webSocket.url()).toContain(`${process.env.COLLABORATION_WS_URL}`);
|
||||
});
|
||||
|
||||
test('it checks that Crisp is trying to init from config endpoint', async ({
|
||||
|
|
@ -85,9 +87,8 @@ test.describe('Config', () => {
|
|||
|
||||
await page.goto('/');
|
||||
|
||||
await expect(
|
||||
page.locator('#crisp-chatbox').getByText('Invalid website'),
|
||||
).toBeVisible();
|
||||
const crispElement = page.locator('#crisp-chatbox');
|
||||
await expect(crispElement).toBeAttached();
|
||||
});
|
||||
|
||||
test('it checks FRONTEND_CSS_URL config', async ({ page }) => {
|
||||
|
|
@ -118,20 +119,22 @@ test.describe('Config', () => {
|
|||
).toBeAttached();
|
||||
});
|
||||
|
||||
test('it checks the config api is called', async ({ page }) => {
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/config/') && response.status() === 200,
|
||||
);
|
||||
if (process.env.IS_INSTANCE !== 'true') {
|
||||
test('it checks the config api is called', async ({ page }) => {
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/config/') && response.status() === 200,
|
||||
);
|
||||
|
||||
await page.goto('/');
|
||||
await page.goto('/');
|
||||
|
||||
const response = await responsePromise;
|
||||
expect(response.ok()).toBeTruthy();
|
||||
const response = await responsePromise;
|
||||
expect(response.ok()).toBeTruthy();
|
||||
|
||||
const json = (await response.json()) as typeof CONFIG;
|
||||
expect(json).toStrictEqual(CONFIG);
|
||||
});
|
||||
const json = (await response.json()) as typeof CONFIG;
|
||||
expect(json).toStrictEqual(CONFIG);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test.describe('Config: Not logged', () => {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { expect, test } from '@playwright/test';
|
|||
|
||||
import {
|
||||
createDoc,
|
||||
getCurrentConfig,
|
||||
mockedDocument,
|
||||
overrideConfig,
|
||||
verifyDocName,
|
||||
|
|
@ -13,210 +14,295 @@ import {
|
|||
writeInEditor,
|
||||
} from './utils-editor';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
if (process.env.IS_INSTANCE !== 'true') {
|
||||
test.describe('Doc AI feature', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test.describe('Doc AI feature', () => {
|
||||
[
|
||||
{
|
||||
AI_FEATURE_ENABLED: false,
|
||||
selector: 'Ask AI',
|
||||
},
|
||||
{
|
||||
AI_FEATURE_ENABLED: true,
|
||||
AI_FEATURE_BLOCKNOTE_ENABLED: false,
|
||||
selector: 'Ask AI',
|
||||
},
|
||||
{
|
||||
AI_FEATURE_ENABLED: true,
|
||||
AI_FEATURE_LEGACY_ENABLED: false,
|
||||
selector: 'AI',
|
||||
},
|
||||
].forEach((config) => {
|
||||
test(`it checks the AI feature flag from config endpoint: ${JSON.stringify(config)}`, async ({
|
||||
[
|
||||
{
|
||||
AI_FEATURE_ENABLED: false,
|
||||
selector: 'Ask AI',
|
||||
},
|
||||
{
|
||||
AI_FEATURE_ENABLED: true,
|
||||
AI_FEATURE_BLOCKNOTE_ENABLED: false,
|
||||
selector: 'Ask AI',
|
||||
},
|
||||
{
|
||||
AI_FEATURE_ENABLED: true,
|
||||
AI_FEATURE_LEGACY_ENABLED: false,
|
||||
selector: 'AI',
|
||||
},
|
||||
].forEach((config) => {
|
||||
test(`it checks the AI feature flag from config endpoint: ${JSON.stringify(config)}`, async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await overrideConfig(page, config);
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await createDoc(page, 'doc-ai-feature', browserName, 1);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Anything');
|
||||
await page.getByText('Anything').selectText();
|
||||
await expect(
|
||||
page.locator('button[data-test="convertMarkdown"]'),
|
||||
).toHaveCount(1);
|
||||
await expect(
|
||||
page.getByRole('button', { name: config.selector, exact: true }),
|
||||
).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test('it checks the AI feature and accepts changes', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await overrideConfig(page, config);
|
||||
await overrideConfig(page, {
|
||||
AI_BOT: {
|
||||
name: 'Albert AI',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
});
|
||||
|
||||
await mockAIResponse(page);
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await createDoc(page, 'doc-ai-feature', browserName, 1);
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Anything');
|
||||
await page.getByText('Anything').selectText();
|
||||
await openSuggestionMenu({ page });
|
||||
await page.getByText('Ask AI').click();
|
||||
await expect(
|
||||
page.locator('button[data-test="convertMarkdown"]'),
|
||||
).toHaveCount(1);
|
||||
page.getByRole('option', { name: 'Continue Writing' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('button', { name: config.selector, exact: true }),
|
||||
).toBeHidden();
|
||||
});
|
||||
});
|
||||
page.getByRole('option', { name: 'Summarize' }),
|
||||
).toBeVisible();
|
||||
|
||||
test('it checks the AI feature and accepts changes', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await overrideConfig(page, {
|
||||
AI_BOT: {
|
||||
name: 'Albert AI',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
const editor = await writeInEditor({ page, text: 'Hello World' });
|
||||
await editor.getByText('Hello World').selectText();
|
||||
|
||||
// Check from toolbar
|
||||
await page.getByRole('button', { name: 'Ask AI' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'Improve Writing' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'Fix Spelling' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'Translate' }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('option', { name: 'Translate' }).click();
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.fill('Translate into french');
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.press('Enter');
|
||||
await expect(editor.getByText('Albert AI')).toBeVisible();
|
||||
await page
|
||||
.locator('p.bn-mt-suggestion-menu-item-title')
|
||||
.getByText('Accept')
|
||||
.click();
|
||||
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
|
||||
// Check Suggestion menu
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await expect(page.getByText('Write with AI')).toBeVisible();
|
||||
|
||||
// Reload the page to check that the AI change is still there
|
||||
await page.goto(page.url());
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
});
|
||||
|
||||
await mockAIResponse(page);
|
||||
test('it reverts with the AI feature', async ({ page, browserName }) => {
|
||||
await overrideConfig(page, {
|
||||
AI_BOT: {
|
||||
name: 'Albert AI',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
await mockAIResponse(page);
|
||||
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
await page.goto('/');
|
||||
|
||||
await openSuggestionMenu({ page });
|
||||
await page.getByText('Ask AI').click();
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'Continue Writing' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('option', { name: 'Summarize' })).toBeVisible();
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
|
||||
await page.keyboard.press('Escape');
|
||||
const editor = await writeInEditor({ page, text: 'Hello World' });
|
||||
await editor.getByText('Hello World').selectText();
|
||||
|
||||
const editor = await writeInEditor({ page, text: 'Hello World' });
|
||||
await editor.getByText('Hello World').selectText();
|
||||
// Check from toolbar
|
||||
await page.getByRole('button', { name: 'Ask AI' }).click();
|
||||
|
||||
// Check from toolbar
|
||||
await page.getByRole('button', { name: 'Ask AI' }).click();
|
||||
await page.getByRole('option', { name: 'Translate' }).click();
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.fill('Translate into french');
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.press('Enter');
|
||||
await expect(editor.getByText('Albert AI')).toBeVisible();
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
await page
|
||||
.locator('p.bn-mt-suggestion-menu-item-title')
|
||||
.getByText('Revert')
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'Improve Writing' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'Fix Spelling' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('option', { name: 'Translate' })).toBeVisible();
|
||||
|
||||
await page.getByRole('option', { name: 'Translate' }).click();
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.fill('Translate into french');
|
||||
await page.getByRole('textbox', { name: 'Ask anything...' }).press('Enter');
|
||||
await expect(editor.getByText('Albert AI')).toBeVisible();
|
||||
await page
|
||||
.locator('p.bn-mt-suggestion-menu-item-title')
|
||||
.getByText('Accept')
|
||||
.click();
|
||||
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
|
||||
// Check Suggestion menu
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await expect(page.getByText('Write with AI')).toBeVisible();
|
||||
|
||||
// Reload the page to check that the AI change is still there
|
||||
await page.goto(page.url());
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
});
|
||||
|
||||
test('it reverts with the AI feature', async ({ page, browserName }) => {
|
||||
await overrideConfig(page, {
|
||||
AI_BOT: {
|
||||
name: 'Albert AI',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
await expect(editor.getByText('Hello World')).toBeVisible();
|
||||
});
|
||||
|
||||
await mockAIResponse(page);
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
|
||||
const editor = await writeInEditor({ page, text: 'Hello World' });
|
||||
await editor.getByText('Hello World').selectText();
|
||||
|
||||
// Check from toolbar
|
||||
await page.getByRole('button', { name: 'Ask AI' }).click();
|
||||
|
||||
await page.getByRole('option', { name: 'Translate' }).click();
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.fill('Translate into french');
|
||||
await page.getByRole('textbox', { name: 'Ask anything...' }).press('Enter');
|
||||
await expect(editor.getByText('Albert AI')).toBeVisible();
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
await page
|
||||
.locator('p.bn-mt-suggestion-menu-item-title')
|
||||
.getByText('Revert')
|
||||
.click();
|
||||
|
||||
await expect(editor.getByText('Hello World')).toBeVisible();
|
||||
});
|
||||
|
||||
test('it checks the AI buttons feature legacy', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.route(/.*\/ai-translate\//, async (route) => {
|
||||
const request = route.request();
|
||||
if (request.method().includes('POST')) {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
answer: 'Hallo Welt',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
await page.getByRole('button', { name: 'AI', exact: true }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Rephrase' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Summarize' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('menuitem', { name: 'Correct' })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Language' }).hover();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'English', exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'French', exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'German', exact: true }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'German', exact: true }).click();
|
||||
|
||||
await expect(editor.getByText('Hallo Welt')).toBeVisible();
|
||||
});
|
||||
|
||||
[
|
||||
{ ai_transform: false, ai_translate: false },
|
||||
{ ai_transform: true, ai_translate: false },
|
||||
{ ai_transform: false, ai_translate: true },
|
||||
].forEach(({ ai_transform, ai_translate }) => {
|
||||
test(`it checks AI buttons when can transform is at "${ai_transform}" and can translate is at "${ai_translate}"`, async ({
|
||||
test('it checks the AI buttons feature legacy', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.route(/.*\/ai-translate\//, async (route) => {
|
||||
const request = route.request();
|
||||
if (request.method().includes('POST')) {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
answer: 'Hallo Welt',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
await page.getByRole('button', { name: 'AI', exact: true }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Rephrase' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Summarize' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Correct' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Language' }).hover();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'English', exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'French', exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'German', exact: true }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'German', exact: true }).click();
|
||||
|
||||
await expect(editor.getByText('Hallo Welt')).toBeVisible();
|
||||
});
|
||||
|
||||
[
|
||||
{ ai_transform: false, ai_translate: false },
|
||||
{ ai_transform: true, ai_translate: false },
|
||||
{ ai_transform: false, ai_translate: true },
|
||||
].forEach(({ ai_transform, ai_translate }) => {
|
||||
test(`it checks AI buttons when can transform is at "${ai_transform}" and can translate is at "${ai_translate}"`, async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await mockedDocument(page, {
|
||||
accesses: [
|
||||
{
|
||||
id: 'b0df4343-c8bd-4c20-9ff6-fbf94fc94egg',
|
||||
role: 'owner',
|
||||
user: {
|
||||
email: 'super@owner.com',
|
||||
full_name: 'Super Owner',
|
||||
},
|
||||
},
|
||||
],
|
||||
abilities: {
|
||||
destroy: true, // Means owner
|
||||
link_configuration: true,
|
||||
ai_transform,
|
||||
ai_translate,
|
||||
accesses_manage: true,
|
||||
accesses_view: true,
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
},
|
||||
link_reach: 'restricted',
|
||||
link_role: 'editor',
|
||||
created_at: '2021-09-01T09:00:00Z',
|
||||
title: '',
|
||||
});
|
||||
|
||||
const [randomDoc] = await createDoc(
|
||||
page,
|
||||
'doc-editor-ai',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
||||
await verifyDocName(page, randomDoc);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
if (!ai_transform && !ai_translate) {
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'AI', exact: true }),
|
||||
).toBeHidden();
|
||||
return;
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'AI', exact: true }).click();
|
||||
|
||||
if (ai_transform) {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeVisible();
|
||||
} else {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeHidden();
|
||||
}
|
||||
|
||||
if (ai_translate) {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeVisible();
|
||||
} else {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeHidden();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test(`it checks ai_proxy ability`, async ({ page, browserName }) => {
|
||||
await mockedDocument(page, {
|
||||
accesses: [
|
||||
{
|
||||
|
|
@ -231,8 +317,7 @@ test.describe('Doc AI feature', () => {
|
|||
abilities: {
|
||||
destroy: true, // Means owner
|
||||
link_configuration: true,
|
||||
ai_transform,
|
||||
ai_translate,
|
||||
ai_proxy: false,
|
||||
accesses_manage: true,
|
||||
accesses_view: true,
|
||||
update: true,
|
||||
|
|
@ -247,7 +332,7 @@ test.describe('Doc AI feature', () => {
|
|||
|
||||
const [randomDoc] = await createDoc(
|
||||
page,
|
||||
'doc-editor-ai',
|
||||
'doc-editor-ai-proxy',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
|
@ -259,81 +344,108 @@ test.describe('Doc AI feature', () => {
|
|||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
if (!ai_transform && !ai_translate) {
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'AI', exact: true }),
|
||||
).toBeHidden();
|
||||
return;
|
||||
}
|
||||
await expect(page.getByRole('button', { name: 'Ask AI' })).toBeHidden();
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await expect(page.getByText('Write with AI')).toBeHidden();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.IS_INSTANCE === 'true') {
|
||||
test.describe('Doc AI feature on Instance', () => {
|
||||
test('it checks legacy AI feature', async ({ page, browserName }) => {
|
||||
const currentConfig = await getCurrentConfig(page);
|
||||
test.skip(
|
||||
!currentConfig.AI_FEATURE_ENABLED ||
|
||||
!currentConfig.AI_FEATURE_LEGACY_ENABLED,
|
||||
'Legacy AI feature is not enabled',
|
||||
);
|
||||
|
||||
await createDoc(page, 'doc-editor-ai-legacy-instance', browserName, 1);
|
||||
|
||||
const editor = await writeInEditor({ page, text: 'Hello World' });
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await editor.getByText('Hello World').selectText();
|
||||
|
||||
await page.getByRole('button', { name: 'AI', exact: true }).click();
|
||||
await page.getByRole('menuitem', { name: 'Language' }).hover();
|
||||
await page.getByRole('menuitem', { name: 'French', exact: true }).click();
|
||||
|
||||
if (ai_transform) {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeVisible();
|
||||
} else {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeHidden();
|
||||
}
|
||||
|
||||
if (ai_translate) {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeVisible();
|
||||
} else {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeHidden();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test(`it checks ai_proxy ability`, async ({ page, browserName }) => {
|
||||
await mockedDocument(page, {
|
||||
accesses: [
|
||||
{
|
||||
id: 'b0df4343-c8bd-4c20-9ff6-fbf94fc94egg',
|
||||
role: 'owner',
|
||||
user: {
|
||||
email: 'super@owner.com',
|
||||
full_name: 'Super Owner',
|
||||
},
|
||||
},
|
||||
],
|
||||
abilities: {
|
||||
destroy: true, // Means owner
|
||||
link_configuration: true,
|
||||
ai_proxy: false,
|
||||
accesses_manage: true,
|
||||
accesses_view: true,
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
},
|
||||
link_reach: 'restricted',
|
||||
link_role: 'editor',
|
||||
created_at: '2021-09-01T09:00:00Z',
|
||||
title: '',
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
});
|
||||
|
||||
const [randomDoc] = await createDoc(
|
||||
page,
|
||||
'doc-editor-ai-proxy',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
test('it checks legacy AI Blocknote', async ({ page, browserName }) => {
|
||||
const currentConfig = await getCurrentConfig(page);
|
||||
test.skip(
|
||||
!currentConfig.AI_FEATURE_ENABLED ||
|
||||
!currentConfig.AI_FEATURE_BLOCKNOTE_ENABLED,
|
||||
'Blocknote AI feature is not enabled',
|
||||
);
|
||||
|
||||
await verifyDocName(page, randomDoc);
|
||||
/**
|
||||
* Problem with the POSTHOG flags that keep false.
|
||||
* In case the flag is present, we mock the response
|
||||
*/
|
||||
await page.route(/flags\/\?v=2/, async (route) => {
|
||||
const request = route.request();
|
||||
if (request.method().includes('POST')) {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
errorsWhileComputingFlags: false,
|
||||
flags: {
|
||||
ai_blocknote: {
|
||||
key: 'ai_blocknote',
|
||||
enabled: true,
|
||||
variant: null,
|
||||
reason: {
|
||||
code: 'condition_match',
|
||||
condition_index: 5,
|
||||
description: 'Matched condition set 6',
|
||||
},
|
||||
metadata: {
|
||||
id: 147864,
|
||||
version: 47,
|
||||
description: null,
|
||||
payload: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
requestId: '2e3dc8be-d43c-4c9b-b497-c566f342904b',
|
||||
evaluatedAt: 1775060096052,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
await createDoc(page, 'doc-editor-ai-BN-instance', browserName, 1);
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
const editor = await writeInEditor({ page, text: 'Hello World' });
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Ask AI' })).toBeHidden();
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await expect(page.getByText('Write with AI')).toBeHidden();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await editor.getByText('Hello World').selectText();
|
||||
|
||||
await page.getByRole('button', { name: 'Ask AI' }).click();
|
||||
await page.getByRole('option', { name: 'Translate' }).click();
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.fill('Translate into french');
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.press('Enter');
|
||||
|
||||
await expect(editor.getByText(currentConfig.AI_BOT.name)).toBeVisible();
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
await page
|
||||
.locator('p.bn-mt-suggestion-menu-item-title')
|
||||
.getByText('Revert')
|
||||
.click();
|
||||
|
||||
await expect(editor.getByText('Hello World')).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,10 +58,14 @@ test.describe('Doc Comments', () => {
|
|||
await page.getByRole('button', { name: '👍' }).click();
|
||||
|
||||
await expect(
|
||||
thread.getByRole('img', { name: `E2E ${browserName}` }).first(),
|
||||
thread
|
||||
.getByRole('img', { name: `${process.env.FIRST_NAME} ${browserName}` })
|
||||
.first(),
|
||||
).toBeVisible();
|
||||
await expect(thread.getByText('This is a comment').first()).toBeVisible();
|
||||
await expect(thread.getByText(`E2E ${browserName}`).first()).toBeVisible();
|
||||
await expect(
|
||||
thread.getByText(`${process.env.FIRST_NAME} ${browserName}`).first(),
|
||||
).toBeVisible();
|
||||
await expect(thread.locator('.bn-comment-reaction')).toHaveText('👍1');
|
||||
|
||||
const urlCommentDoc = page.url();
|
||||
|
|
@ -85,7 +89,7 @@ test.describe('Doc Comments', () => {
|
|||
otherThread.getByText('This is a comment').first(),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
otherThread.getByText(`E2E ${browserName}`).first(),
|
||||
otherThread.getByText(`${process.env.FIRST_NAME} ${browserName}`).first(),
|
||||
).toBeVisible();
|
||||
await expect(otherThread.locator('.bn-comment-reaction')).toHaveText('👍2');
|
||||
|
||||
|
|
@ -98,13 +102,19 @@ test.describe('Doc Comments', () => {
|
|||
|
||||
// We check that the second user can see the comment he just made
|
||||
await expect(
|
||||
otherThread.getByRole('img', { name: `E2E ${otherBrowserName}` }).first(),
|
||||
otherThread
|
||||
.getByRole('img', {
|
||||
name: `${process.env.FIRST_NAME} ${otherBrowserName}`,
|
||||
})
|
||||
.first(),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
otherThread.getByText('This is a comment from the other user').first(),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
otherThread.getByText(`E2E ${otherBrowserName}`).first(),
|
||||
otherThread
|
||||
.getByText(`${process.env.FIRST_NAME} ${otherBrowserName}`)
|
||||
.first(),
|
||||
).toBeVisible();
|
||||
|
||||
// We check that the first user can see the comment made by the second user in real time
|
||||
|
|
@ -112,7 +122,7 @@ test.describe('Doc Comments', () => {
|
|||
thread.getByText('This is a comment from the other user').first(),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
thread.getByText(`E2E ${otherBrowserName}`).first(),
|
||||
thread.getByText(`${process.env.FIRST_NAME} ${otherBrowserName}`).first(),
|
||||
).toBeVisible();
|
||||
|
||||
await cleanup();
|
||||
|
|
@ -134,7 +144,7 @@ test.describe('Doc Comments', () => {
|
|||
|
||||
await expect(editor.getByText('Hello')).toHaveCSS(
|
||||
'background-color',
|
||||
'color(srgb 0.882353 0.831373 0.717647 / 0.4)',
|
||||
/color\(srgb\s+[\d\s.]+\s+\/\s+0\.4\)/,
|
||||
);
|
||||
|
||||
await editor.first().click();
|
||||
|
|
@ -201,7 +211,7 @@ test.describe('Doc Comments', () => {
|
|||
|
||||
await expect(editor.getByText('Hello')).toHaveCSS(
|
||||
'background-color',
|
||||
'color(srgb 0.882353 0.831373 0.717647 / 0.4)',
|
||||
/color\(srgb\s+[\d\s.]+\s+\/\s+0\.4\)/,
|
||||
);
|
||||
|
||||
await editor.first().click();
|
||||
|
|
@ -267,11 +277,15 @@ test.describe('Doc Comments', () => {
|
|||
|
||||
await expect(otherEditor.getByText('Hello')).toHaveCSS(
|
||||
'background-color',
|
||||
'color(srgb 0.882353 0.831373 0.717647 / 0.4)',
|
||||
/color\(srgb\s+[\d\s.]+\s+\/\s+0\.4\)/,
|
||||
);
|
||||
|
||||
// We change the role of the second user to reader
|
||||
await updateRoleUser(page, 'Reader', `user.test@${otherBrowserName}.test`);
|
||||
await updateRoleUser(
|
||||
page,
|
||||
'Reader',
|
||||
process.env[`SIGN_IN_USERNAME_${otherBrowserName.toUpperCase()}`] || '',
|
||||
);
|
||||
|
||||
// With the reader role, the second user cannot see comments
|
||||
await otherPage.reload();
|
||||
|
|
@ -296,13 +310,21 @@ test.describe('Doc Comments', () => {
|
|||
// Anonymous user can see and add comments
|
||||
await otherPage.getByRole('button', { name: 'Logout' }).click();
|
||||
|
||||
await expect(
|
||||
otherPage
|
||||
.getByRole('button', { name: process.env.SIGN_IN_EL_TRIGGER })
|
||||
.first(),
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
await otherPage.goto(urlCommentDoc);
|
||||
|
||||
await verifyDocName(otherPage, docTitle);
|
||||
|
||||
await expect(otherEditor.getByText('Hello')).toHaveCSS(
|
||||
'background-color',
|
||||
'color(srgb 0.882353 0.831373 0.717647 / 0.4)',
|
||||
/color\(srgb\s+[\d\s.]+\s+\/\s+0\.4\)/,
|
||||
);
|
||||
await otherEditor.getByText('Hello').click();
|
||||
await expect(
|
||||
|
|
@ -348,7 +370,7 @@ test.describe('Doc Comments', () => {
|
|||
|
||||
await expect(editor1.getByText('Document One')).toHaveCSS(
|
||||
'background-color',
|
||||
'color(srgb 0.882353 0.831373 0.717647 / 0.4)',
|
||||
/color\(srgb\s+[\d\s.]+\s+\/\s+0\.4\)/,
|
||||
);
|
||||
|
||||
await editor1.getByText('Document One').click();
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import { expect, test } from '@playwright/test';
|
|||
import {
|
||||
createDoc,
|
||||
goToGridDoc,
|
||||
keyCloakSignIn,
|
||||
randomName,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { connectOtherUserToDoc } from './utils-share';
|
||||
import { SignIn } from './utils-signin';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
|
@ -22,8 +22,7 @@ test.describe('Doc Create', () => {
|
|||
{ timeout: 5000 },
|
||||
);
|
||||
|
||||
const header = page.locator('header').first();
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||
|
||||
const docsGrid = page.getByTestId('docs-grid');
|
||||
await expect(docsGrid).toBeVisible();
|
||||
|
|
@ -134,7 +133,7 @@ test.describe('Doc Create', () => {
|
|||
withoutSignIn: true,
|
||||
});
|
||||
|
||||
await keyCloakSignIn(otherPage, otherBrowserName, false);
|
||||
await SignIn(otherPage, otherBrowserName, false);
|
||||
|
||||
await verifyDocName(otherPage, 'From unlogged doc from url');
|
||||
|
||||
|
|
@ -160,22 +159,28 @@ test.describe('Doc Create: Not logged', () => {
|
|||
browserName,
|
||||
request,
|
||||
}) => {
|
||||
const SERVER_TO_SERVER_API_TOKENS = 'server-api-token';
|
||||
test.skip(
|
||||
!process.env.SERVER_TO_SERVER_API_TOKENS ||
|
||||
!process.env[`SUB_${browserName.toUpperCase()}`] ||
|
||||
!process.env[`SIGN_IN_USERNAME_${browserName.toUpperCase()}`],
|
||||
'Server to server API tokens and credentials must be set',
|
||||
);
|
||||
|
||||
const markdown = `This is a normal text\n\n# And this is a large heading`;
|
||||
const [title] = randomName('My server way doc create', browserName, 1);
|
||||
const data = {
|
||||
title,
|
||||
content: markdown,
|
||||
sub: `user.test@${browserName}.test`,
|
||||
email: `user.test@${browserName}.test`,
|
||||
sub: process.env[`SUB_${browserName.toUpperCase()}`],
|
||||
email: process.env[`SIGN_IN_USERNAME_${browserName.toUpperCase()}`],
|
||||
};
|
||||
|
||||
const newDoc = await request.post(
|
||||
`http://localhost:8071/api/v1.0/documents/create-for-owner/`,
|
||||
`${process.env.BASE_API_URL}/documents/create-for-owner/`,
|
||||
{
|
||||
data,
|
||||
headers: {
|
||||
Authorization: `Bearer ${SERVER_TO_SERVER_API_TOKENS}`,
|
||||
Authorization: `Bearer ${process.env.SERVER_TO_SERVER_API_TOKENS}`,
|
||||
format: 'json',
|
||||
},
|
||||
},
|
||||
|
|
@ -183,7 +188,7 @@ test.describe('Doc Create: Not logged', () => {
|
|||
|
||||
expect(newDoc.ok()).toBeTruthy();
|
||||
|
||||
await keyCloakSignIn(page, browserName);
|
||||
await SignIn(page, browserName);
|
||||
|
||||
await goToGridDoc(page, { title });
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,12 @@ test.describe('Doc Editor', () => {
|
|||
}) => {
|
||||
await createDoc(page, 'doc-toolbar', browserName, 1);
|
||||
|
||||
await verifyDocName(page, 'doc-toolbar');
|
||||
|
||||
const editor = await writeInEditor({ page, text: 'test content' });
|
||||
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
await editor
|
||||
.getByText('test content', {
|
||||
exact: true,
|
||||
|
|
@ -37,10 +41,7 @@ test.describe('Doc Editor', () => {
|
|||
.selectText();
|
||||
|
||||
const toolbar = page.locator('.bn-formatting-toolbar');
|
||||
await expect(toolbar.getByRole('button', { name: 'Ask AI' })).toBeVisible();
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="comment-toolbar-button"]'),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(toolbar.locator('button[data-test="bold"]')).toBeVisible();
|
||||
await expect(toolbar.locator('button[data-test="italic"]')).toBeVisible();
|
||||
await expect(
|
||||
|
|
@ -63,6 +64,23 @@ test.describe('Doc Editor', () => {
|
|||
await expect(
|
||||
toolbar.locator('button[data-test="createLink"]'),
|
||||
).toBeVisible();
|
||||
|
||||
/**
|
||||
* Because of how Posthog is loaded and how auth session are
|
||||
* saved, this assertion is not reliable on test instances
|
||||
* We will dedicate a testcase to check the AI features
|
||||
* on test instances with a specific setup
|
||||
*/
|
||||
if (process.env.IS_INSTANCE !== 'true') {
|
||||
// eslint-disable-next-line playwright/no-conditional-expect
|
||||
await expect(
|
||||
toolbar.getByRole('button', { name: 'Ask AI' }),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="comment-toolbar-button"]'),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="convertMarkdown"]'),
|
||||
).toBeVisible();
|
||||
|
|
@ -117,7 +135,7 @@ test.describe('Doc Editor', () => {
|
|||
let webSocketPromise = page.waitForEvent('websocket', (webSocket) => {
|
||||
return webSocket
|
||||
.url()
|
||||
.includes('ws://localhost:4444/collaboration/ws/?room=');
|
||||
.includes(`${process.env.COLLABORATION_WS_URL}?room=`);
|
||||
});
|
||||
|
||||
await page
|
||||
|
|
@ -128,7 +146,7 @@ test.describe('Doc Editor', () => {
|
|||
|
||||
let webSocket = await webSocketPromise;
|
||||
expect(webSocket.url()).toContain(
|
||||
'ws://localhost:4444/collaboration/ws/?room=',
|
||||
`${process.env.COLLABORATION_WS_URL}?room=`,
|
||||
);
|
||||
|
||||
// Is connected
|
||||
|
|
@ -157,7 +175,7 @@ test.describe('Doc Editor', () => {
|
|||
webSocket = await page.waitForEvent('websocket', (webSocket) => {
|
||||
return webSocket
|
||||
.url()
|
||||
.includes('ws://localhost:4444/collaboration/ws/?room=');
|
||||
.includes(`${process.env.COLLABORATION_WS_URL}?room=`);
|
||||
});
|
||||
framesentPromise = webSocket.waitForEvent('framesent');
|
||||
framesent = await framesentPromise;
|
||||
|
|
@ -331,7 +349,9 @@ test.describe('Doc Editor', () => {
|
|||
const viewerImg = otherPage
|
||||
.locator('.--docs--editor-container img.bn-visual-media')
|
||||
.first();
|
||||
await expect(viewerImg).toBeVisible();
|
||||
await expect(viewerImg).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// Viewer can download the image
|
||||
await viewerImg.click();
|
||||
|
|
@ -364,15 +384,16 @@ test.describe('Doc Editor', () => {
|
|||
.locator('.--docs--editor-container img.bn-visual-media')
|
||||
.first();
|
||||
|
||||
await expect(image).toBeVisible();
|
||||
await expect(image).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// Wait for the media-check to be processed
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Check src of image
|
||||
expect(await image.getAttribute('src')).toMatch(
|
||||
/http:\/\/localhost:8083\/media\/.*\/attachments\/.*.png/,
|
||||
/media\/.*\/attachments\/.*.png/,
|
||||
);
|
||||
|
||||
await expect(image).toHaveAttribute('role', 'presentation');
|
||||
|
|
@ -381,60 +402,62 @@ test.describe('Doc Editor', () => {
|
|||
await expect(image).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
test('it downloads unsafe files', async ({ page, browserName }) => {
|
||||
const [randomDoc] = await createDoc(page, 'doc-editor', browserName, 1);
|
||||
if (process.env.IS_INSTANCE !== 'true') {
|
||||
test('it downloads unsafe files', async ({ page, browserName }) => {
|
||||
const [randomDoc] = await createDoc(page, 'doc-editor', browserName, 1);
|
||||
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
const downloadPromise = page.waitForEvent('download', (download) => {
|
||||
return download.suggestedFilename().includes(`html`);
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
const downloadPromise = page.waitForEvent('download', (download) => {
|
||||
return download.suggestedFilename().includes(`html`);
|
||||
});
|
||||
const responseCheckPromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('media-check') && response.status() === 200,
|
||||
);
|
||||
|
||||
await verifyDocName(page, randomDoc);
|
||||
|
||||
await page.locator('.ProseMirror.bn-editor').click();
|
||||
await page.locator('.ProseMirror.bn-editor').fill('Hello World');
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await page.getByText('Embedded file').click();
|
||||
await page.getByText('Upload file').click();
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(path.join(__dirname, 'assets/test.html'));
|
||||
|
||||
await responseCheckPromise;
|
||||
|
||||
await page.locator('.bn-block-content[data-name="test.html"]').click();
|
||||
await page.getByRole('button', { name: 'Download file' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByText('This file is flagged as unsafe.'),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', {
|
||||
name: 'Download',
|
||||
exact: true,
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
void page
|
||||
.getByRole('button', {
|
||||
name: 'Download',
|
||||
exact: true,
|
||||
})
|
||||
.click();
|
||||
|
||||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toContain(`-unsafe.html`);
|
||||
|
||||
const svgBuffer = await cs.toBuffer(await download.createReadStream());
|
||||
expect(svgBuffer.toString()).toContain('Hello svg');
|
||||
});
|
||||
const responseCheckPromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('media-check') && response.status() === 200,
|
||||
);
|
||||
|
||||
await verifyDocName(page, randomDoc);
|
||||
|
||||
await page.locator('.ProseMirror.bn-editor').click();
|
||||
await page.locator('.ProseMirror.bn-editor').fill('Hello World');
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await page.getByText('Embedded file').click();
|
||||
await page.getByText('Upload file').click();
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(path.join(__dirname, 'assets/test.html'));
|
||||
|
||||
await responseCheckPromise;
|
||||
|
||||
await page.locator('.bn-block-content[data-name="test.html"]').click();
|
||||
await page.getByRole('button', { name: 'Download file' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByText('This file is flagged as unsafe.'),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', {
|
||||
name: 'Download',
|
||||
exact: true,
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
void page
|
||||
.getByRole('button', {
|
||||
name: 'Download',
|
||||
exact: true,
|
||||
})
|
||||
.click();
|
||||
|
||||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toContain(`-unsafe.html`);
|
||||
|
||||
const svgBuffer = await cs.toBuffer(await download.createReadStream());
|
||||
expect(svgBuffer.toString()).toContain('Hello svg');
|
||||
});
|
||||
}
|
||||
|
||||
test('it analyzes uploads', async ({ page, browserName }) => {
|
||||
const [randomDoc] = await createDoc(page, 'doc-editor', browserName, 1);
|
||||
|
|
@ -484,144 +507,150 @@ test.describe('Doc Editor', () => {
|
|||
await expect(editor.getByText('Analyzing file...')).toBeHidden();
|
||||
});
|
||||
|
||||
test('it checks block editing when not connected to collab server', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
test.slow();
|
||||
|
||||
/**
|
||||
* The good port is 4444, but we want to simulate a not connected
|
||||
* collaborative server.
|
||||
* So we use a port that is not used by the collaborative server.
|
||||
* The server will not be able to connect to the collaborative server.
|
||||
*/
|
||||
await overrideConfig(page, {
|
||||
COLLABORATION_WS_URL: 'ws://localhost:5555/collaboration/ws/',
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const [parentTitle] = await createDoc(
|
||||
page,
|
||||
'editing-blocking',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
||||
const card = page.getByLabel('It is the card information');
|
||||
await expect(
|
||||
card.getByText('Others are editing. Your network prevent changes.'),
|
||||
).toBeHidden();
|
||||
const editor = page.locator('.ProseMirror');
|
||||
|
||||
await expect(editor).toHaveAttribute('contenteditable', 'true');
|
||||
|
||||
let responseCanEditPromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes(`/can-edit/`) && response.status() === 200,
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
await updateShareLink(page, 'Public', 'Editing');
|
||||
|
||||
// Close the modal
|
||||
await page.getByRole('button', { name: 'close' }).first().click();
|
||||
|
||||
const urlParentDoc = page.url();
|
||||
|
||||
const { name: childTitle } = await createRootSubPage(
|
||||
if (process.env.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY === 'true') {
|
||||
test('it checks block editing when not connected to collab server', async ({
|
||||
page,
|
||||
browserName,
|
||||
'editing-blocking - child',
|
||||
);
|
||||
}) => {
|
||||
test.slow();
|
||||
|
||||
let responseCanEdit = await responseCanEditPromise;
|
||||
expect(responseCanEdit.ok()).toBeTruthy();
|
||||
let jsonCanEdit = (await responseCanEdit.json()) as { can_edit: boolean };
|
||||
expect(jsonCanEdit.can_edit).toBeTruthy();
|
||||
/**
|
||||
* The good port is 4444, but we want to simulate a not connected
|
||||
* collaborative server.
|
||||
* So we use a port that is not used by the collaborative server.
|
||||
* The server will not be able to connect to the collaborative server.
|
||||
*/
|
||||
await overrideConfig(page, {
|
||||
COLLABORATION_WS_URL: 'ws://localhost:5555/collaboration/ws/',
|
||||
});
|
||||
|
||||
const urlChildDoc = page.url();
|
||||
await page.goto('/');
|
||||
|
||||
/**
|
||||
* We open another browser that will connect to the collaborative server
|
||||
* and will block the current browser to edit the doc.
|
||||
*/
|
||||
const { otherPage } = await connectOtherUserToDoc({
|
||||
browserName,
|
||||
docUrl: urlChildDoc,
|
||||
docTitle: childTitle,
|
||||
withoutSignIn: true,
|
||||
const [parentTitle] = await createDoc(
|
||||
page,
|
||||
'editing-blocking',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
||||
const card = page.getByLabel('It is the card information');
|
||||
await expect(
|
||||
card.getByText('Others are editing. Your network prevent changes.'),
|
||||
).toBeHidden();
|
||||
const editor = page.locator('.ProseMirror');
|
||||
|
||||
await expect(editor).toHaveAttribute('contenteditable', 'true');
|
||||
|
||||
let responseCanEditPromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes(`/can-edit/`) && response.status() === 200,
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
await updateShareLink(page, 'Public', 'Editing');
|
||||
|
||||
// Close the modal
|
||||
await page.getByRole('button', { name: 'close' }).first().click();
|
||||
|
||||
const urlParentDoc = page.url();
|
||||
|
||||
const { name: childTitle } = await createRootSubPage(
|
||||
page,
|
||||
browserName,
|
||||
'editing-blocking - child',
|
||||
);
|
||||
|
||||
let responseCanEdit = await responseCanEditPromise;
|
||||
expect(responseCanEdit.ok()).toBeTruthy();
|
||||
let jsonCanEdit = (await responseCanEdit.json()) as { can_edit: boolean };
|
||||
expect(jsonCanEdit.can_edit).toBeTruthy();
|
||||
|
||||
const urlChildDoc = page.url();
|
||||
|
||||
/**
|
||||
* We open another browser that will connect to the collaborative server
|
||||
* and will block the current browser to edit the doc.
|
||||
*/
|
||||
const { otherPage } = await connectOtherUserToDoc({
|
||||
browserName,
|
||||
docUrl: urlChildDoc,
|
||||
docTitle: childTitle,
|
||||
withoutSignIn: true,
|
||||
});
|
||||
|
||||
const webSocketPromise = otherPage.waitForEvent(
|
||||
'websocket',
|
||||
(webSocket) => {
|
||||
return webSocket
|
||||
.url()
|
||||
.includes(`${process.env.COLLABORATION_WS_URL}?room=`);
|
||||
},
|
||||
);
|
||||
|
||||
await otherPage.goto(urlChildDoc);
|
||||
|
||||
const webSocket = await webSocketPromise;
|
||||
expect(webSocket.url()).toContain(
|
||||
`${process.env.COLLABORATION_WS_URL}?room=`,
|
||||
);
|
||||
|
||||
await verifyDocName(otherPage, childTitle);
|
||||
|
||||
await page.reload();
|
||||
|
||||
responseCanEdit = await page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes(`/can-edit/`) && response.status() === 200,
|
||||
);
|
||||
expect(responseCanEdit.ok()).toBeTruthy();
|
||||
|
||||
jsonCanEdit = (await responseCanEdit.json()) as { can_edit: boolean };
|
||||
expect(jsonCanEdit.can_edit).toBeFalsy();
|
||||
|
||||
await expect(
|
||||
card.getByText('Others are editing. Your network prevent changes.'),
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
await expect(editor).toHaveAttribute('contenteditable', 'false');
|
||||
|
||||
await expect(
|
||||
page.getByRole('textbox', { name: 'Document title' }),
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
page.getByRole('heading', { name: childTitle }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.goto(urlParentDoc);
|
||||
|
||||
await verifyDocName(page, parentTitle);
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
await page.getByTestId('doc-access-mode').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Reading' }).click();
|
||||
|
||||
// Close the modal
|
||||
await page.getByRole('button', { name: 'close' }).first().click();
|
||||
|
||||
await page.goto(urlChildDoc);
|
||||
|
||||
await expect(editor).toHaveAttribute('contenteditable', 'true');
|
||||
|
||||
await expect(
|
||||
page.getByRole('textbox', { name: 'Document title' }),
|
||||
).toContainText(childTitle);
|
||||
await expect(
|
||||
page.getByRole('heading', { name: childTitle }),
|
||||
).toBeHidden();
|
||||
|
||||
await expect(
|
||||
card.getByText('Others are editing. Your network prevent changes.'),
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
const webSocketPromise = otherPage.waitForEvent(
|
||||
'websocket',
|
||||
(webSocket) => {
|
||||
return webSocket
|
||||
.url()
|
||||
.includes('ws://localhost:4444/collaboration/ws/?room=');
|
||||
},
|
||||
);
|
||||
|
||||
await otherPage.goto(urlChildDoc);
|
||||
|
||||
const webSocket = await webSocketPromise;
|
||||
expect(webSocket.url()).toContain(
|
||||
'ws://localhost:4444/collaboration/ws/?room=',
|
||||
);
|
||||
|
||||
await verifyDocName(otherPage, childTitle);
|
||||
|
||||
await page.reload();
|
||||
|
||||
responseCanEdit = await page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes(`/can-edit/`) && response.status() === 200,
|
||||
);
|
||||
expect(responseCanEdit.ok()).toBeTruthy();
|
||||
|
||||
jsonCanEdit = (await responseCanEdit.json()) as { can_edit: boolean };
|
||||
expect(jsonCanEdit.can_edit).toBeFalsy();
|
||||
|
||||
await expect(
|
||||
card.getByText('Others are editing. Your network prevent changes.'),
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
await expect(editor).toHaveAttribute('contenteditable', 'false');
|
||||
|
||||
await expect(
|
||||
page.getByRole('textbox', { name: 'Document title' }),
|
||||
).toBeHidden();
|
||||
await expect(page.getByRole('heading', { name: childTitle })).toBeVisible();
|
||||
|
||||
await page.goto(urlParentDoc);
|
||||
|
||||
await verifyDocName(page, parentTitle);
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
await page.getByTestId('doc-access-mode').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Reading' }).click();
|
||||
|
||||
// Close the modal
|
||||
await page.getByRole('button', { name: 'close' }).first().click();
|
||||
|
||||
await page.goto(urlChildDoc);
|
||||
|
||||
await expect(editor).toHaveAttribute('contenteditable', 'true');
|
||||
|
||||
await expect(
|
||||
page.getByRole('textbox', { name: 'Document title' }),
|
||||
).toContainText(childTitle);
|
||||
await expect(page.getByRole('heading', { name: childTitle })).toBeHidden();
|
||||
|
||||
await expect(
|
||||
card.getByText('Others are editing. Your network prevent changes.'),
|
||||
).toBeHidden();
|
||||
});
|
||||
}
|
||||
|
||||
test('it checks if callout custom block', async ({ page, browserName }) => {
|
||||
await createDoc(page, 'doc-toolbar', browserName, 1);
|
||||
|
|
@ -868,7 +897,7 @@ test.describe('Doc Editor', () => {
|
|||
|
||||
// Check src of pdf
|
||||
expect(await pdfIframe.getAttribute('src')).toMatch(
|
||||
/http:\/\/localhost:8083\/media\/.*\/attachments\/.*.pdf/,
|
||||
/\/media\/.*\/attachments\/.*.pdf/,
|
||||
);
|
||||
|
||||
await expect(pdfIframe).toHaveAttribute('role', 'presentation');
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@ test.describe('Doc Export', () => {
|
|||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toBe(`${randomDoc}.zip`);
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const zipBuffer = await cs.toBuffer(await download.createReadStream());
|
||||
// Unzip and inspect contents
|
||||
const zip = await JSZip.loadAsync(zipBuffer);
|
||||
|
|
@ -254,6 +256,8 @@ test.describe('Doc Export', () => {
|
|||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const pdfBuffer = await cs.toBuffer(await download.createReadStream());
|
||||
|
||||
const pdfParse = new PDFParse({ data: pdfBuffer });
|
||||
|
|
@ -301,6 +305,8 @@ test.describe('Doc Export', () => {
|
|||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toBe(`${randomDocFrench}.pdf`);
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const pdfBuffer = await cs.toBuffer(await download.createReadStream());
|
||||
const pdfString = pdfBuffer.toString('latin1');
|
||||
|
||||
|
|
@ -388,6 +394,8 @@ test.describe('Doc Export', () => {
|
|||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const pdfBuffer = await cs.toBuffer(await download.createReadStream());
|
||||
|
||||
// If we need to update the PDF regression fixture, uncomment the line below
|
||||
|
|
|
|||
|
|
@ -22,11 +22,10 @@ test.describe('Doc grid move', () => {
|
|||
browserName,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
const header = page.locator('header').first();
|
||||
await createDoc(page, 'Draggable doc', browserName, 1);
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||
await createDoc(page, 'Droppable doc', browserName, 1);
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||
|
||||
const response = await page.waitForResponse(
|
||||
(response) =>
|
||||
|
|
@ -333,9 +332,14 @@ test.describe('Doc grid move', () => {
|
|||
// The other user should receive the access request and be able to approve it
|
||||
await otherPage.getByRole('button', { name: 'Share' }).click();
|
||||
await expect(otherPage.getByText('Access Requests')).toBeVisible();
|
||||
await expect(otherPage.getByText(`E2E ${browserName}`)).toBeVisible();
|
||||
await expect(
|
||||
otherPage.getByText(
|
||||
process.env[`USERNAME_${browserName.toUpperCase()}`] || '',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
const emailRequest = `user.test@${browserName}.test`;
|
||||
const emailRequest =
|
||||
process.env[`SIGN_IN_USERNAME_${browserName.toUpperCase()}`] || '';
|
||||
await expect(otherPage.getByText(emailRequest)).toBeVisible();
|
||||
const container = otherPage.getByTestId(
|
||||
`doc-share-access-request-row-${emailRequest}`,
|
||||
|
|
@ -348,7 +352,11 @@ test.describe('Doc grid move', () => {
|
|||
|
||||
await expect(otherPage.getByText('Access Requests')).toBeHidden();
|
||||
await expect(otherPage.getByText('Share with 2 users')).toBeVisible();
|
||||
await expect(otherPage.getByText(`E2E ${browserName}`)).toBeVisible();
|
||||
await expect(
|
||||
otherPage.getByText(
|
||||
process.env[`USERNAME_${browserName.toUpperCase()}`] || '',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
// The first user should now be able to move the doc
|
||||
await page.reload();
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ test.describe('Doc Import', () => {
|
|||
).toBeVisible();
|
||||
|
||||
/* eslint-disable playwright/no-conditional-expect */
|
||||
if (isMDCheck) {
|
||||
if (isMDCheck && process.env.IS_INSTANCE !== 'true') {
|
||||
await expect(
|
||||
editor.locator(
|
||||
'img[src="http://localhost:3000/assets/logo-suite-numerique.png"]',
|
||||
|
|
|
|||
|
|
@ -17,12 +17,14 @@ test.describe('Inherited share accesses', () => {
|
|||
page.getByText('People with access via the parent document'),
|
||||
).toBeVisible();
|
||||
|
||||
const user = page.getByTestId(
|
||||
`doc-share-member-row-user.test@${browserName}.test`,
|
||||
);
|
||||
await expect(user).toBeVisible();
|
||||
await expect(user.getByText(`E2E ${browserName}`)).toBeVisible();
|
||||
await expect(user.getByText('Owner')).toBeVisible();
|
||||
const users = page.locator('.--docs--doc-share-member-item');
|
||||
await expect(users).toBeVisible();
|
||||
await expect(
|
||||
users.getByText(
|
||||
process.env[`SIGN_IN_USERNAME_${browserName.toUpperCase()}`] || '',
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(users.getByText('Owner')).toBeVisible();
|
||||
|
||||
await page
|
||||
.locator('.--docs--doc-inherited-share-content')
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import {
|
||||
BROWSERS,
|
||||
createDoc,
|
||||
keyCloakSignIn,
|
||||
randomName,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { BROWSERS, createDoc, randomName, verifyDocName } from './utils-common';
|
||||
import { writeInEditor } from './utils-editor';
|
||||
import { connectOtherUserToDoc, updateRoleUser } from './utils-share';
|
||||
import { SignIn } from './utils-signin';
|
||||
import { createRootSubPage } from './utils-sub-pages';
|
||||
|
||||
test.describe('Document create member', () => {
|
||||
|
|
@ -99,7 +94,7 @@ test.describe('Document create member', () => {
|
|||
list.getByTestId(`doc-share-add-member-${users[1].email}`),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
list.getByText(`${users[1].full_name || users[1].email}`),
|
||||
list.getByText(`${users[1].full_name || users[1].email}`).first(),
|
||||
).toBeVisible();
|
||||
|
||||
// Select email and verify tag
|
||||
|
|
@ -302,9 +297,14 @@ test.describe('Document create member', () => {
|
|||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
await expect(page.getByText('Access Requests')).toBeVisible();
|
||||
await expect(page.getByText(`E2E ${otherBrowserName}`)).toBeVisible();
|
||||
await expect(
|
||||
page.getByText(
|
||||
process.env[`USERNAME_${otherBrowserName.toUpperCase()}`] || '',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
const emailRequest = `user.test@${otherBrowserName}.test`;
|
||||
const emailRequest =
|
||||
process.env[`SIGN_IN_USERNAME_${otherBrowserName.toUpperCase()}`] || '';
|
||||
await expect(page.getByText(emailRequest)).toBeVisible();
|
||||
const container = page.getByTestId(
|
||||
`doc-share-access-request-row-${emailRequest}`,
|
||||
|
|
@ -315,7 +315,11 @@ test.describe('Document create member', () => {
|
|||
|
||||
await expect(page.getByText('Access Requests')).toBeHidden();
|
||||
await expect(page.getByText('Share with 2 users')).toBeVisible();
|
||||
await expect(page.getByText(`E2E ${otherBrowserName}`)).toBeVisible();
|
||||
await expect(
|
||||
page.getByText(
|
||||
process.env[`USERNAME_${otherBrowserName.toUpperCase()}`] || '',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
// Other user verifies he has access
|
||||
await otherPage.reload();
|
||||
|
|
@ -343,7 +347,7 @@ test.describe('Document create member: Multiple login', () => {
|
|||
test.slow();
|
||||
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
await SignIn(page, browserName);
|
||||
|
||||
const [docParent] = await createDoc(
|
||||
page,
|
||||
|
|
@ -370,7 +374,7 @@ test.describe('Document create member: Multiple login', () => {
|
|||
|
||||
const otherBrowser = BROWSERS.find((b) => b !== browserName);
|
||||
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
await SignIn(page, otherBrowser!);
|
||||
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible({
|
||||
timeout: 10000,
|
||||
|
|
|
|||
|
|
@ -149,8 +149,10 @@ test.describe('Document list members', () => {
|
|||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
const list = page.getByTestId('doc-share-quick-search');
|
||||
await expect(list).toBeVisible();
|
||||
const emailRequest =
|
||||
process.env[`SIGN_IN_USERNAME_${browserName.toUpperCase()}`] || '';
|
||||
const currentUser = list.getByTestId(
|
||||
`doc-share-member-row-user.test@${browserName}.test`,
|
||||
`doc-share-member-row-${emailRequest}`,
|
||||
);
|
||||
const currentUserRole = currentUser.getByTestId('doc-role-dropdown');
|
||||
await expect(currentUser).toBeVisible();
|
||||
|
|
@ -214,8 +216,9 @@ test.describe('Document list members', () => {
|
|||
|
||||
const list = page.getByTestId('doc-share-quick-search');
|
||||
|
||||
const emailMyself = `user.test@${browserName}.test`;
|
||||
const mySelf = list.getByTestId(`doc-share-member-row-${emailMyself}`);
|
||||
const emailRequest =
|
||||
process.env[`SIGN_IN_USERNAME_${browserName.toUpperCase()}`] || '';
|
||||
const mySelf = list.getByTestId(`doc-share-member-row-${emailRequest}`);
|
||||
const mySelfRole = mySelf.getByTestId('doc-role-dropdown');
|
||||
|
||||
const userOwnerEmail = await addNewMember(page, 0, 'Owner');
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import { expect, test } from '@playwright/test';
|
|||
|
||||
import {
|
||||
createDoc,
|
||||
expectLoginPage,
|
||||
keyCloakSignIn,
|
||||
getCurrentConfig,
|
||||
mockedDocument,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { writeInEditor } from './utils-editor';
|
||||
import { SignIn, expectLoginPage } from './utils-signin';
|
||||
import { createRootSubPage } from './utils-sub-pages';
|
||||
|
||||
test.describe('Doc Routing', () => {
|
||||
|
|
@ -54,6 +54,13 @@ test.describe('Doc Routing', () => {
|
|||
});
|
||||
|
||||
test('checks 401 on docs/[id] page', async ({ page, browserName }) => {
|
||||
const currentConfig = await getCurrentConfig(page);
|
||||
|
||||
test.skip(
|
||||
currentConfig.FRONTEND_SILENT_LOGIN_ENABLED,
|
||||
'This test is only relevant when silent login is disabled.',
|
||||
);
|
||||
|
||||
const [docTitle] = await createDoc(page, '401-doc-parent', browserName, 1);
|
||||
await verifyDocName(page, docTitle);
|
||||
|
||||
|
|
@ -117,7 +124,7 @@ test.describe('Doc Routing: Not logged', () => {
|
|||
await page.goto(`/docs/${uuid}/`);
|
||||
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Login' }).click();
|
||||
await keyCloakSignIn(page, browserName, false);
|
||||
await SignIn(page, browserName, false);
|
||||
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -46,9 +46,27 @@ test.describe('Doc Trashbin', () => {
|
|||
|
||||
const docsGrid = page.getByTestId('docs-grid');
|
||||
await expect(docsGrid.getByText('Days remaining')).toBeVisible();
|
||||
await expect(row1.getByText(title1)).toBeVisible();
|
||||
|
||||
try {
|
||||
await expect(row1.getByText(title1)).toBeVisible();
|
||||
} catch {
|
||||
test.skip(
|
||||
true,
|
||||
'We skip this test, it will fails because too much document deleted in the trashbin and it is ordered by day remaining',
|
||||
);
|
||||
}
|
||||
|
||||
await expect(row1.getByText('30 days')).toBeVisible();
|
||||
await expect(row2.getByText(title2)).toBeVisible();
|
||||
|
||||
try {
|
||||
await expect(row2.getByText(title2)).toBeVisible();
|
||||
} catch {
|
||||
test.skip(
|
||||
true,
|
||||
'We skip this test, it will fails because too much document deleted in the trashbin and it is ordered by day remaining',
|
||||
);
|
||||
}
|
||||
|
||||
await expect(
|
||||
row2.getByRole('button', {
|
||||
name: 'Open the sharing settings for the document',
|
||||
|
|
@ -115,8 +133,18 @@ test.describe('Doc Trashbin', () => {
|
|||
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||
await page.getByRole('link', { name: 'Trashbin' }).click();
|
||||
const row = await getGridRow(page, subDocName);
|
||||
await row.getByText(subDocName).click();
|
||||
|
||||
let row;
|
||||
try {
|
||||
row = await getGridRow(page, subDocName);
|
||||
} catch {
|
||||
test.skip(
|
||||
true,
|
||||
'We skip this test, it will fails because too much document deleted in the trashbin and it is ordered by day remaining',
|
||||
);
|
||||
}
|
||||
|
||||
await row?.getByText(subDocName).click();
|
||||
await verifyDocName(page, subDocName);
|
||||
|
||||
await expect(
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@ import { expect, test } from '@playwright/test';
|
|||
|
||||
import {
|
||||
createDoc,
|
||||
expectLoginPage,
|
||||
keyCloakSignIn,
|
||||
getOtherBrowserName,
|
||||
updateDocTitle,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { addNewMember } from './utils-share';
|
||||
import { addNewMember, connectOtherUserToDoc } from './utils-share';
|
||||
import {
|
||||
addChild,
|
||||
clickOnAddRootSubPage,
|
||||
|
|
@ -28,10 +27,10 @@ test.describe('Doc Tree', () => {
|
|||
|
||||
const response = {
|
||||
count: 40,
|
||||
next: `http://localhost:8071/api/v1.0/documents/anything/children/?page=${parseInt(pageId) + 1}`,
|
||||
next: `${process.env.BASE_API_URL}/documents/anything/children/?page=${parseInt(pageId) + 1}`,
|
||||
previous:
|
||||
parseInt(pageId) > 1
|
||||
? `http://localhost:8071/api/v1.0/documents/anything/children/?page=${parseInt(pageId) - 1}`
|
||||
? `${process.env.BASE_API_URL}/documents/anything/children/?page=${parseInt(pageId) - 1}`
|
||||
: null,
|
||||
results: Array.from({ length: 20 }, (_, i) => ({
|
||||
id: `doc-child-${pageId}-${i}`,
|
||||
|
|
@ -142,8 +141,8 @@ test.describe('Doc Tree', () => {
|
|||
.click();
|
||||
|
||||
await expect(docTree.getByText('doc-child-1-19')).toBeVisible();
|
||||
await expect(docTree.locator('.c__spinner')).toBeVisible();
|
||||
await docTree.getByText('doc-child-1-19').hover();
|
||||
await expect(docTree.locator('.c__spinner')).toBeVisible();
|
||||
await expect(
|
||||
docTree.getByText('doc-child-2-1', {
|
||||
exact: true,
|
||||
|
|
@ -264,8 +263,7 @@ test.describe('Doc Tree', () => {
|
|||
page.getByRole('textbox', { name: 'Document title' }),
|
||||
).not.toHaveText(docChild);
|
||||
|
||||
const header = page.locator('header').first();
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||
await expect(page.getByText(docChild)).toBeVisible();
|
||||
});
|
||||
|
||||
|
|
@ -281,11 +279,14 @@ test.describe('Doc Tree', () => {
|
|||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
await addNewMember(page, 0, 'Owner', 'impress');
|
||||
const otherBrowserName = getOtherBrowserName(browserName);
|
||||
await addNewMember(page, 0, 'Owner', otherBrowserName);
|
||||
|
||||
const list = page.getByTestId('doc-share-quick-search');
|
||||
const currentEmail =
|
||||
process.env[`SIGN_IN_USERNAME_${browserName.toUpperCase()}`] || '';
|
||||
const currentUser = list.getByTestId(
|
||||
`doc-share-member-row-user.test@${browserName}.test`,
|
||||
`doc-share-member-row-${currentEmail}`,
|
||||
);
|
||||
const currentUserRole = currentUser.getByTestId('doc-role-dropdown');
|
||||
await currentUserRole.click();
|
||||
|
|
@ -492,19 +493,12 @@ test.describe('Doc Tree', () => {
|
|||
await expect(row.getByText('😀')).toBeHidden();
|
||||
await expect(titleEmojiPicker).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Doc Tree: Inheritance', () => {
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
|
||||
test('A child inherit from the parent', async ({ page, browserName }) => {
|
||||
// test.slow() to extend timeout since this scenario chains Keycloak login + redirects,
|
||||
// doc creation/navigation and async doc-tree loading (/documents/:id/tree), which can exceed 30s (especially in CI).
|
||||
test.slow();
|
||||
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
|
||||
const [docParent] = await createDoc(
|
||||
page,
|
||||
'doc-tree-inheritance-parent',
|
||||
|
|
@ -531,22 +525,19 @@ test.describe('Doc Tree: Inheritance', () => {
|
|||
'doc-tree-inheritance-child',
|
||||
);
|
||||
|
||||
const urlDoc = page.url();
|
||||
const docUrl = page.url();
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Logout',
|
||||
})
|
||||
.click();
|
||||
const { otherPage, cleanup } = await connectOtherUserToDoc({
|
||||
browserName,
|
||||
docUrl,
|
||||
withoutSignIn: true,
|
||||
docTitle: docChild,
|
||||
});
|
||||
|
||||
await expectLoginPage(page);
|
||||
|
||||
await page.goto(urlDoc);
|
||||
|
||||
await expect(page.locator('h2').getByText(docChild)).toBeVisible();
|
||||
|
||||
const docTree = page.getByTestId('doc-tree');
|
||||
const docTree = otherPage.getByTestId('doc-tree');
|
||||
await expect(docTree).toBeVisible({ timeout: 10000 });
|
||||
await expect(docTree.getByText(docParent)).toBeVisible();
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import {
|
||||
BROWSERS,
|
||||
createDoc,
|
||||
expectLoginPage,
|
||||
keyCloakSignIn,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { BROWSERS, createDoc, verifyDocName } from './utils-common';
|
||||
import { getEditor, writeInEditor } from './utils-editor';
|
||||
import { addNewMember, connectOtherUserToDoc } from './utils-share';
|
||||
import { SignIn, expectLoginPage } from './utils-signin';
|
||||
import { createRootSubPage } from './utils-sub-pages';
|
||||
|
||||
test.describe('Doc Visibility', () => {
|
||||
|
|
@ -74,7 +69,7 @@ test.describe('Doc Visibility: Restricted', () => {
|
|||
browserName,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
await SignIn(page, browserName);
|
||||
|
||||
const [docTitle] = await createDoc(
|
||||
page,
|
||||
|
|
@ -109,7 +104,7 @@ test.describe('Doc Visibility: Restricted', () => {
|
|||
test.slow();
|
||||
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
await SignIn(page, browserName);
|
||||
|
||||
const [docTitle] = await createDoc(page, 'Restricted auth', browserName, 1);
|
||||
|
||||
|
|
@ -128,7 +123,7 @@ test.describe('Doc Visibility: Restricted', () => {
|
|||
throw new Error('No alternative browser found');
|
||||
}
|
||||
|
||||
await keyCloakSignIn(page, otherBrowser);
|
||||
await SignIn(page, otherBrowser);
|
||||
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible({
|
||||
timeout: 10000,
|
||||
|
|
@ -146,7 +141,7 @@ test.describe('Doc Visibility: Restricted', () => {
|
|||
test('A doc is accessible when member.', async ({ page, browserName }) => {
|
||||
test.slow();
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
await SignIn(page, browserName);
|
||||
|
||||
const [docTitle] = await createDoc(page, 'Restricted auth', browserName, 1);
|
||||
|
||||
|
|
@ -369,15 +364,14 @@ test.describe('Doc Visibility: Public', () => {
|
|||
});
|
||||
|
||||
test.describe('Doc Visibility: Authenticated', () => {
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('A doc is not accessible when unauthenticated.', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
|
||||
const [docTitle] = await createDoc(
|
||||
page,
|
||||
'Authenticated unauthentified',
|
||||
|
|
@ -398,23 +392,21 @@ test.describe('Doc Visibility: Authenticated', () => {
|
|||
|
||||
await page.getByRole('button', { name: 'close' }).click();
|
||||
|
||||
const urlDoc = page.url();
|
||||
const docUrl = page.url();
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Logout',
|
||||
})
|
||||
.click();
|
||||
const { otherPage, cleanup } = await connectOtherUserToDoc({
|
||||
browserName,
|
||||
docUrl,
|
||||
withoutSignIn: true,
|
||||
});
|
||||
|
||||
await expectLoginPage(page);
|
||||
|
||||
await page.goto(urlDoc);
|
||||
|
||||
await expect(page.locator('h2').getByText(docTitle)).toBeHidden();
|
||||
await expect(otherPage.locator('h2').getByText(docTitle)).toBeHidden();
|
||||
|
||||
await expect(
|
||||
page.getByText('Log in to access the document.'),
|
||||
otherPage.getByText('Log in to access the document.'),
|
||||
).toBeVisible();
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
test('It checks a authenticated doc in read only mode', async ({
|
||||
|
|
@ -423,9 +415,6 @@ test.describe('Doc Visibility: Authenticated', () => {
|
|||
}) => {
|
||||
test.slow();
|
||||
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
|
||||
const [docTitle] = await createDoc(
|
||||
page,
|
||||
'Authenticated read only',
|
||||
|
|
@ -454,7 +443,7 @@ test.describe('Doc Visibility: Authenticated', () => {
|
|||
|
||||
await page.getByRole('button', { name: 'close' }).click();
|
||||
|
||||
const urlDoc = page.url();
|
||||
const docUrl = page.url();
|
||||
|
||||
const { name: childTitle } = await createRootSubPage(
|
||||
page,
|
||||
|
|
@ -464,56 +453,43 @@ test.describe('Doc Visibility: Authenticated', () => {
|
|||
|
||||
const urlChildDoc = page.url();
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Logout',
|
||||
})
|
||||
.click();
|
||||
|
||||
const otherBrowser = BROWSERS.find((b) => b !== browserName);
|
||||
if (!otherBrowser) {
|
||||
throw new Error('No alternative browser found');
|
||||
}
|
||||
await keyCloakSignIn(page, otherBrowser);
|
||||
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible({
|
||||
timeout: 10000,
|
||||
const { otherPage, cleanup } = await connectOtherUserToDoc({
|
||||
browserName,
|
||||
docUrl,
|
||||
docTitle,
|
||||
});
|
||||
|
||||
await page.goto(urlDoc);
|
||||
|
||||
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await page.getByRole('button', { name: 'Copy link' }).click();
|
||||
await expect(page.getByText('Link Copied !')).toBeVisible();
|
||||
await otherPage.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByText(
|
||||
otherPage.getByText(
|
||||
'You can view this document but need additional access to see its members or modify settings.',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Request access' }).click();
|
||||
await otherPage.getByRole('button', { name: 'Request access' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Request access' }),
|
||||
otherPage.getByRole('button', { name: 'Request access' }),
|
||||
).toBeDisabled();
|
||||
|
||||
await page.goto(urlChildDoc);
|
||||
await otherPage.goto(urlChildDoc);
|
||||
|
||||
await expect(page.locator('h2').getByText(childTitle)).toBeVisible();
|
||||
await expect(otherPage.locator('h2').getByText(childTitle)).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await otherPage.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByText(
|
||||
otherPage.getByText(
|
||||
'As this is a sub-document, please request access to the parent document to enable these features.',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Request access' }),
|
||||
otherPage.getByRole('button', { name: 'Request access' }),
|
||||
).toBeHidden();
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
test('It checks a authenticated doc in editable mode', async ({
|
||||
|
|
@ -521,8 +497,6 @@ test.describe('Doc Visibility: Authenticated', () => {
|
|||
browserName,
|
||||
}) => {
|
||||
test.slow();
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
|
||||
const [docTitle] = await createDoc(
|
||||
page,
|
||||
|
|
@ -542,7 +516,7 @@ test.describe('Doc Visibility: Authenticated', () => {
|
|||
page.getByText('The document visibility has been updated.'),
|
||||
).toBeVisible();
|
||||
|
||||
const urlDoc = page.url();
|
||||
const docUrl = page.url();
|
||||
await page.getByTestId('doc-access-mode').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Editing' }).click();
|
||||
|
||||
|
|
@ -552,29 +526,24 @@ test.describe('Doc Visibility: Authenticated', () => {
|
|||
|
||||
await page.getByRole('button', { name: 'close' }).click();
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Logout',
|
||||
})
|
||||
.click();
|
||||
|
||||
const otherBrowser = BROWSERS.find((b) => b !== browserName);
|
||||
if (!otherBrowser) {
|
||||
throw new Error('No alternative browser found');
|
||||
}
|
||||
await keyCloakSignIn(page, otherBrowser);
|
||||
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible({
|
||||
timeout: 10000,
|
||||
const { otherPage, cleanup } = await connectOtherUserToDoc({
|
||||
browserName,
|
||||
docUrl,
|
||||
docTitle,
|
||||
});
|
||||
|
||||
await page.goto(urlDoc);
|
||||
await otherPage.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
await verifyDocName(page, docTitle);
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await page.getByRole('button', { name: 'Copy link' }).click();
|
||||
await expect(page.getByText('Link Copied !')).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await expect(
|
||||
otherPage.getByText(
|
||||
'You can view this document but need additional access to see its members or modify settings.',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
otherPage.getByRole('button', { name: 'Request access' }),
|
||||
).toBeVisible();
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,45 +14,47 @@ test.describe('Footer', () => {
|
|||
await expect(page.locator('footer')).toBeHidden();
|
||||
});
|
||||
|
||||
test('checks all the elements are visible', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
const footer = page.locator('footer').first();
|
||||
if (process.env.IS_INSTANCE !== 'true') {
|
||||
test('checks all the elements are visible', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
const footer = page.locator('footer').first();
|
||||
|
||||
await expect(footer.getByAltText('Docs Logo')).toBeVisible();
|
||||
await expect(footer.getByRole('heading', { name: 'Docs' })).toBeVisible();
|
||||
await expect(footer.getByAltText('Docs Logo')).toBeVisible();
|
||||
await expect(footer.getByRole('heading', { name: 'Docs' })).toBeVisible();
|
||||
|
||||
await expect(footer.getByRole('link', { name: 'GitHub' })).toBeVisible();
|
||||
await expect(footer.getByRole('link', { name: 'DINUM' })).toBeVisible();
|
||||
await expect(footer.getByRole('link', { name: 'ZenDiS' })).toBeVisible();
|
||||
await expect(footer.getByRole('link', { name: 'GitHub' })).toBeVisible();
|
||||
await expect(footer.getByRole('link', { name: 'DINUM' })).toBeVisible();
|
||||
await expect(footer.getByRole('link', { name: 'ZenDiS' })).toBeVisible();
|
||||
|
||||
await expect(
|
||||
footer.getByRole('link', { name: 'BlockNote.js' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
footer.getByRole('link', { name: 'Legal Notice' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
footer.getByRole('link', { name: 'Personal data and cookies' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
footer.getByRole('link', { name: 'Accessibility' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
footer.getByRole('link', { name: 'BlockNote.js' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
footer.getByRole('link', { name: 'Legal Notice' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
footer.getByRole('link', { name: 'Personal data and cookies' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
footer.getByRole('link', { name: 'Accessibility' }),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
footer.getByText(
|
||||
'Unless otherwise stated, all content on this site is under licence',
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
footer.getByText(
|
||||
'Unless otherwise stated, all content on this site is under licence',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
// Check the translation
|
||||
const header = page.locator('header').first();
|
||||
await header.getByRole('button').getByText('English').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Français' }).click();
|
||||
// Check the translation
|
||||
const header = page.locator('header').first();
|
||||
await header.getByRole('button').getByText('English').click();
|
||||
await page.getByRole('menuitemradio', { name: 'Français' }).click();
|
||||
|
||||
await expect(
|
||||
page.locator('footer').getByText('Mentions légales'),
|
||||
).toBeVisible();
|
||||
});
|
||||
await expect(
|
||||
page.locator('footer').getByText('Mentions légales'),
|
||||
).toBeVisible();
|
||||
});
|
||||
}
|
||||
|
||||
test('checks the footer is correctly overrided', async ({ page }) => {
|
||||
await overrideConfig(page, {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import {
|
||||
expectLoginPage,
|
||||
keyCloakSignIn,
|
||||
overrideConfig,
|
||||
} from './utils-common';
|
||||
import { overrideConfig } from './utils-common';
|
||||
import { SignIn, expectLoginPage } from './utils-signin';
|
||||
|
||||
test.describe('Header', () => {
|
||||
test('checks all the elements are visible', async ({ page }) => {
|
||||
|
|
@ -142,27 +139,31 @@ test.describe('Header', () => {
|
|||
await expect(page.getByRole('link', { name: 'Grist' })).toBeVisible();
|
||||
await expect(page.getByRole('link', { name: 'Visio' })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Header: Log out', () => {
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
|
||||
// eslint-disable-next-line playwright/expect-expect
|
||||
test('checks logout button', async ({ page, browserName }) => {
|
||||
test('it displays skip link on first TAB and focuses page heading on click', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Logout',
|
||||
})
|
||||
.click();
|
||||
// Wait for skip link to be mounted (client-side only component)
|
||||
const skipLink = page.getByRole('link', { name: 'Go to content' });
|
||||
await skipLink.waitFor({ state: 'attached' });
|
||||
|
||||
await expectLoginPage(page);
|
||||
// First TAB shows the skip link
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// The skip link should be visible and focused
|
||||
await expect(skipLink).toBeFocused();
|
||||
await expect(skipLink).toBeVisible();
|
||||
// Clicking moves focus to the page heading
|
||||
await skipLink.click();
|
||||
const pageHeading = page.getByRole('heading', {
|
||||
name: 'All docs',
|
||||
level: 2,
|
||||
});
|
||||
await expect(pageHeading).toBeFocused();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Header: Override configuration', () => {
|
||||
test('checks the header is correctly overrided', async ({ page }) => {
|
||||
await overrideConfig(page, {
|
||||
FRONTEND_THEME: 'dsfr',
|
||||
|
|
@ -190,28 +191,20 @@ test.describe('Header: Override configuration', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test.describe('Header: Skip to Content', () => {
|
||||
test('it displays skip link on first TAB and focuses page heading on click', async ({
|
||||
page,
|
||||
}) => {
|
||||
test.describe('Header: Log out', () => {
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
|
||||
// eslint-disable-next-line playwright/expect-expect
|
||||
test('checks logout button', async ({ page, browserName }) => {
|
||||
await page.goto('/');
|
||||
await SignIn(page, browserName);
|
||||
|
||||
// Wait for skip link to be mounted (client-side only component)
|
||||
const skipLink = page.getByRole('link', { name: 'Go to content' });
|
||||
await skipLink.waitFor({ state: 'attached' });
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Logout',
|
||||
})
|
||||
.click();
|
||||
|
||||
// First TAB shows the skip link
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
// The skip link should be visible and focused
|
||||
await expect(skipLink).toBeFocused();
|
||||
await expect(skipLink).toBeVisible();
|
||||
// Clicking moves focus to the page heading
|
||||
await skipLink.click();
|
||||
const pageHeading = page.getByRole('heading', {
|
||||
name: 'All docs',
|
||||
level: 2,
|
||||
});
|
||||
await expect(pageHeading).toBeFocused();
|
||||
await expectLoginPage(page);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@ import {
|
|||
} from './utils-common';
|
||||
|
||||
test.describe('Help feature', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test.describe('Onboarding modal', () => {
|
||||
test('Help menu not displayed if onboarding is disabled', async ({
|
||||
page,
|
||||
|
|
@ -23,6 +19,8 @@ test.describe('Help feature', () => {
|
|||
},
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await expect(page.getByRole('button', { name: 'New doc' })).toBeVisible();
|
||||
|
||||
await expect(
|
||||
|
|
@ -42,6 +40,8 @@ test.describe('Help feature', () => {
|
|||
},
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await page.getByRole('button', { name: 'Open help menu' }).click();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Onboarding' }).click();
|
||||
|
|
@ -86,23 +86,21 @@ test.describe('Help feature', () => {
|
|||
});
|
||||
|
||||
test('closes modal with Skip button', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
await page.getByRole('button', { name: 'Open help menu' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Onboarding' }).click();
|
||||
|
||||
const modal = page.getByTestId('onboarding-modal');
|
||||
await expect(modal).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', {
|
||||
name: 'Learn more docs features',
|
||||
}),
|
||||
).toBeHidden();
|
||||
|
||||
await page.getByRole('button', { name: /skip/i }).click();
|
||||
await expect(modal).toBeHidden();
|
||||
});
|
||||
|
||||
test('Modal onboarding translated correctly', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// switch to french
|
||||
await waitForLanguageSwitch(page, TestLanguage.French);
|
||||
|
||||
|
|
@ -131,6 +129,8 @@ test.describe('Help feature', () => {
|
|||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
|
||||
await expect(page.getByRole('button', { name: 'New doc' })).toBeVisible();
|
||||
await expect(page.getByTestId('onboarding-modal')).toBeHidden();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@ import { expect, test } from '@playwright/test';
|
|||
|
||||
import { overrideConfig } from './utils-common';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/docs/');
|
||||
});
|
||||
|
||||
test.describe('Home page', () => {
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
|
||||
|
|
@ -23,7 +19,6 @@ test.describe('Home page', () => {
|
|||
await expect(languageButton).toBeVisible();
|
||||
|
||||
await expect(header.getByTestId('header-icon-docs')).toBeVisible();
|
||||
await expect(header.getByRole('heading', { name: 'Docs' })).toBeVisible();
|
||||
|
||||
// Check the titles
|
||||
const h2 = page.locator('h2');
|
||||
|
|
@ -69,7 +64,9 @@ test.describe('Home page', () => {
|
|||
h2.getByText('A new way to organize knowledge.'),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Writing' }),
|
||||
page
|
||||
.getByRole('button', { name: process.env.SIGN_IN_EL_TRIGGER })
|
||||
.first(),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(footer).toBeVisible();
|
||||
|
|
@ -178,7 +175,7 @@ test.describe('Home page', () => {
|
|||
|
||||
// Keyclock login page
|
||||
await expect(
|
||||
page.locator('.login-pf #kc-header-wrapper').getByText('impress'),
|
||||
page.locator(`${process.env.SIGN_IN_EL_LOGIN_PAGE}`).getByText('impress'),
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ test.describe('Language', () => {
|
|||
// Helper function to intercept and assert 404 response
|
||||
const check404Response = async (expectedDetail: string) => {
|
||||
const interceptedBackendResponse = await page.request.get(
|
||||
'http://localhost:8071/api/v1.0/documents/non-existent-doc-uuid/',
|
||||
`${process.env.BASE_API_URL}/documents/non-existent-doc-uuid/`,
|
||||
);
|
||||
|
||||
// Assert that the intercepted error message is in the expected language
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export const CONFIG = {
|
|||
AI_FEATURE_LEGACY_ENABLED: true,
|
||||
API_USERS_SEARCH_QUERY_MIN_LENGTH: 3,
|
||||
CRISP_WEBSITE_ID: null,
|
||||
COLLABORATION_WS_URL: 'ws://localhost:4444/collaboration/ws/',
|
||||
COLLABORATION_WS_URL: process.env.COLLABORATION_WS_URL,
|
||||
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY: true,
|
||||
CONVERSION_UPLOAD_ENABLED: true,
|
||||
CONVERSION_FILE_EXTENSIONS_ALLOWED: ['.docx', '.md'],
|
||||
|
|
@ -29,7 +29,7 @@ export const CONFIG = {
|
|||
FRONTEND_HOMEPAGE_FEATURE_ENABLED: true,
|
||||
FRONTEND_SILENT_LOGIN_ENABLED: false,
|
||||
FRONTEND_THEME: null,
|
||||
MEDIA_BASE_URL: 'http://localhost:8083',
|
||||
MEDIA_BASE_URL: process.env.MEDIA_BASE_URL,
|
||||
LANGUAGES: [
|
||||
['en-us', 'English'],
|
||||
['fr-fr', 'Français'],
|
||||
|
|
@ -62,29 +62,18 @@ export const overrideConfig = async (
|
|||
}
|
||||
});
|
||||
|
||||
export const keyCloakSignIn = async (
|
||||
page: Page,
|
||||
browserName: string,
|
||||
fromHome = true,
|
||||
) => {
|
||||
if (fromHome) {
|
||||
await page.getByRole('button', { name: 'Start Writing' }).first().click();
|
||||
}
|
||||
export const getCurrentConfig = async (page: Page) => {
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/config/') && response.status() === 200,
|
||||
);
|
||||
|
||||
const login = `user-e2e-${browserName}`;
|
||||
const password = `password-e2e-${browserName}`;
|
||||
await page.goto('/');
|
||||
|
||||
await expect(
|
||||
page.locator('.login-pf #kc-header-wrapper').getByText('impress'),
|
||||
).toBeVisible();
|
||||
const response = await responsePromise;
|
||||
expect(response.ok()).toBeTruthy();
|
||||
|
||||
if (await page.getByLabel('Restart login').isVisible()) {
|
||||
await page.getByLabel('Restart login').click();
|
||||
}
|
||||
|
||||
await page.getByRole('textbox', { name: 'username' }).fill(login);
|
||||
await page.getByRole('textbox', { name: 'password' }).fill(password);
|
||||
await page.click('button[type="submit"]', { force: true });
|
||||
return (await response.json()) as typeof CONFIG;
|
||||
};
|
||||
|
||||
export const getOtherBrowserName = (browserName: BrowserName) => {
|
||||
|
|
@ -209,8 +198,11 @@ export const goToGridDoc = async (
|
|||
page: Page,
|
||||
{ nthRow = 1, title }: GoToGridDocOptions = {},
|
||||
) => {
|
||||
const header = page.locator('header').first();
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
if (
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).isVisible()
|
||||
) {
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||
}
|
||||
|
||||
const docsGrid = page.getByTestId('docs-grid');
|
||||
await expect(docsGrid).toBeVisible();
|
||||
|
|
@ -325,13 +317,6 @@ export const mockedListDocs = async (page: Page, data: object[] = []) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const expectLoginPage = async (page: Page) =>
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Collaborative writing' }),
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// language helper
|
||||
export const TestLanguage = {
|
||||
English: {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,9 @@ export const overrideDocContent = async ({
|
|||
const image = page
|
||||
.locator('.--docs--editor-container img.bn-visual-media[src$=".svg"]')
|
||||
.first();
|
||||
await expect(image).toBeVisible();
|
||||
await expect(image).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { Page, chromium, expect } from '@playwright/test';
|
|||
import {
|
||||
BrowserName,
|
||||
getOtherBrowserName,
|
||||
keyCloakSignIn,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { SignIn } from './utils-signin';
|
||||
|
||||
export type Role = 'Administrator' | 'Owner' | 'Editor' | 'Reader';
|
||||
export type LinkReach = 'Private' | 'Connected' | 'Public';
|
||||
|
|
@ -131,14 +131,14 @@ export const connectOtherUserToDoc = async ({
|
|||
.getByRole('main', { name: 'Main content' })
|
||||
.getByLabel('Login');
|
||||
const loginFromHome = otherPage.getByRole('button', {
|
||||
name: 'Start Writing',
|
||||
name: process.env.SIGN_IN_EL_TRIGGER,
|
||||
});
|
||||
|
||||
await loginFromApp.or(loginFromHome).first().click({
|
||||
timeout: 15000,
|
||||
});
|
||||
|
||||
await keyCloakSignIn(otherPage, otherBrowserName, false);
|
||||
await SignIn(otherPage, otherBrowserName, false);
|
||||
}
|
||||
if (docTitle) {
|
||||
await verifyDocName(otherPage, docTitle);
|
||||
|
|
|
|||
94
src/frontend/apps/e2e/__tests__/app-impress/utils-signin.ts
Normal file
94
src/frontend/apps/e2e/__tests__/app-impress/utils-signin.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
export const SignIn = async (
|
||||
page: Page,
|
||||
browserName: string,
|
||||
fromHome = true,
|
||||
) => {
|
||||
if (process.env.CUSTOM_SIGN_IN === 'true') {
|
||||
await customSignIn(page, browserName, fromHome);
|
||||
return;
|
||||
}
|
||||
|
||||
await keycloakSignIn(page, browserName, fromHome);
|
||||
};
|
||||
|
||||
export const customSignIn = async (
|
||||
page: Page,
|
||||
browserName: string,
|
||||
fromHome = true,
|
||||
) => {
|
||||
// Check if already signed in (Silent login or session still valid)
|
||||
if (
|
||||
await page
|
||||
.locator('header')
|
||||
.first()
|
||||
.getByRole('button', {
|
||||
name: 'Logout',
|
||||
})
|
||||
.isVisible()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fromHome) {
|
||||
await page
|
||||
.getByRole('button', { name: process.env.SIGN_IN_EL_TRIGGER })
|
||||
.first()
|
||||
.click();
|
||||
}
|
||||
|
||||
await page
|
||||
.getByRole('textbox', { name: process.env.SIGN_IN_EL_USERNAME_INPUT })
|
||||
.fill(process.env[`SIGN_IN_USERNAME_${browserName.toUpperCase()}`] || '');
|
||||
|
||||
if (process.env.SIGN_IN_EL_USERNAME_VALIDATION) {
|
||||
await page
|
||||
.getByRole('button', { name: process.env.SIGN_IN_EL_USERNAME_VALIDATION })
|
||||
.first()
|
||||
.click();
|
||||
}
|
||||
|
||||
await page
|
||||
.locator(
|
||||
`input[name="${process.env.SIGN_IN_EL_PASSWORD_INPUT || 'password'}"]`,
|
||||
)
|
||||
.fill(process.env[`SIGN_IN_PASSWORD_${browserName.toUpperCase()}`] || '');
|
||||
|
||||
await page.click('button[type="submit"]', { force: true });
|
||||
};
|
||||
|
||||
export const keycloakSignIn = async (
|
||||
page: Page,
|
||||
browserName: string,
|
||||
fromHome = true,
|
||||
) => {
|
||||
if (fromHome) {
|
||||
await page
|
||||
.getByRole('button', { name: process.env.SIGN_IN_EL_TRIGGER })
|
||||
.first()
|
||||
.click();
|
||||
}
|
||||
|
||||
const login = `user-e2e-${browserName}`;
|
||||
const password = `password-e2e-${browserName}`;
|
||||
|
||||
await expect(
|
||||
page.locator('.login-pf #kc-header-wrapper').getByText('impress'),
|
||||
).toBeVisible();
|
||||
|
||||
if (await page.getByLabel('Restart login').isVisible()) {
|
||||
await page.getByLabel('Restart login').click();
|
||||
}
|
||||
|
||||
await page.getByRole('textbox', { name: 'username' }).fill(login);
|
||||
await page.getByRole('textbox', { name: 'password' }).fill(password);
|
||||
await page.click('button[type="submit"]', { force: true });
|
||||
};
|
||||
|
||||
export const expectLoginPage = async (page: Page) =>
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Collaborative writing' }),
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
"dependencies": {
|
||||
"@types/pngjs": "6.0.5",
|
||||
"convert-stream": "1.0.2",
|
||||
"dotenv": "17.3.1",
|
||||
"pdf-parse": "2.4.5",
|
||||
"pixelmatch": "7.1.0",
|
||||
"pngjs": "7.0.0"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
import { defineConfig, devices } from '@playwright/test';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
dotenv.config({
|
||||
path: ['./.env.local', './.env'],
|
||||
quiet: true,
|
||||
debug: !process.env.CI,
|
||||
});
|
||||
|
||||
const baseURL = `http://localhost:${PORT}`;
|
||||
const PORT = process.env.PORT;
|
||||
const baseURL = process.env.BASE_URL;
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
|
|
@ -23,7 +29,10 @@ export default defineConfig({
|
|||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 3 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: [['html', { outputFolder: './report' }]],
|
||||
reporter: [
|
||||
['html', { outputFolder: './report' }],
|
||||
['list', { printSteps: true }],
|
||||
],
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
baseURL,
|
||||
|
|
@ -31,13 +40,16 @@ export default defineConfig({
|
|||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
|
||||
webServer: {
|
||||
command: !process.env.CI ? `cd ../.. && yarn app:dev --port ${PORT}` : '',
|
||||
url: baseURL,
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: true,
|
||||
},
|
||||
...(process.env.CI
|
||||
? {}
|
||||
: {
|
||||
webServer: {
|
||||
command: `cd ../.. && yarn app:dev --port ${PORT}`,
|
||||
url: baseURL,
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: true,
|
||||
},
|
||||
}),
|
||||
globalSetup: require.resolve('./__tests__/app-impress/auth.setup'),
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
|
|
|
|||
Loading…
Reference in a new issue