🐛(frontend) improve e2e tests avoiding race condition from mocks

Reorder mock setup with page.route so that it takes place before nav.
This commit is contained in:
Laurent Bossavit
2025-01-16 12:30:35 +01:00
committed by Laurent Bossavit
parent 8a2b0d0a76
commit e7af1fd591
8 changed files with 168 additions and 87 deletions

View File

@@ -46,6 +46,7 @@ and this project adheres to
### Fixed
- 🐛(frontend) improve e2e tests avoiding race condition from mocks #641
- 🐛(backend) fix flaky test with search contact #605
- 🐛(backend) fix flaky test with team access #646
- 🧑‍💻(dimail) remove 'NoneType: None' log in debug mode

View File

@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.

View File

@@ -60,12 +60,6 @@ test.describe('Config', () => {
page,
browserName,
}) => {
await page.goto('/');
// Login with a user who has the visibility on the groups should see groups
// It's now the backend that decides if the user can see the group menu and they
// should be redirected to the groups page in such case
await keyCloakSignIn(page, browserName, 'team-administrator');
await page.route('**/api/v1.0/config/', async (route) => {
const request = route.request();
if (request.method().includes('GET')) {
@@ -83,6 +77,12 @@ test.describe('Config', () => {
}
});
await page.goto('/');
// Login with a user who has the visibility on the groups should see groups
// It's now the backend that decides if the user can see the group menu and they
// should be redirected to the groups page in such case
await keyCloakSignIn(page, browserName, 'team-administrator');
await expect(page.locator('menu')).toBeHidden();
await expect(page.getByText('Groups')).toBeVisible();

View File

@@ -25,9 +25,9 @@ const payloadGetTeams = {
],
};
const mockApiRequests = (page: Page) => {
void page.route('**/teams/?ordering=-created_at', (route) => {
void route.fulfill({
const mockApiRequests = async (page: Page) => {
await page.route('**/teams/?ordering=-created_at', async (route) => {
await route.fulfill({
json: payloadGetTeams.results,
});
});
@@ -40,11 +40,11 @@ test.describe('Keyboard navigation', () => {
}) => {
const page = await browser.newPage();
await mockApiRequests(page);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'team-owner-mail-member');
void mockApiRequests(page);
const header = page.locator('header');
// La Gauffre button is loaded asynchronously, so we wait for it to be visible

View File

@@ -85,10 +85,13 @@ const mailboxesFixtures = {
},
};
const interceptCommonApiRequests = (page: Page, mailDomains?: MailDomain[]) => {
const interceptCommonApiRequests = async (
page: Page,
mailDomains?: MailDomain[],
) => {
const mailDomainsToUse = mailDomains ?? mailDomainsFixtures;
void page.route('**/api/v1.0/mail-domains/?page=*', (route) => {
void route.fulfill({
await page.route('**/api/v1.0/mail-domains/?page=*', async (route) => {
await route.fulfill({
json: {
count: mailDomainsToUse.length,
next: null,
@@ -98,28 +101,37 @@ const interceptCommonApiRequests = (page: Page, mailDomains?: MailDomain[]) => {
});
});
mailDomainsToUse.forEach((mailDomain) => {
void page.route(`**/api/v1.0/mail-domains/${mailDomain.slug}/`, (route) => {
void route.fulfill({
json: mailDomain,
});
});
await Promise.all(
mailDomainsToUse.map(async (mailDomain) => {
await page.route(
`**/api/v1.0/mail-domains/${mailDomain.slug}/`,
async (route) => {
await route.fulfill({
json: mailDomain,
});
},
);
}),
);
void page.route(
`**/api/v1.0/mail-domains/${mailDomain.slug}/mailboxes/?page=1**`,
(route) => {
void route.fulfill({
json: {
count: mailboxesFixtures.domainFr.page1.length,
next: null,
previous: null,
results: mailboxesFixtures.domainFr.page1,
},
});
},
{ times: 1 },
);
});
await Promise.all(
mailDomainsToUse.map(async (mailDomain) => {
await page.route(
`**/api/v1.0/mail-domains/${mailDomain.slug}/mailboxes/?page=1**`,
async (route) => {
await route.fulfill({
json: {
count: mailboxesFixtures.domainFr.page1.length,
next: null,
previous: null,
results: mailboxesFixtures.domainFr.page1,
},
});
},
{ times: 1 },
);
}),
);
};
const navigateToMailboxCreationFormForMailDomainFr = async (
@@ -131,19 +143,20 @@ const navigateToMailboxCreationFormForMailDomainFr = async (
};
test.describe('Mail domain create mailbox', () => {
test.beforeEach(async ({ page, browserName }) => {
await page.goto('/');
// Login with a user who has the visibility on the mail domains
await keyCloakSignIn(page, browserName, 'mail-member');
});
test('checks create mailbox button is visible or not', async ({ page }) => {
test('checks create mailbox button is visible or not', async ({
page,
browserName,
}) => {
const domains = [...mailDomainsFixtures];
domains[0].status = 'enabled';
domains[1].status = 'pending';
domains[2].status = 'disabled';
domains[3].status = 'failed';
void interceptCommonApiRequests(page, domains);
await interceptCommonApiRequests(page, domains);
await page.goto('/');
// Login with a user who has the visibility on the mail domains
await keyCloakSignIn(page, browserName, 'mail-member');
await page
.locator('menu')
@@ -190,6 +203,7 @@ test.describe('Mail domain create mailbox', () => {
test('checks user can create a mailbox when he has post ability', async ({
page,
browserName,
}) => {
const newMailbox = {
id: '04433733-c9b7-453a-8122-755ac115bb00',
@@ -197,13 +211,13 @@ test.describe('Mail domain create mailbox', () => {
secondary_email: 'john.doe-complex2024@mail.com',
};
const interceptRequests = (page: Page) => {
void interceptCommonApiRequests(page);
const interceptRequests = async (page: Page) => {
await interceptCommonApiRequests(page);
void page.route(
await page.route(
'**/api/v1.0/mail-domains/domainfr/mailboxes/?page=1**',
(route) => {
void route.fulfill({
async (route) => {
await route.fulfill({
json: {
count: [...mailboxesFixtures.domainFr.page1, newMailbox].length,
next: null,
@@ -214,15 +228,15 @@ test.describe('Mail domain create mailbox', () => {
},
);
void page.route(
await page.route(
'**/api/v1.0/mail-domains/domainfr/mailboxes/',
(route) => {
async (route) => {
if (route.request().method() === 'POST') {
void route.fulfill({
await route.fulfill({
json: newMailbox,
});
} else {
void route.continue();
await route.continue();
}
},
);
@@ -249,8 +263,12 @@ test.describe('Mail domain create mailbox', () => {
}
});
void interceptRequests(page);
expect(true).toBeTruthy();
await interceptRequests(page);
await page.goto('/');
// Login with a user who has the visibility on the mail domains
await keyCloakSignIn(page, browserName, 'mail-member');
await navigateToMailboxCreationFormForMailDomainFr(page);
await page.getByRole('button', { name: 'Cancel' }).click();
@@ -315,15 +333,16 @@ test.describe('Mail domain create mailbox', () => {
test('checks user is not allowed to create a mailbox when he is missing post ability', async ({
page,
browserName,
}) => {
const localMailDomainsFixtures = [...mailDomainsFixtures];
localMailDomainsFixtures[0].abilities.post = false;
const localMailDomainDomainFr = localMailDomainsFixtures[0];
const localMailboxFixtures = { ...mailboxesFixtures };
const interceptRequests = (page: Page) => {
void page.route('**/api/v1.0/mail-domains/?page=*', (route) => {
void route.fulfill({
const interceptRequests = async (page: Page) => {
await page.route('**/api/v1.0/mail-domains/?page=*', async (route) => {
await route.fulfill({
json: {
count: localMailDomainsFixtures.length,
next: null,
@@ -333,16 +352,16 @@ test.describe('Mail domain create mailbox', () => {
});
});
void page.route('**/api/v1.0/mail-domains/domainfr/', (route) => {
void route.fulfill({
await page.route('**/api/v1.0/mail-domains/domainfr/', async (route) => {
await route.fulfill({
json: localMailDomainDomainFr,
});
});
void page.route(
await page.route(
'**/api/v1.0/mail-domains/domainfr/mailboxes/?page=1**',
(route) => {
void route.fulfill({
async (route) => {
await route.fulfill({
json: {
count: localMailboxFixtures.domainFr.page1.length,
next: null,
@@ -355,7 +374,11 @@ test.describe('Mail domain create mailbox', () => {
);
};
void interceptRequests(page);
await interceptRequests(page);
await page.goto('/');
// Login with a user who has the visibility on the mail domains
await keyCloakSignIn(page, browserName, 'mail-member');
await page
.locator('menu')

View File

@@ -142,11 +142,6 @@ test.describe('Mail domain', () => {
});
test.describe('user is administrator or owner', () => {
test.beforeEach(async ({ page, browserName }) => {
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-owner');
});
test.describe('mail domain is enabled', () => {
const mailDomainsFixtures: MailDomain[] = [
{
@@ -215,9 +210,12 @@ test.describe('Mail domain', () => {
},
];
test('checks if all tabs are visible', async ({ page }) => {
test('checks if all tabs are visible', async ({ page, browserName }) => {
await interceptCommonApiCalls(page, mailDomainsFixtures);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-owner');
await clickOnMailDomainsNavButton(page);
await assertMailDomainUpperElementsAreVisible(page);
@@ -230,9 +228,13 @@ test.describe('Mail domain', () => {
test('checks all the elements are visible when domain exist but contains no mailboxes', async ({
page,
browserName,
}) => {
await interceptCommonApiCalls(page, mailDomainsFixtures);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-owner');
await clickOnMailDomainsNavButton(page);
await assertMailDomainUpperElementsAreVisible(page);
@@ -248,6 +250,7 @@ test.describe('Mail domain', () => {
test('checks all the elements are visible when domain exists and contains 2 pages of mailboxes', async ({
page,
browserName,
}) => {
const mailboxesFixtures = {
domainFr: {
@@ -324,6 +327,9 @@ test.describe('Mail domain', () => {
await interceptApiCalls();
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-owner');
await clickOnMailDomainsNavButton(page);
await assertMailDomainUpperElementsAreVisible(page);
@@ -360,9 +366,15 @@ test.describe('Mail domain', () => {
},
];
test('checks expected elements are visible', async ({ page }) => {
test('checks expected elements are visible', async ({
page,
browserName,
}) => {
await interceptCommonApiCalls(page, mailDomainsFixtures);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-owner');
await clickOnMailDomainsNavButton(page);
await expect(page).toHaveURL(/mail-domains\//);
@@ -404,9 +416,15 @@ test.describe('Mail domain', () => {
},
];
test('checks expected elements are visible', async ({ page }) => {
test('checks expected elements are visible', async ({
page,
browserName,
}) => {
await interceptCommonApiCalls(page, mailDomainsFixtures);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-owner');
await clickOnMailDomainsNavButton(page);
await assertMailDomainUpperElementsAreVisible(page);
@@ -447,9 +465,15 @@ test.describe('Mail domain', () => {
},
];
test('checks expected elements are visible', async ({ page }) => {
test('checks expected elements are visible', async ({
page,
browserName,
}) => {
await interceptCommonApiCalls(page, mailDomainsFixtures);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-owner');
await clickOnMailDomainsNavButton(page);
await assertMailDomainUpperElementsAreVisible(page);
@@ -476,11 +500,6 @@ test.describe('Mail domain', () => {
});
test.describe('user is member', () => {
test.beforeEach(async ({ page, browserName }) => {
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-member');
});
test.describe('mail domain is enabled', () => {
const mailDomainsFixtures: MailDomain[] = [
{
@@ -551,9 +570,13 @@ test.describe('Mail domain', () => {
test('checks all the elements are visible when domain exist but contains no mailboxes', async ({
page,
browserName,
}) => {
await interceptCommonApiCalls(page, mailDomainsFixtures);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-member');
await clickOnMailDomainsNavButton(page);
await assertMailDomainUpperElementsAreVisible(page);
@@ -569,6 +592,7 @@ test.describe('Mail domain', () => {
test('checks all the elements are visible when domain exists and contains 2 pages of mailboxes', async ({
page,
browserName,
}) => {
const mailboxesFixtures = {
domainFr: {
@@ -645,6 +669,9 @@ test.describe('Mail domain', () => {
await interceptApiCalls();
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-member');
await clickOnMailDomainsNavButton(page);
await assertMailDomainUpperElementsAreVisible(page);
@@ -681,9 +708,15 @@ test.describe('Mail domain', () => {
},
];
test('checks expected elements are visible', async ({ page }) => {
test('checks expected elements are visible', async ({
page,
browserName,
}) => {
await interceptCommonApiCalls(page, mailDomainsFixtures);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-member');
await clickOnMailDomainsNavButton(page);
await expect(page).toHaveURL(/mail-domains\//);
@@ -725,9 +758,15 @@ test.describe('Mail domain', () => {
},
];
test('checks expected elements are visible', async ({ page }) => {
test('checks expected elements are visible', async ({
page,
browserName,
}) => {
await interceptCommonApiCalls(page, mailDomainsFixtures);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-member');
await clickOnMailDomainsNavButton(page);
await assertMailDomainUpperElementsAreVisible(page);
@@ -768,9 +807,15 @@ test.describe('Mail domain', () => {
},
];
test('checks expected elements are visible', async ({ page }) => {
test('checks expected elements are visible', async ({
page,
browserName,
}) => {
await interceptCommonApiCalls(page, mailDomainsFixtures);
await page.goto('/');
await keyCloakSignIn(page, browserName, 'mail-member');
await clickOnMailDomainsNavButton(page);
await assertMailDomainUpperElementsAreVisible(page);

View File

@@ -73,16 +73,14 @@ const mailDomainsFixtures: MailDomain[] = [
test.describe('Mail domains', () => {
test.describe('checks all the elements are visible', () => {
test.beforeEach(async ({ page, browserName }) => {
test('checks the sort button', async ({ page, browserName }) => {
await page.goto('/');
// The user is a team administrator, so they land on the team page
// This allows to prevent the redirection to the mail domains page
// to make the '/api/v1.0/mail-domains/?page=1&ordering=created_at'
// query at login, which will be cached and not called afterward in tests.
await keyCloakSignIn(page, browserName, 'team-administrator-mail-member');
});
test('checks the sort button', async ({ page }) => {
await page
.locator('menu')
.first()
@@ -128,7 +126,7 @@ test.describe('Mail domains', () => {
expect(responseSortDesc.ok()).toBeTruthy();
});
test('when no mail domain exists', async ({ page }) => {
test('when no mail domain exists', async ({ page, browserName }) => {
await page.route('**/api/v1.0/mail-domains/?page=*', async (route) => {
await route.fulfill({
json: {
@@ -140,6 +138,13 @@ test.describe('Mail domains', () => {
});
});
await page.goto('/');
// The user is a team administrator, so they land on the team page
// This allows to prevent the redirection to the mail domains page
// to make the '/api/v1.0/mail-domains/?page=1&ordering=created_at'
// query at login, which will be cached and not called afterward in tests.
await keyCloakSignIn(page, browserName, 'team-administrator-mail-member');
await page
.locator('menu')
.first()
@@ -152,7 +157,7 @@ test.describe('Mail domains', () => {
await expect(page.getByText('No domains exist.')).toBeVisible();
});
test('when 4 mail domains exist', async ({ page }) => {
test('when 4 mail domains exist', async ({ page, browserName }) => {
await page.route('**/api/v1.0/mail-domains/?page=*', async (route) => {
await route.fulfill({
json: {
@@ -164,6 +169,13 @@ test.describe('Mail domains', () => {
});
});
await page.goto('/');
// The user is a team administrator, so they land on the team page
// This allows to prevent the redirection to the mail domains page
// to make the '/api/v1.0/mail-domains/?page=1&ordering=created_at'
// query at login, which will be cached and not called afterward in tests.
await keyCloakSignIn(page, browserName, 'team-administrator-mail-member');
await page
.locator('menu')
.first()

View File

@@ -9,7 +9,7 @@ const baseURL = `http://localhost:${PORT}`;
*/
export default defineConfig({
// Timeout per test
timeout: 30 * 1000,
timeout: 10 * 1000,
testDir: './__tests__',
outputDir: './test-results',