Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,13 @@ and this project adheres to
- ♿(frontend) improve accessibility:
- #1354
- ♿ improve accessibility by adding landmark roles to layout #1394
- ✨ add document visible in list and openable via enter key #1365
- ♿ add pdf outline property to enable bookmarks display #1368

### Fixed

- 🐛(backend) duplicate sub docs as root for reader users

### Changed

- ♿(frontend) improve accessibility:
- ✨ add document visible in list and openable via enter key #1365

### Changed

- ♿(frontend) improve accessibility:
- ♿ add pdf outline property to enable bookmarks display #1368
- 🐛(frontend) fix 404 page when reload 403 page #1402

## [3.7.0] - 2025-09-12

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
randomName,
verifyDocName,
} from './utils-common';
import { connectOtherUserToDoc, updateRoleUser } from './utils-share';
import { createRootSubPage } from './utils-sub-pages';

test.describe('Document create member', () => {
Expand Down Expand Up @@ -209,20 +210,13 @@ test.describe('Document create member', () => {

await expect(userInvitation).toBeHidden();
});
});

test.describe('Document create member: Multiple login', () => {
test.use({ storageState: { cookies: [], origins: [] } });

test('It creates a member from a request coming from a 403 page', async ({
page,
browserName,
}) => {
test.slow();

await page.goto('/');
await keyCloakSignIn(page, browserName);

const [docTitle] = await createDoc(
page,
'Member access request',
Expand All @@ -232,55 +226,37 @@ test.describe('Document create member: Multiple login', () => {

await verifyDocName(page, docTitle);

const urlDoc = page.url();

await page
.getByRole('button', {
name: 'Logout',
})
.click();

const otherBrowser = BROWSERS.find((b) => b !== browserName);

await keyCloakSignIn(page, otherBrowser!);
.locator('.ProseMirror')
.locator('.bn-block-outer')
.last()
.fill('Hello World');

await expect(page.getByTestId('header-logo-link')).toBeVisible();
const urlDoc = page.url();

await page.goto(urlDoc);
// Other user will request access
const { otherPage, otherBrowserName, cleanup } =
await connectOtherUserToDoc(browserName, urlDoc);

await expect(
page.getByText('Insufficient access rights to view the document.'),
otherPage.getByText('Insufficient access rights to view the document.'),
).toBeVisible({
timeout: 10000,
});

await page.getByRole('button', { name: 'Request access' }).click();
await otherPage.getByRole('button', { name: 'Request access' }).click();

await expect(
page.getByText('Your access request for this document is pending.'),
otherPage.getByText('Your access request for this document is pending.'),
).toBeVisible();

await page
.getByRole('button', {
name: 'Logout',
})
.click();

await page.goto('/');
await keyCloakSignIn(page, browserName);

await expect(page.getByTestId('header-logo-link')).toBeVisible({
timeout: 10000,
});

await page.goto(urlDoc);

// First user approves the request
await page.getByRole('button', { name: 'Share' }).click();

await expect(page.getByText('Access Requests')).toBeVisible();
await expect(page.getByText(`E2E ${otherBrowser}`)).toBeVisible();
await expect(page.getByText(`E2E ${otherBrowserName}`)).toBeVisible();

const emailRequest = `user.test@${otherBrowser}.test`;
const emailRequest = `user.test@${otherBrowserName}.test`;
await expect(page.getByText(emailRequest)).toBeVisible();
const container = page.getByTestId(
`doc-share-access-request-row-${emailRequest}`,
Expand All @@ -291,8 +267,26 @@ test.describe('Document create member: Multiple login', () => {

await expect(page.getByText('Access Requests')).toBeHidden();
await expect(page.getByText('Share with 2 users')).toBeVisible();
await expect(page.getByText(`E2E ${otherBrowser}`)).toBeVisible();
await expect(page.getByText(`E2E ${otherBrowserName}`)).toBeVisible();

// Other user verifies he has access
await otherPage.reload();
await verifyDocName(otherPage, docTitle);
await expect(otherPage.getByText('Hello World')).toBeVisible();

// Revoke access
await updateRoleUser(page, 'Remove access', emailRequest);
await expect(
otherPage.getByText('Insufficient access rights to view the document.'),
).toBeVisible();

// Cleanup: other user logout
await cleanup();
});
});

test.describe('Document create member: Multiple login', () => {
test.use({ storageState: { cookies: [], origins: [] } });

test('It cannot request member access on child doc on a 403 page', async ({
page,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ test.describe('Doc Routing', () => {
await expect(page.getByText('Log in to access the document.')).toBeVisible({
timeout: 10000,
});

await expect(page.locator('meta[name="robots"]')).toHaveAttribute(
'content',
'noindex',
);
await expect(page).toHaveTitle(/401 Unauthorized - Docs/);
});
});

Expand Down
53 changes: 19 additions & 34 deletions src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
keyCloakSignIn,
verifyDocName,
} from './utils-common';
import { addNewMember, connectOtherUserToDoc } from './utils-share';
import { createRootSubPage } from './utils-sub-pages';

test.describe('Doc Visibility', () => {
Expand Down Expand Up @@ -146,47 +147,31 @@ test.describe('Doc Visibility: Restricted', () => {

await verifyDocName(page, docTitle);

await page.getByRole('button', { name: 'Share' }).click();

const inputSearch = page.getByRole('combobox', {
name: 'Quick search input',
});

const otherBrowser = BROWSERS.find((b) => b !== browserName);
if (!otherBrowser) {
throw new Error('No alternative browser found');
}
const username = `user.test@${otherBrowser}.test`;
await inputSearch.fill(username);
await page.getByRole('option', { name: username }).click();

// Choose a role
const container = page.getByTestId('doc-share-add-member-list');
await container.getByLabel('doc-role-dropdown').click();
await page.getByLabel('Reader').click();

await page.getByRole('button', { name: 'Invite' }).click();

await page.locator('.c__modal__backdrop').click({
position: { x: 0, y: 0 },
});
await page
.locator('.ProseMirror')
.locator('.bn-block-outer')
.last()
.fill('Hello World');

const urlDoc = page.url();

await page
.getByRole('button', {
name: 'Logout',
})
.click();
const { otherBrowserName, otherPage } = await connectOtherUserToDoc(
browserName,
urlDoc,
);

await keyCloakSignIn(page, otherBrowser);
await expect(
otherPage.getByText('Insufficient access rights to view the document.'),
).toBeVisible({
timeout: 10000,
});

await expect(page.getByTestId('header-logo-link')).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();

await page.goto(urlDoc);
await addNewMember(page, 0, 'Reader', otherBrowserName);

await verifyDocName(page, docTitle);
await expect(page.getByLabel('Share button')).toBeVisible();
await otherPage.reload();
await expect(otherPage.getByText('Hello World')).toBeVisible();
});
});

Expand Down
3 changes: 2 additions & 1 deletion src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Page, expect } from '@playwright/test';

export const BROWSERS = ['chromium', 'webkit', 'firefox'];
export type BrowserName = 'chromium' | 'firefox' | 'webkit';
export const BROWSERS: BrowserName[] = ['chromium', 'webkit', 'firefox'];

export const CONFIG = {
AI_FEATURE_ENABLED: true,
Expand Down
73 changes: 72 additions & 1 deletion src/frontend/apps/e2e/__tests__/app-impress/utils-share.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Page, expect } from '@playwright/test';
import { Page, chromium, expect } from '@playwright/test';

import {
BROWSERS,
BrowserName,
keyCloakSignIn,
verifyDocName,
} from './utils-common';

export type Role = 'Administrator' | 'Owner' | 'Member' | 'Editor' | 'Reader';
export type LinkReach = 'Private' | 'Connected' | 'Public';
Expand Down Expand Up @@ -61,6 +68,70 @@ export const updateShareLink = async (
}
};

export const updateRoleUser = async (
page: Page,
role: Role | 'Remove access',
email: string,
) => {
const list = page.getByTestId('doc-share-quick-search');

const currentUser = list.getByTestId(`doc-share-member-row-${email}`);
const currentUserRole = currentUser.getByLabel('doc-role-dropdown');
await currentUserRole.click();
await page.getByLabel(role).click();
await list.click();
};

/**
* Connects another user to a document.
* Useful to test real-time collaboration features.
* @param browserName The name of the browser to use.
* @param docUrl The URL of the document to connect to.
* @param docTitle The title of the document (optional).
* @returns An object containing the other browser, context, and page.
*/
export const connectOtherUserToDoc = async (
browserName: BrowserName,
docUrl: string,
docTitle?: string,
) => {
const otherBrowserName = BROWSERS.find((b) => b !== browserName);
if (!otherBrowserName) {
throw new Error('No alternative browser found');
}

const otherBrowser = await chromium.launch({ headless: true });
const otherContext = await otherBrowser.newContext({
locale: 'en-US',
timezoneId: 'Europe/Paris',
permissions: [],
storageState: {
cookies: [],
origins: [],
},
});
const otherPage = await otherContext.newPage();
await otherPage.goto(docUrl);

await otherPage.getByRole('button', { name: 'Login' }).click({
timeout: 15000,
});

await keyCloakSignIn(otherPage, otherBrowserName, false);

if (docTitle) {
await verifyDocName(otherPage, docTitle);
}

const cleanup = async () => {
await otherPage.close();
await otherContext.close();
await otherBrowser.close();
};

return { otherBrowser, otherContext, otherPage, otherBrowserName, cleanup };
};

export const mockedInvitations = async (page: Page, json?: object) => {
let result = [
{
Expand Down
Loading
Loading