♻️(frontend) list members from side modal

We refactorize the members grid to display
it inside the share side modal.
It is not a member grid anymore but a member list
with infinite scroll. We can directly update the
role or delete a member from the each row of the
list.
This commit is contained in:
Anthony LC
2024-07-12 15:00:33 +02:00
committed by Anthony LC
parent e5de5a4345
commit 69f2641159
36 changed files with 520 additions and 1671 deletions

View File

@@ -71,7 +71,7 @@ export const createDoc = async (
export const addNewMember = async (
page: Page,
index: number,
role: 'Admin' | 'Owner' | 'Member',
role: 'Administrator' | 'Owner' | 'Member' | 'Editor' | 'Reader',
fillText: string = 'user',
) => {
const responsePromiseSearchUser = page.waitForResponse(
@@ -80,8 +80,6 @@ export const addNewMember = async (
response.status() === 200,
);
await page.getByRole('button', { name: 'Share' }).click();
const inputSearch = page.getByLabel(/Find a member to add to the document/);
// Select a new user

View File

@@ -1,192 +0,0 @@
import { expect, test } from '@playwright/test';
import { addNewMember, createDoc } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test.describe('Members Delete', () => {
test('it cannot delete himself when it is the last owner', async ({
page,
browserName,
}) => {
await createDoc(page, 'member-delete-1', browserName, 1);
await page.getByLabel('Open the document options').click();
await page.getByRole('button', { name: 'Manage members' }).click();
const table = page.getByLabel('List members card').getByRole('table');
const cells = table.getByRole('row').nth(1).getByRole('cell');
await expect(cells.nth(0)).toHaveText(
new RegExp(`user@${browserName}.e2e`, 'i'),
);
await cells.nth(2).getByLabel('Member options').click();
await page.getByLabel('Open the modal to delete this member').click();
await expect(
page.getByText(
'You are the last owner, you cannot be removed from your document.',
),
).toBeVisible();
await expect(page.getByRole('button', { name: 'Validate' })).toBeDisabled();
});
test('it deletes himself when it is not the last owner', async ({
page,
browserName,
}) => {
await createDoc(page, 'member-delete-2', browserName, 1);
await addNewMember(page, 0, 'Owner');
await page.getByLabel('Open the document options').click();
await page.getByRole('button', { name: 'Manage members' }).click();
const table = page.getByLabel('List members card').getByRole('table');
// find row where regexp match the name
const cells = table
.getByRole('row')
.filter({ hasText: new RegExp(`user@${browserName}.e2e`, 'i') })
.getByRole('cell');
await cells.nth(2).getByLabel('Member options').click();
await page.getByLabel('Open the modal to delete this member').click();
await page.getByRole('button', { name: 'Validate' }).click();
await expect(
page.getByText(`The member has been removed from the document`),
).toBeVisible();
await expect(
page.getByRole('button', { name: `Create a new document` }),
).toBeVisible();
});
test('it cannot delete owner member', async ({ page, browserName }) => {
await createDoc(page, 'member-delete-3', browserName, 1);
const username = await addNewMember(page, 0, 'Owner');
await page.getByLabel('Open the document options').click();
await page.getByRole('button', { name: 'Manage members' }).click();
const table = page.getByLabel('List members card').getByRole('table');
// find row where regexp match the name
const cells = table
.getByRole('row')
.filter({ hasText: username })
.getByRole('cell');
await cells.getByLabel('Member options').click();
await page.getByLabel('Open the modal to delete this member').click();
await expect(
page.getByText(`You cannot remove other owner.`),
).toBeVisible();
await expect(page.getByRole('button', { name: 'Validate' })).toBeDisabled();
});
test('it deletes admin member', async ({ page, browserName }) => {
await createDoc(page, 'member-delete-4', browserName, 1);
const username = await addNewMember(page, 0, 'Admin');
await page.getByLabel('Open the document options').click();
await page.getByRole('button', { name: 'Manage members' }).click();
const table = page.getByLabel('List members card').getByRole('table');
// find row where regexp match the name
const cells = table
.getByRole('row')
.filter({ hasText: username })
.getByRole('cell');
await cells.getByLabel('Member options').click();
await page.getByLabel('Open the modal to delete this member').click();
await page.getByRole('button', { name: 'Validate' }).click();
await expect(
page.getByText(`The member has been removed from the document`),
).toBeVisible();
await expect(table.getByText(username)).toBeHidden();
});
test('it cannot delete owner member when admin', async ({
page,
browserName,
}) => {
await createDoc(page, 'member-delete-5', browserName, 1);
const username = await addNewMember(page, 0, 'Owner');
await page.getByLabel('Open the document options').click();
await page.getByRole('button', { name: 'Manage members' }).click();
const table = page.getByLabel('List members card').getByRole('table');
// find row where regexp match the name
const myCells = table
.getByRole('row')
.filter({ hasText: new RegExp(`user@${browserName}.e2e`, 'i') })
.getByRole('cell');
await myCells.getByLabel('Member options').click();
// Change role to Admin
await page.getByText('Update role').click();
const radioGroup = page.getByLabel('Radio buttons to update the roles');
await radioGroup.getByRole('radio', { name: 'Administrator' }).click();
await page.getByRole('button', { name: 'Validate' }).click();
const cells = table
.getByRole('row')
.filter({ hasText: username })
.getByRole('cell');
await expect(cells.getByLabel('Member options')).toBeHidden();
});
test('it deletes admin member when admin', async ({ page, browserName }) => {
await createDoc(page, 'member-delete-6', browserName, 1);
// To not be the only owner
await addNewMember(page, 0, 'Owner');
const username = await addNewMember(page, 1, 'Admin');
await page.getByLabel('Open the document options').click();
await page.getByRole('button', { name: 'Manage members' }).click();
const table = page.getByLabel('List members card').getByRole('table');
// find row where regexp match the name
const myCells = table
.getByRole('row')
.filter({ hasText: new RegExp(`user@${browserName}.e2e`, 'i') })
.getByRole('cell');
await myCells.getByLabel('Member options').click();
// Change role to Admin
await page.getByText('Update role').click();
const radioGroup = page.getByLabel('Radio buttons to update the roles');
await radioGroup.getByRole('radio', { name: 'Administrator' }).click();
await page.getByRole('button', { name: 'Validate' }).click();
await expect(page.getByText(`The role has been updated`)).toBeVisible();
await expect(page.getByText(`The role has been updated`)).toBeHidden({
timeout: 5000,
});
const cells = table
.getByRole('row')
.filter({ hasText: new RegExp(username, 'i') })
.getByRole('cell');
await cells.nth(2).getByLabel('Member options').click();
await page.getByLabel('Open the modal to delete this member').click();
await page.getByRole('button', { name: 'Validate' }).click();
await expect(
page.getByText(`The member has been removed from the document`),
).toBeVisible();
await expect(table.getByText(username)).toBeHidden();
});
});

View File

@@ -1,112 +0,0 @@
import { expect, test } from '@playwright/test';
import { createDoc, goToGridDoc } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test.describe('Document grid members', () => {
test('it display the grid', async ({ page, browserName }) => {
await createDoc(page, 'grid-display', browserName, 1);
await page.getByLabel('Open the document options').click();
await page.getByRole('button', { name: 'Manage members' }).click();
await expect(page.getByText('Members of the document')).toBeVisible();
const table = page.getByLabel('List members card').getByRole('table');
const thead = table.locator('thead');
await expect(thead.getByText(/Emails/i)).toBeVisible();
await expect(thead.getByText(/Roles/i)).toBeVisible();
const cells = table.getByRole('row').nth(1).getByRole('cell');
await expect(cells.nth(0)).toHaveText(`user@${browserName}.e2e`);
await expect(cells.nth(1)).toHaveText(/Owner/i);
await expect(cells.nth(2)).toHaveAccessibleName(
'Open the member options modal',
);
});
test('it display the grid with many members', async ({ page }) => {
await page.route('**/documents/*/', async (route) => {
const request = route.request();
if (
request.method().includes('GET') &&
!request.url().includes('page=')
) {
await route.fulfill({
json: {
id: 'b0df4343-c8bd-4c20-9ff6-fbf94fc94egg',
content: '',
title: 'Mocked document',
accesses: [],
abilities: {
destroy: true,
manage_accesses: true,
partial_update: true,
},
is_public: false,
},
});
} else {
await route.continue();
}
});
await page.route(
'**/documents/b0df4343-c8bd-4c20-9ff6-fbf94fc94egg/accesses/?page=*',
async (route) => {
const request = route.request();
const url = new URL(request.url());
const pageId = url.searchParams.get('page');
const accesses = {
count: 100,
next: null,
previous: null,
results: Array.from({ length: 20 }, (_, i) => ({
id: `2ff1ec07-86c1-4534-a643-f41824a6c53a-${pageId}-${i}`,
user: {
id: `fc092149-cafa-4ffa-a29d-e4b18af751-${pageId}-${i}`,
email: `impress@impress.world-page-${pageId}-${i}`,
},
team: '',
role: 'owner',
abilities: {
destroy: false,
partial_update: true,
},
})),
};
if (request.method().includes('GET')) {
await route.fulfill({
json: accesses,
});
} else {
await route.continue();
}
},
);
await goToGridDoc(page);
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
await page.getByLabel('Open the document options').click();
await page.getByRole('button', { name: 'Manage members' }).click();
await expect(
page.getByText('impress@impress.world-page-1-19'),
).toBeVisible();
await page.getByLabel('Go to page 4').click();
await expect(
page.getByText('impress@impress.world-page-1-19'),
).toBeHidden();
await expect(
page.getByText('impress@impress.world-page-4-19'),
).toBeVisible();
});
});

View File

@@ -0,0 +1,164 @@
import { expect, test } from '@playwright/test';
import { waitForElementCount } from '../helpers';
import { addNewMember, createDoc, goToGridDoc } from './common';
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test.describe('Document list members', () => {
test('it checks a big list of members', async ({ page }) => {
await page.route(
/.*\/documents\/.*\/accesses\/\?page=.*/,
async (route) => {
const request = route.request();
const url = new URL(request.url());
const pageId = url.searchParams.get('page');
const accesses = {
count: 100,
next: 'http://anything/?page=2',
previous: null,
results: Array.from({ length: 20 }, (_, i) => ({
id: `2ff1ec07-86c1-4534-a643-f41824a6c53a-${pageId}-${i}`,
user: {
id: `fc092149-cafa-4ffa-a29d-e4b18af751-${pageId}-${i}`,
email: `impress@impress.world-page-${pageId}-${i}`,
},
team: '',
role: 'editor',
abilities: {
destroy: false,
partial_update: true,
set_role_to: [],
},
})),
};
if (request.method().includes('GET')) {
await route.fulfill({
json: accesses,
});
} else {
await route.continue();
}
},
);
await goToGridDoc(page);
await page.getByRole('button', { name: 'Share' }).click();
const list = page.getByLabel('List members card').locator('ul');
await expect(list.locator('li')).toHaveCount(20);
await list.getByText(`impress@impress.world-page-${1}-18`).hover();
await page.mouse.wheel(0, 10);
await waitForElementCount(list.locator('li'), 21, 10000);
expect(await list.locator('li').count()).toBeGreaterThan(20);
await expect(
list.getByText(`impress@impress.world-page-1-16`),
).toBeVisible();
await expect(
list.getByText(`impress@impress.world-page-2-15`),
).toBeVisible();
});
test('it checks the role rules', async ({ page, browserName }) => {
const [docTitle] = await createDoc(page, 'Doc role rules', browserName, 1);
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
const list = page.getByLabel('List members card').locator('ul');
await expect(list.getByText(`user@${browserName}.e2e`)).toBeVisible();
const soleOwner = list.getByText(
`You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.`,
);
await expect(soleOwner).toBeVisible();
const username = await addNewMember(page, 0, 'Owner');
await expect(list.getByText(username)).toBeVisible();
await expect(soleOwner).toBeHidden();
const otherOwner = list.getByText(
`You cannot update the role or remove other owner.`,
);
await expect(otherOwner).toBeVisible();
const SelectRoleCurrentUser = list
.locator('li')
.filter({
hasText: `user@${browserName}.e2e`,
})
.getByRole('combobox', { name: 'Role' });
await SelectRoleCurrentUser.click();
await page.getByRole('option', { name: 'Administrator' }).click();
await expect(page.getByText('The role has been updated')).toBeVisible();
// Admin still have the right to share
await expect(page.locator('h3').getByText('Share')).toBeVisible();
await SelectRoleCurrentUser.click();
await page.getByRole('option', { name: 'Reader' }).click();
await expect(page.getByText('The role has been updated')).toBeVisible();
// Reader does not have the right to share
await expect(page.locator('h3').getByText('Share')).toBeHidden();
});
test('it checks the delete members', async ({ page, browserName }) => {
const [docTitle] = await createDoc(page, 'Doc role rules', browserName, 1);
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
const list = page.getByLabel('List members card').locator('ul');
const nameMyself = `user@${browserName}.e2e`;
await expect(list.getByText(nameMyself)).toBeVisible();
const userOwner = await addNewMember(page, 0, 'Owner');
await expect(list.getByText(userOwner)).toBeVisible();
const userReader = await addNewMember(page, 0, 'Reader');
await expect(list.getByText(userReader)).toBeVisible();
await list
.locator('li')
.filter({
hasText: userReader,
})
.getByText('delete')
.click();
await expect(list.getByText(userReader)).toBeHidden();
await list
.locator('li')
.filter({
hasText: nameMyself,
})
.getByText('delete')
.click();
await expect(list.getByText(nameMyself)).toBeHidden();
await expect(
page.getByText('The member has been removed from the document').first(),
).toBeVisible();
await expect(page.getByText('Share')).toBeHidden();
});
});