mirror of
https://github.com/suitenumerique/docs.git
synced 2026-04-25 17:15:01 +02:00
We replace icons with what is used in the ui kit. By replacing the icons, we also fix the height problems with Firefox.
579 lines
17 KiB
TypeScript
579 lines
17 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
import {
|
|
createDoc,
|
|
getGridRow,
|
|
getOtherBrowserName,
|
|
mockedListDocs,
|
|
toggleHeaderMenu,
|
|
verifyDocName,
|
|
} from './utils-common';
|
|
import { writeInEditor } from './utils-editor';
|
|
import {
|
|
addNewMember,
|
|
connectOtherUserToDoc,
|
|
updateShareLink,
|
|
} from './utils-share';
|
|
import { createRootSubPage } from './utils-sub-pages';
|
|
|
|
test.describe('Doc grid move', () => {
|
|
test('it checks drag and drop functionality', async ({
|
|
page,
|
|
browserName,
|
|
}) => {
|
|
await page.goto('/');
|
|
await createDoc(page, 'Draggable doc', browserName, 1);
|
|
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
|
await createDoc(page, 'Droppable doc', browserName, 1);
|
|
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
|
|
|
const response = await page.waitForResponse(
|
|
(response) =>
|
|
response.url().endsWith('documents/?page=1') &&
|
|
response.status() === 200,
|
|
);
|
|
const responseJson = await response.json();
|
|
|
|
const items = responseJson.results;
|
|
|
|
const docsGrid = page.getByTestId('docs-grid');
|
|
await expect(docsGrid).toBeVisible();
|
|
await expect(page.getByTestId('grid-loader')).toBeHidden();
|
|
const draggableElement = page.getByTestId(`draggable-doc-${items[1].id}`);
|
|
const dropZone = page.getByTestId(`droppable-doc-${items[0].id}`);
|
|
await expect(draggableElement).toBeVisible();
|
|
await expect(dropZone).toBeVisible();
|
|
|
|
// Get the position of the elements
|
|
const draggableBoundingBox = await draggableElement.boundingBox();
|
|
const dropZoneBoundingBox = await dropZone.boundingBox();
|
|
|
|
expect(draggableBoundingBox).toBeDefined();
|
|
expect(dropZoneBoundingBox).toBeDefined();
|
|
|
|
if (!draggableBoundingBox || !dropZoneBoundingBox) {
|
|
throw new Error('Unable to determine the position of the elements');
|
|
}
|
|
|
|
await page.mouse.move(
|
|
draggableBoundingBox.x + draggableBoundingBox.width / 2,
|
|
draggableBoundingBox.y + draggableBoundingBox.height / 2,
|
|
);
|
|
await page.mouse.down();
|
|
|
|
// Move to the target zone
|
|
await page.mouse.move(
|
|
dropZoneBoundingBox.x + dropZoneBoundingBox.width / 2,
|
|
dropZoneBoundingBox.y + dropZoneBoundingBox.height / 2,
|
|
{ steps: 10 }, // Make the movement smoother
|
|
);
|
|
|
|
const dragOverlay = page.getByTestId('drag-doc-overlay');
|
|
|
|
await expect(dragOverlay).toBeVisible();
|
|
await expect(dragOverlay).toHaveText(items[1].title as string);
|
|
await page.mouse.up();
|
|
|
|
await expect(dragOverlay).toBeHidden();
|
|
});
|
|
|
|
test("it checks can't drop when we have not the minimum role", async ({
|
|
page,
|
|
}) => {
|
|
await mockedListDocs(page, data);
|
|
await page.goto('/');
|
|
const docsGrid = page.getByTestId('docs-grid');
|
|
await expect(docsGrid).toBeVisible();
|
|
await expect(page.getByTestId('grid-loader')).toBeHidden();
|
|
|
|
const canDropAndDrag = page.getByTestId('droppable-doc-can-drop-and-drag');
|
|
|
|
const noDropAndNoDrag = page.getByTestId(
|
|
'droppable-doc-no-drop-and-no-drag',
|
|
);
|
|
|
|
await expect(canDropAndDrag).toBeVisible();
|
|
|
|
await expect(noDropAndNoDrag).toBeVisible();
|
|
|
|
const canDropAndDragBoundigBox = await canDropAndDrag.boundingBox();
|
|
|
|
const noDropAndNoDragBoundigBox = await noDropAndNoDrag.boundingBox();
|
|
|
|
if (!canDropAndDragBoundigBox || !noDropAndNoDragBoundigBox) {
|
|
throw new Error('Unable to determine the position of the elements');
|
|
}
|
|
|
|
await page.mouse.move(
|
|
canDropAndDragBoundigBox.x + canDropAndDragBoundigBox.width / 2,
|
|
canDropAndDragBoundigBox.y + canDropAndDragBoundigBox.height / 2,
|
|
);
|
|
|
|
await page.mouse.down();
|
|
|
|
await page.mouse.move(
|
|
noDropAndNoDragBoundigBox.x + noDropAndNoDragBoundigBox.width / 2,
|
|
noDropAndNoDragBoundigBox.y + noDropAndNoDragBoundigBox.height / 2,
|
|
{ steps: 10 },
|
|
);
|
|
|
|
const dragOverlay = page.getByTestId('drag-doc-overlay');
|
|
|
|
await expect(dragOverlay).toBeVisible();
|
|
await expect(dragOverlay).toHaveText(
|
|
'You must be at least the administrator of the target document',
|
|
);
|
|
|
|
await page.mouse.up();
|
|
});
|
|
|
|
test("it checks can't drag when we have not the minimum role", async ({
|
|
page,
|
|
}) => {
|
|
await mockedListDocs(page, data);
|
|
await page.goto('/');
|
|
const docsGrid = page.getByTestId('docs-grid');
|
|
await expect(docsGrid).toBeVisible();
|
|
await expect(page.getByTestId('grid-loader')).toBeHidden();
|
|
|
|
const canDropAndDrag = page.getByTestId('droppable-doc-can-drop-and-drag');
|
|
|
|
const noDropAndNoDrag = page.getByTestId(
|
|
'droppable-doc-no-drop-and-no-drag',
|
|
);
|
|
|
|
await expect(canDropAndDrag).toBeVisible();
|
|
|
|
await expect(noDropAndNoDrag).toBeVisible();
|
|
|
|
const canDropAndDragBoundigBox = await canDropAndDrag.boundingBox();
|
|
|
|
const noDropAndNoDragBoundigBox = await noDropAndNoDrag.boundingBox();
|
|
|
|
if (!canDropAndDragBoundigBox || !noDropAndNoDragBoundigBox) {
|
|
throw new Error('Unable to determine the position of the elements');
|
|
}
|
|
|
|
await page.mouse.move(
|
|
noDropAndNoDragBoundigBox.x + noDropAndNoDragBoundigBox.width / 2,
|
|
noDropAndNoDragBoundigBox.y + noDropAndNoDragBoundigBox.height / 2,
|
|
);
|
|
|
|
await page.mouse.down();
|
|
|
|
await page.mouse.move(
|
|
canDropAndDragBoundigBox.x + canDropAndDragBoundigBox.width / 2,
|
|
canDropAndDragBoundigBox.y + canDropAndDragBoundigBox.height / 2,
|
|
{ steps: 10 },
|
|
);
|
|
|
|
const dragOverlay = page.getByTestId('drag-doc-overlay');
|
|
|
|
await expect(dragOverlay).toBeVisible();
|
|
await expect(dragOverlay).toHaveText(
|
|
'You must be the owner to move the document',
|
|
);
|
|
|
|
await page.mouse.up();
|
|
});
|
|
|
|
test('it moves a doc from the doc search modal', async ({
|
|
page,
|
|
browserName,
|
|
}) => {
|
|
await page.goto('/');
|
|
|
|
const [titleDoc1] = await createDoc(page, 'Draggable doc', browserName, 1);
|
|
|
|
const otherBrowserName = getOtherBrowserName(browserName);
|
|
await page.getByRole('button', { name: 'Share' }).click();
|
|
await addNewMember(page, 0, 'Administrator', otherBrowserName);
|
|
await page
|
|
.getByRole('dialog')
|
|
.getByRole('button', { name: 'close' })
|
|
.click();
|
|
|
|
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
|
|
|
const [titleDoc2] = await createDoc(page, 'Droppable doc', browserName, 1);
|
|
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
|
|
|
const docsGrid = page.getByTestId('docs-grid');
|
|
await expect(docsGrid.getByText(titleDoc1)).toBeVisible();
|
|
await expect(docsGrid.getByText(titleDoc2)).toBeVisible();
|
|
|
|
const row = await getGridRow(page, titleDoc1);
|
|
await row.getByRole('button', { name: /Open the menu of actions/ }).click();
|
|
|
|
await page.getByRole('menuitem', { name: 'Move into a doc' }).click();
|
|
|
|
await expect(
|
|
page.getByRole('dialog').getByRole('heading', { name: 'Move' }),
|
|
).toBeVisible();
|
|
|
|
const input = page.getByRole('combobox', { name: 'Quick search input' });
|
|
await input.click();
|
|
await input.fill(titleDoc2);
|
|
|
|
await expect(
|
|
page.getByRole('option').first().getByText(titleDoc2),
|
|
).toBeVisible();
|
|
|
|
// Select the first result
|
|
await page.keyboard.press('Enter');
|
|
// The CTA should get the focus
|
|
await page.keyboard.press('Tab');
|
|
// Validate the move action
|
|
await page.keyboard.press('Enter');
|
|
|
|
await expect(
|
|
page
|
|
.getByRole('dialog')
|
|
.getByText('it will lose its current access rights'),
|
|
).toBeVisible();
|
|
|
|
await page
|
|
.getByRole('dialog')
|
|
.getByRole('button', { name: 'Move', exact: true })
|
|
.first()
|
|
.click();
|
|
|
|
await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
|
|
await docsGrid
|
|
.getByRole('link', { name: `Open document ${titleDoc2}` })
|
|
.click();
|
|
|
|
await verifyDocName(page, titleDoc2);
|
|
|
|
const docTree = page.getByTestId('doc-tree');
|
|
await expect(docTree.getByText(titleDoc1)).toBeVisible();
|
|
});
|
|
|
|
test('it proposes an access request when moving a doc without sufficient permissions', async ({
|
|
page,
|
|
browserName,
|
|
}) => {
|
|
test.slow();
|
|
await page.goto('/');
|
|
|
|
const [titleDoc1] = await createDoc(page, 'Move doc', browserName, 1);
|
|
|
|
const { otherPage, cleanup } = await connectOtherUserToDoc({
|
|
docUrl: '/',
|
|
browserName,
|
|
});
|
|
|
|
// Another user creates a doc
|
|
const [titleDoc2] = await createDoc(otherPage, 'Drop doc', browserName, 1);
|
|
await writeInEditor({
|
|
page: otherPage,
|
|
text: 'Hello world',
|
|
});
|
|
// Make it public
|
|
await otherPage.getByRole('button', { name: 'Share' }).click();
|
|
await updateShareLink(otherPage, 'Public');
|
|
await otherPage
|
|
.getByRole('dialog')
|
|
.getByRole('button', { name: 'close' })
|
|
.click();
|
|
const otherPageUrl = otherPage.url();
|
|
|
|
// The first user visit the doc to have it in his grid list
|
|
await page.goto(otherPageUrl);
|
|
await expect(page.getByText('Hello world')).toBeVisible();
|
|
|
|
await page.waitForTimeout(1000);
|
|
|
|
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
|
|
|
const docsGrid = page.getByTestId('docs-grid');
|
|
await expect(docsGrid.getByText(titleDoc1)).toBeVisible();
|
|
await expect(docsGrid.getByText(titleDoc2)).toBeVisible();
|
|
|
|
const row = await getGridRow(page, titleDoc1);
|
|
await row.getByRole('button', { name: /Open the menu of actions/ }).click();
|
|
|
|
await page.getByRole('menuitem', { name: 'Move into a doc' }).click();
|
|
|
|
await expect(
|
|
page.getByRole('dialog').getByRole('heading', { name: 'Move' }),
|
|
).toBeVisible();
|
|
|
|
const input = page.getByRole('combobox', { name: 'Quick search input' });
|
|
await input.click();
|
|
await input.fill(titleDoc2);
|
|
|
|
await expect(
|
|
page.getByRole('option').first().getByText(titleDoc2),
|
|
).toBeVisible();
|
|
|
|
// Select the first result
|
|
await page.keyboard.press('Enter');
|
|
// The CTA should get the focus
|
|
await page.keyboard.press('Tab');
|
|
// Validate the move action
|
|
await page.keyboard.press('Enter');
|
|
|
|
// Request access modal should be visible
|
|
await expect(
|
|
page
|
|
.getByRole('dialog')
|
|
.getByText(
|
|
'You need edit access to the destination. Request access, then try again.',
|
|
),
|
|
).toBeVisible();
|
|
|
|
await page
|
|
.getByRole('dialog')
|
|
.getByRole('button', { name: 'Request access', exact: true })
|
|
.first()
|
|
.click();
|
|
|
|
// 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(
|
|
process.env[`USERNAME_${browserName.toUpperCase()}`] || '',
|
|
),
|
|
).toBeVisible();
|
|
|
|
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}`,
|
|
);
|
|
await container.getByTestId('doc-role-dropdown').click();
|
|
await otherPage
|
|
.getByRole('menuitemradio', { name: 'Administrator' })
|
|
.click();
|
|
await container.getByRole('button', { name: 'Approve' }).click();
|
|
|
|
await expect(otherPage.getByText('Access Requests')).toBeHidden();
|
|
await expect(otherPage.getByText('Share with 2 users')).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();
|
|
await row.getByRole('button', { name: /Open the menu of actions/ }).click();
|
|
|
|
await page.getByRole('menuitem', { name: 'Move into a doc' }).click();
|
|
|
|
await expect(
|
|
page.getByRole('dialog').getByRole('heading', { name: 'Move' }),
|
|
).toBeVisible();
|
|
|
|
await input.click();
|
|
await input.fill(titleDoc2);
|
|
|
|
await expect(
|
|
page.getByRole('option').first().getByText(titleDoc2),
|
|
).toBeVisible();
|
|
|
|
// Select the first result
|
|
await page.keyboard.press('Enter');
|
|
// The CTA should get the focus
|
|
await page.keyboard.press('Tab');
|
|
// Validate the move action
|
|
await page.keyboard.press('Enter');
|
|
|
|
await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
|
|
await docsGrid
|
|
.getByRole('link', { name: `Open document ${titleDoc2}` })
|
|
.click();
|
|
|
|
await verifyDocName(page, titleDoc2);
|
|
|
|
const docTree = page.getByTestId('doc-tree');
|
|
await expect(docTree.getByText(titleDoc1)).toBeVisible({
|
|
timeout: 15000,
|
|
});
|
|
|
|
await cleanup();
|
|
});
|
|
});
|
|
|
|
test.describe('Doc grid dnd mobile', () => {
|
|
test.use({ viewport: { width: 500, height: 1200 } });
|
|
|
|
test('DND is deactivated on mobile', async ({ page, browserName }) => {
|
|
await page.goto('/');
|
|
|
|
const docsGrid = page.getByTestId('docs-grid');
|
|
await expect(page.getByTestId('docs-grid')).toBeVisible();
|
|
await expect(page.getByTestId('grid-loader')).toBeHidden();
|
|
|
|
await expect(docsGrid.getByRole('listitem').first()).toBeVisible();
|
|
await expect(docsGrid.locator('.--docs--grid-droppable')).toHaveCount(0);
|
|
|
|
await createDoc(page, 'Draggable doc mobile', browserName, 1, true);
|
|
|
|
await createRootSubPage(
|
|
page,
|
|
browserName,
|
|
'Draggable doc mobile child',
|
|
true,
|
|
);
|
|
|
|
await toggleHeaderMenu(page);
|
|
|
|
await expect(page.locator('.--docs-sub-page-item').first()).toHaveAttribute(
|
|
'draggable',
|
|
'false',
|
|
);
|
|
});
|
|
});
|
|
|
|
const data = [
|
|
{
|
|
id: 'can-drop-and-drag',
|
|
abilities: {
|
|
accesses_manage: true,
|
|
accesses_view: true,
|
|
ai_transform: true,
|
|
ai_translate: true,
|
|
attachment_upload: true,
|
|
children_list: true,
|
|
children_create: true,
|
|
collaboration_auth: true,
|
|
descendants: true,
|
|
destroy: true,
|
|
favorite: true,
|
|
link_configuration: true,
|
|
invite_owner: true,
|
|
move: true,
|
|
partial_update: true,
|
|
restore: true,
|
|
retrieve: true,
|
|
media_auth: true,
|
|
link_select_options: {
|
|
restricted: ['reader', 'editor'],
|
|
authenticated: ['reader', 'editor'],
|
|
public: ['reader', 'editor'],
|
|
},
|
|
tree: true,
|
|
update: true,
|
|
versions_destroy: true,
|
|
versions_list: true,
|
|
versions_retrieve: true,
|
|
},
|
|
created_at: '2025-03-14T14:45:22.527221Z',
|
|
creator: 'bc6895e0-8f6d-4b00-827d-c143aa6b2ecb',
|
|
depth: 1,
|
|
excerpt: null,
|
|
is_favorite: false,
|
|
link_role: 'reader',
|
|
link_reach: 'restricted',
|
|
nb_accesses_ancestors: 1,
|
|
nb_accesses_direct: 1,
|
|
numchild: 5,
|
|
path: '000000o',
|
|
title: 'Can drop and drag',
|
|
updated_at: '2025-03-14T14:45:27.699542Z',
|
|
user_roles: ['owner'],
|
|
user_role: 'owner',
|
|
},
|
|
{
|
|
id: 'can-only-drop',
|
|
title: 'Can only drop',
|
|
abilities: {
|
|
accesses_manage: true,
|
|
accesses_view: true,
|
|
ai_transform: true,
|
|
ai_translate: true,
|
|
attachment_upload: true,
|
|
children_list: true,
|
|
children_create: true,
|
|
collaboration_auth: true,
|
|
descendants: true,
|
|
destroy: true,
|
|
favorite: true,
|
|
link_configuration: true,
|
|
invite_owner: true,
|
|
move: true,
|
|
partial_update: true,
|
|
restore: true,
|
|
retrieve: true,
|
|
media_auth: true,
|
|
link_select_options: {
|
|
restricted: ['reader', 'editor'],
|
|
authenticated: ['reader', 'editor'],
|
|
public: ['reader', 'editor'],
|
|
},
|
|
tree: true,
|
|
update: true,
|
|
versions_destroy: true,
|
|
versions_list: true,
|
|
versions_retrieve: true,
|
|
},
|
|
created_at: '2025-03-14T14:45:22.527221Z',
|
|
creator: 'bc6895e0-8f6d-4b00-827d-c143aa6b2ecb',
|
|
depth: 1,
|
|
excerpt: null,
|
|
is_favorite: false,
|
|
link_role: 'reader',
|
|
link_reach: 'restricted',
|
|
nb_accesses_ancestors: 1,
|
|
nb_accesses_direct: 1,
|
|
numchild: 5,
|
|
path: '000000o',
|
|
|
|
updated_at: '2025-03-14T14:45:27.699542Z',
|
|
user_roles: ['editor'],
|
|
user_role: 'editor',
|
|
},
|
|
{
|
|
id: 'no-drop-and-no-drag',
|
|
abilities: {
|
|
accesses_manage: false,
|
|
accesses_view: true,
|
|
ai_transform: false,
|
|
ai_translate: false,
|
|
attachment_upload: false,
|
|
children_list: true,
|
|
children_create: false,
|
|
collaboration_auth: true,
|
|
descendants: true,
|
|
destroy: false,
|
|
favorite: true,
|
|
link_configuration: false,
|
|
invite_owner: false,
|
|
move: false,
|
|
partial_update: false,
|
|
restore: false,
|
|
retrieve: true,
|
|
media_auth: true,
|
|
link_select_options: {
|
|
restricted: ['reader', 'editor'],
|
|
authenticated: ['reader', 'editor'],
|
|
public: ['reader', 'editor'],
|
|
},
|
|
tree: true,
|
|
update: false,
|
|
versions_destroy: false,
|
|
versions_list: true,
|
|
versions_retrieve: true,
|
|
},
|
|
created_at: '2025-03-14T14:44:16.032773Z',
|
|
creator: '9264f420-f018-4bd6-96ae-4788f41af56d',
|
|
depth: 1,
|
|
excerpt: null,
|
|
is_favorite: false,
|
|
link_role: 'reader',
|
|
link_reach: 'restricted',
|
|
nb_accesses_ancestors: 14,
|
|
nb_accesses_direct: 14,
|
|
numchild: 0,
|
|
path: '000000l',
|
|
title: 'No drop and no drag',
|
|
updated_at: '2025-03-14T14:44:16.032774Z',
|
|
user_roles: ['reader'],
|
|
user_role: 'reader',
|
|
},
|
|
];
|