✅(e2e) fix flakiness
Some flakiness appeared in the e2e tests. It started to impact many pull requests. Time to fix them.
This commit is contained in:
@@ -26,7 +26,7 @@ export const createDoc = async (
|
||||
page: Page,
|
||||
docName: string,
|
||||
browserName: string,
|
||||
length: number,
|
||||
length: number = 1,
|
||||
) => {
|
||||
const randomDocs = randomName(docName, browserName, length);
|
||||
|
||||
@@ -40,7 +40,8 @@ export const createDoc = async (
|
||||
})
|
||||
.click();
|
||||
|
||||
const input = page.getByRole('textbox', { name: 'doc title input' });
|
||||
const input = page.getByLabel('doc title input');
|
||||
await expect(input).toHaveText('');
|
||||
await input.click();
|
||||
await input.fill(randomDocs[i]);
|
||||
await input.blur();
|
||||
@@ -91,6 +92,22 @@ export const addNewMember = async (
|
||||
return users[index].email;
|
||||
};
|
||||
|
||||
export const getGridRow = async (page: Page, title: string) => {
|
||||
const docsGrid = page.getByRole('grid');
|
||||
await expect(docsGrid).toBeVisible();
|
||||
await expect(page.getByTestId('grid-loader')).toBeHidden();
|
||||
|
||||
const rows = docsGrid.getByRole('row');
|
||||
|
||||
const row = rows.filter({
|
||||
hasText: title,
|
||||
});
|
||||
|
||||
await expect(row).toBeVisible();
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
interface GoToGridDocOptions {
|
||||
nthRow?: number;
|
||||
title?: string;
|
||||
@@ -104,7 +121,7 @@ export const goToGridDoc = async (
|
||||
|
||||
const docsGrid = page.getByTestId('docs-grid');
|
||||
await expect(docsGrid).toBeVisible();
|
||||
await expect(docsGrid.getByTestId('grid-loader')).toBeHidden();
|
||||
await expect(page.getByTestId('grid-loader')).toBeHidden();
|
||||
|
||||
const rows = docsGrid.getByRole('row');
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ test.describe('Doc Editor', () => {
|
||||
browserName,
|
||||
}) => {
|
||||
// Check the first doc
|
||||
const [doc] = await createDoc(page, 'doc-saves-change', browserName, 1);
|
||||
const [doc] = await createDoc(page, 'doc-saves-change', browserName);
|
||||
await verifyDocName(page, doc);
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
@@ -228,9 +228,11 @@ test.describe('Doc Editor', () => {
|
||||
await editor.fill('Hello World Doc persisted 1');
|
||||
await expect(editor.getByText('Hello World Doc persisted 1')).toBeVisible();
|
||||
|
||||
const secondDoc = await goToGridDoc(page, {
|
||||
nthRow: 2,
|
||||
});
|
||||
const [secondDoc] = await createDoc(
|
||||
page,
|
||||
'doc-saves-change-other',
|
||||
browserName,
|
||||
);
|
||||
|
||||
await verifyDocName(page, secondDoc);
|
||||
|
||||
@@ -238,6 +240,7 @@ test.describe('Doc Editor', () => {
|
||||
title: doc,
|
||||
});
|
||||
|
||||
await verifyDocName(page, doc);
|
||||
await expect(editor.getByText('Hello World Doc persisted 1')).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -246,8 +249,7 @@ test.describe('Doc Editor', () => {
|
||||
test.skip(browserName === 'webkit', 'This test is very flaky with webkit');
|
||||
|
||||
// Check the first doc
|
||||
const doc = await goToGridDoc(page);
|
||||
|
||||
const [doc] = await createDoc(page, 'doc-quit-1', browserName, 1);
|
||||
await verifyDocName(page, doc);
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { createDoc, verifyDocName } from './common';
|
||||
|
||||
type SmallDoc = {
|
||||
id: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
test.describe('Document favorite', () => {
|
||||
test('it check the favorite workflow', async ({ page, browserName }) => {
|
||||
const id = Math.random().toString(7);
|
||||
await page.goto('/');
|
||||
|
||||
// Create document
|
||||
const createdDoc = await createDoc(page, `Doc ${id}`, browserName, 1);
|
||||
await verifyDocName(page, createdDoc[0]);
|
||||
|
||||
// Reload page
|
||||
await page.reload();
|
||||
await page.goto('/');
|
||||
|
||||
// Get all documents
|
||||
let docs: SmallDoc[] = [];
|
||||
const response = await page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().endsWith('documents/?page=1') &&
|
||||
response.status() === 200,
|
||||
);
|
||||
const result = await response.json();
|
||||
docs = result.results as SmallDoc[];
|
||||
const docsGrid = page.getByTestId('docs-grid');
|
||||
await docsGrid.getByRole('heading', { name: 'All docs' }).click();
|
||||
await expect(docsGrid.getByText(`Doc ${id}`)).toBeVisible();
|
||||
const doc = docs.find((doc) => doc.title === createdDoc[0]) as SmallDoc;
|
||||
|
||||
// Check document
|
||||
expect(doc).not.toBeUndefined();
|
||||
expect(doc?.title).toBe(createdDoc[0]);
|
||||
|
||||
// Open document actions
|
||||
const button = docsGrid.getByTestId(`docs-grid-actions-button-${doc.id}`);
|
||||
await expect(button).toBeVisible();
|
||||
await button.click();
|
||||
|
||||
// Pin document
|
||||
const pinButton = page.getByTestId(`docs-grid-actions-pin-${docs[0].id}`);
|
||||
await expect(pinButton).toBeVisible();
|
||||
await pinButton.click();
|
||||
|
||||
// Check response
|
||||
const responsePin = await page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes(`documents/${doc.id}/favorite/`) &&
|
||||
response.status() === 201,
|
||||
);
|
||||
expect(responsePin.ok()).toBeTruthy();
|
||||
|
||||
// Check left panel favorites
|
||||
const leftPanelFavorites = page.getByTestId('left-panel-favorites');
|
||||
await expect(leftPanelFavorites).toBeVisible();
|
||||
await expect(leftPanelFavorites.getByText(`Doc ${id}`)).toBeVisible();
|
||||
|
||||
//
|
||||
await button.click();
|
||||
const unpinButton = page.getByTestId(
|
||||
`docs-grid-actions-unpin-${docs[0].id}`,
|
||||
);
|
||||
await expect(unpinButton).toBeVisible();
|
||||
await unpinButton.click();
|
||||
|
||||
// Check left panel favorites
|
||||
await expect(leftPanelFavorites.getByText(`Doc ${id}`)).toBeHidden();
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,7 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { createDoc, getGridRow } from './common';
|
||||
|
||||
type SmallDoc = {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -92,6 +94,31 @@ test.describe('Documents Grid mobile', () => {
|
||||
});
|
||||
|
||||
test.describe('Document grid item options', () => {
|
||||
test('it pins a document', async ({ page, browserName }) => {
|
||||
const [docTitle] = await createDoc(page, `Favorite doc`, browserName);
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const row = await getGridRow(page, docTitle);
|
||||
|
||||
// Pin
|
||||
await row.getByText(`more_horiz`).click();
|
||||
await page.getByText('push_pin').click();
|
||||
|
||||
// Check is pinned
|
||||
await expect(row.getByLabel('Pin document icon')).toBeVisible();
|
||||
const leftPanelFavorites = page.getByTestId('left-panel-favorites');
|
||||
await expect(leftPanelFavorites.getByText(docTitle)).toBeVisible();
|
||||
|
||||
// Unpin
|
||||
await row.getByText(`more_horiz`).click();
|
||||
await page.getByText('Unpin').click();
|
||||
|
||||
// Check is unpinned
|
||||
await expect(row.getByLabel('Pin document icon')).toBeHidden();
|
||||
await expect(leftPanelFavorites.getByText(docTitle)).toBeHidden();
|
||||
});
|
||||
|
||||
test('it deletes the document', async ({ page }) => {
|
||||
let docs: SmallDoc[] = [];
|
||||
const response = await page.waitForResponse(
|
||||
|
||||
@@ -26,7 +26,7 @@ test.describe('Document create member', () => {
|
||||
const response = await responsePromise;
|
||||
const users = (await response.json()).results as {
|
||||
email: string;
|
||||
full_name: string;
|
||||
full_name?: string | null;
|
||||
}[];
|
||||
|
||||
const list = page.getByTestId('doc-share-add-member-list');
|
||||
@@ -40,7 +40,9 @@ test.describe('Document create member', () => {
|
||||
await expect(
|
||||
list.getByTestId(`doc-share-add-member-${users[0].email}`),
|
||||
).toBeVisible();
|
||||
await expect(list.getByText(`${users[0].full_name}`)).toBeVisible();
|
||||
await expect(
|
||||
list.getByText(`${users[0].full_name || users[0].email}`),
|
||||
).toBeVisible();
|
||||
|
||||
// Select user 2 and verify tag
|
||||
await inputSearch.fill('user');
|
||||
@@ -51,7 +53,9 @@ test.describe('Document create member', () => {
|
||||
await expect(
|
||||
list.getByTestId(`doc-share-add-member-${users[1].email}`),
|
||||
).toBeVisible();
|
||||
await expect(list.getByText(`${users[1].full_name}`)).toBeVisible();
|
||||
await expect(
|
||||
list.getByText(`${users[1].full_name || users[1].email}`),
|
||||
).toBeVisible();
|
||||
|
||||
// Select email and verify tag
|
||||
const email = randomName('test@test.fr', browserName, 1)[0];
|
||||
@@ -81,7 +85,9 @@ test.describe('Document create member', () => {
|
||||
// Check user added
|
||||
await expect(page.getByText('Share with 3 users')).toBeVisible();
|
||||
await expect(
|
||||
quickSearchContent.getByText(users[0].full_name).first(),
|
||||
quickSearchContent
|
||||
.getByText(users[0].full_name || users[0].email)
|
||||
.first(),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
quickSearchContent.getByText(users[0].email).first(),
|
||||
@@ -90,7 +96,9 @@ test.describe('Document create member', () => {
|
||||
quickSearchContent.getByText(users[1].email).first(),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
quickSearchContent.getByText(users[1].full_name).first(),
|
||||
quickSearchContent
|
||||
.getByText(users[1].full_name || users[1].email)
|
||||
.first(),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { createDoc, verifyDocName } from './common';
|
||||
|
||||
type SmallDoc = {
|
||||
id: string;
|
||||
title: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test.describe('Document search', () => {
|
||||
test('it checks all elements are visible', async ({ page }) => {
|
||||
test('it searches documents', async ({ page, browserName }) => {
|
||||
const [doc1Title] = await createDoc(
|
||||
page,
|
||||
'My doc search super',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
await verifyDocName(page, doc1Title);
|
||||
await page.goto('/');
|
||||
|
||||
const [doc2Title] = await createDoc(
|
||||
page,
|
||||
'My doc search doc',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
await verifyDocName(page, doc2Title);
|
||||
await page.goto('/');
|
||||
await page.getByRole('button', { name: 'search' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('img', { name: 'No active search' }),
|
||||
).toBeVisible();
|
||||
@@ -24,91 +35,32 @@ test.describe('Document search', () => {
|
||||
page.getByLabel('Search modal').getByText('search'),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByPlaceholder('Type the name of a document'),
|
||||
).toBeVisible();
|
||||
});
|
||||
const inputSearch = page.getByPlaceholder('Type the name of a document');
|
||||
|
||||
test('it checks search for a document', async ({ page, browserName }) => {
|
||||
const id = Math.random().toString(36).substring(7);
|
||||
await inputSearch.click();
|
||||
await inputSearch.fill('My doc search');
|
||||
await inputSearch.press('ArrowDown');
|
||||
|
||||
const doc1 = await createDoc(page, `My super ${id} doc`, browserName, 1);
|
||||
await verifyDocName(page, doc1[0]);
|
||||
await page.goto('/');
|
||||
const doc2 = await createDoc(
|
||||
page,
|
||||
`My super ${id} very doc`,
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
await verifyDocName(page, doc2[0]);
|
||||
await page.goto('/');
|
||||
await page.getByRole('button', { name: 'search' }).click();
|
||||
await page.getByPlaceholder('Type the name of a document').click();
|
||||
await page
|
||||
.getByPlaceholder('Type the name of a document')
|
||||
.fill(`My super ${id}`);
|
||||
|
||||
let responsePromisePage = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes(`/documents/?page=1&title=My+super+${id}`) &&
|
||||
response.status() === 200,
|
||||
);
|
||||
let response = await responsePromisePage;
|
||||
let result = (await response.json()) as { results: SmallDoc[] };
|
||||
let docs = result.results;
|
||||
expect(docs.length).toEqual(2);
|
||||
|
||||
await Promise.all(
|
||||
docs.map(async (doc: SmallDoc) => {
|
||||
await expect(
|
||||
page.getByTestId(`doc-search-item-${doc.id}`),
|
||||
).toBeVisible();
|
||||
const updatedAt = DateTime.fromISO(doc.updated_at ?? DateTime.now())
|
||||
.setLocale('en')
|
||||
.toRelative();
|
||||
await expect(
|
||||
page.getByTestId(`doc-search-item-${doc.id}`).getByText(updatedAt!),
|
||||
).toBeVisible();
|
||||
}),
|
||||
);
|
||||
|
||||
const firstDoc = docs[0];
|
||||
const listSearch = page.getByRole('listbox').getByRole('group');
|
||||
const rowdoc = listSearch.getByRole('option').first();
|
||||
await expect(rowdoc.getByText('keyboard_return')).toBeVisible();
|
||||
await expect(rowdoc.getByText(/seconds? ago/)).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page
|
||||
.getByTestId(`doc-search-item-${firstDoc.id}`)
|
||||
.getByText('keyboard_return'),
|
||||
listSearch.getByRole('option').getByText(doc1Title),
|
||||
).toBeVisible();
|
||||
|
||||
await page
|
||||
.getByPlaceholder('Type the name of a document')
|
||||
.press('ArrowDown');
|
||||
|
||||
const secondDoc = docs[1];
|
||||
await expect(
|
||||
page
|
||||
.getByTestId(`doc-search-item-${secondDoc.id}`)
|
||||
.getByText('keyboard_return'),
|
||||
listSearch.getByRole('option').getByText(doc2Title),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByPlaceholder('Type the name of a document').click();
|
||||
await page
|
||||
.getByPlaceholder('Type the name of a document')
|
||||
.fill(`My super ${id} doc`);
|
||||
await inputSearch.fill('My doc search super');
|
||||
|
||||
responsePromisePage = page.waitForResponse(
|
||||
(response) =>
|
||||
response
|
||||
.url()
|
||||
.includes(`/documents/?page=1&title=My+super+${id}+doc`) &&
|
||||
response.status() === 200,
|
||||
);
|
||||
await expect(
|
||||
listSearch.getByRole('option').getByText(doc1Title),
|
||||
).toBeVisible();
|
||||
|
||||
response = await responsePromisePage;
|
||||
result = (await response.json()) as { results: SmallDoc[] };
|
||||
docs = result.results;
|
||||
|
||||
expect(docs.length).toEqual(1);
|
||||
await expect(
|
||||
listSearch.getByRole('option').getByText(doc2Title),
|
||||
).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -171,6 +171,9 @@ test.describe('Doc Visibility: Restricted', () => {
|
||||
|
||||
await page.goto(urlDoc);
|
||||
|
||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await verifyDocName(page, docTitle);
|
||||
await expect(page.getByLabel('Share button')).toBeVisible();
|
||||
});
|
||||
@@ -209,6 +212,7 @@ test.describe('Doc Visibility: Public', () => {
|
||||
page.getByText('The document visibility has been updated.'),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(page.getByLabel('Visibility mode')).toBeVisible();
|
||||
await page.getByLabel('Visibility mode').click();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
|
||||
@@ -13,11 +13,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.49.1",
|
||||
"@types/luxon": "3.4.2",
|
||||
"@types/node": "*",
|
||||
"@types/pdf-parse": "1.1.4",
|
||||
"eslint-config-impress": "*",
|
||||
"luxon": "3.5.0",
|
||||
"typescript": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -30,12 +30,9 @@ import { DocShareModalFooter } from './DocShareModalFooter';
|
||||
import { DocShareModalInviteUserRow } from './DocShareModalInviteUserByEmail';
|
||||
|
||||
const ShareModalStyle = createGlobalStyle`
|
||||
|
||||
.c__modal__title {
|
||||
padding-bottom: 0 !important;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -70,6 +70,7 @@ export const DocsGrid = ({
|
||||
>
|
||||
<DocsGridLoader isLoading={isRefetching || loading} />
|
||||
<Card
|
||||
role="grid"
|
||||
data-testid="docs-grid"
|
||||
$height="100%"
|
||||
$width="100%"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import { Box, Text } from '@/components';
|
||||
@@ -30,6 +31,7 @@ export const SimpleDocItem = ({
|
||||
isPinned = false,
|
||||
showAccesses = false,
|
||||
}: SimpleDocItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { spacingsTokens } = useCunninghamTheme();
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const spacings = spacingsTokens();
|
||||
@@ -44,7 +46,11 @@ export const SimpleDocItem = ({
|
||||
filter: drop-shadow(0px 2px 2px rgba(0, 0, 0, 0.05));
|
||||
`}
|
||||
>
|
||||
{isPinned ? <PinnedDocumentIcon /> : <SimpleFileIcon />}
|
||||
{isPinned ? (
|
||||
<PinnedDocumentIcon aria-label={t('Pin document icon')} />
|
||||
) : (
|
||||
<SimpleFileIcon aria-label={t('Simple document icon')} />
|
||||
)}
|
||||
</Box>
|
||||
<Box $justify="center">
|
||||
<Text
|
||||
|
||||
Reference in New Issue
Block a user