Refactored e2e tests to use Promise.all for parallel execution

In this update, several end-to-end (e2e) tests were refactored to use Promise.all for parallel execution of operations. This was done to improve the efficiency and stability of the tests, as waiting for each operation one by one could cause bottlenecks and potential failures in the test execution. Receives and click actions were grouped together to allow them to be executed concurrently where possible. The changes were applied across different test scenarios.
This commit is contained in:
giancarlo
2024-05-07 17:29:54 +07:00
parent 171a404379
commit 7c447c8848
6 changed files with 105 additions and 58 deletions

View File

@@ -32,13 +32,17 @@ export class AccountPageObject {
email, email,
); );
await this.page.click('[data-test="account-email-form"] button'); const click = this.page.click('[data-test="account-email-form"] button');
const req = await this.page.waitForResponse((resp) => { const req = await this.page
return resp.url().includes('auth/v1/user'); .waitForResponse((resp) => {
}); return resp.url().includes('auth/v1/user');
})
.then((response) => {
expect(response.status()).toBe(200);
});
expect(req.status()).toBe(200); return Promise.all([click, req]);
}).toPass(); }).toPass();
} }
@@ -57,20 +61,28 @@ export class AccountPageObject {
async deleteAccount() { async deleteAccount() {
await expect(async () => { await expect(async () => {
await this.page.click('[data-test="delete-account-button"]'); await this.page.click('[data-test="delete-account-button"]');
await this.page.fill( await this.page.fill(
'[data-test="delete-account-input-field"]', '[data-test="delete-account-input-field"]',
'DELETE', 'DELETE',
); );
await this.page.click('[data-test="confirm-delete-account-button"]');
const response = await this.page.waitForResponse((resp) => { const click = this.page.click(
return ( '[data-test="confirm-delete-account-button"]',
resp.url().includes('home/settings') && );
resp.request().method() === 'POST'
);
});
expect(response.status()).toBe(204); const response = await this.page
.waitForResponse((resp) => {
return (
resp.url().includes('home/settings') &&
resp.request().method() === 'POST'
);
})
.then((response) => {
expect(response.status()).toBe(303);
});
await Promise.all([click, response]);
}).toPass(); }).toPass();
} }

View File

@@ -16,12 +16,14 @@ test.describe('Account Settings', () => {
test('user can update their profile name', async () => { test('user can update their profile name', async () => {
const name = 'John Doe'; const name = 'John Doe';
await account.updateName(name); const request = account.updateName(name);
await page.waitForResponse((resp) => { const response = page.waitForResponse((resp) => {
return resp.url().includes('accounts'); return resp.url().includes('accounts');
}); });
await Promise.all([request, response]);
await expect(account.getProfileName()).toHaveText(name); await expect(account.getProfileName()).toHaveText(name);
}); });
@@ -34,12 +36,14 @@ test.describe('Account Settings', () => {
test('user can update their password', async () => { test('user can update their password', async () => {
const password = (Math.random() * 100000).toString(); const password = (Math.random() * 100000).toString();
await account.updatePassword(password); const request = account.updatePassword(password);
await page.waitForResponse((resp) => { const response = page.waitForResponse((resp) => {
return resp.url().includes('auth/v1/user'); return resp.url().includes('auth/v1/user');
}); });
await Promise.all([request, response]);
await account.auth.signOut(); await account.auth.signOut();
}); });
}); });
@@ -49,16 +53,20 @@ test.describe('Account Deletion', () => {
const account = new AccountPageObject(page); const account = new AccountPageObject(page);
await account.setup(); await account.setup();
await account.deleteAccount();
const response = await page.waitForResponse((resp) => { const request = account.deleteAccount();
return (
resp.url().includes('home/settings') &&
resp.request().method() === 'POST'
);
});
// The server should respond with a 303 redirect const response = page
expect(response.status()).toBe(303); .waitForResponse((resp) => {
return (
resp.url().includes('home/settings') &&
resp.request().method() === 'POST'
);
})
.then((response) => {
expect(response.status()).toBe(303);
});
await Promise.all([request, response]);
}); });
}); });

View File

@@ -1,4 +1,5 @@
import { test, expect } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { AuthPageObject } from './auth.po'; import { AuthPageObject } from './auth.po';
test.describe('Auth flow', () => { test.describe('Auth flow', () => {
@@ -14,19 +15,21 @@ test.describe('Auth flow', () => {
console.log(`Signing up with email ${email} ...`); console.log(`Signing up with email ${email} ...`);
await auth.signUp({ const signUp = auth.signUp({
email, email,
password: 'password', password: 'password',
repeatPassword: 'password', repeatPassword: 'password',
}); });
await page.waitForResponse(resp => { const response = page.waitForResponse((resp) => {
return resp.url().includes('auth'); return resp.url().includes('auth');
}); });
await Promise.all([signUp, response]);
await auth.visitConfirmEmailLink(email); await auth.visitConfirmEmailLink(email);
await page.waitForURL('http://localhost:3000/home'); await page.waitForURL('**/home');
expect(page.url()).toContain('http://localhost:3000/home'); expect(page.url()).toContain('http://localhost:3000/home');
}); });
@@ -42,7 +45,7 @@ test.describe('Auth flow', () => {
password: 'password', password: 'password',
}); });
await page.waitForURL('http://localhost:3000/home'); await page.waitForURL('**/home');
expect(page.url()).toContain('/home'); expect(page.url()).toContain('/home');
@@ -53,15 +56,17 @@ test.describe('Auth flow', () => {
}); });
test.describe('Protected routes', () => { test.describe('Protected routes', () => {
test('will redirect to the sign-in page if not authenticated', async ({ page }) => { test('will redirect to the sign-in page if not authenticated', async ({
page,
}) => {
await page.goto('/home/settings'); await page.goto('/home/settings');
expect(page.url()).toContain('/auth/sign-in?next=/home/settings'); expect(page.url()).toContain('/auth/sign-in?next=/home/settings');
}); });
test('will return a 404 for the admin page', async ({ page }) => { test('will return a 404 for the admin page', async ({ page }) => {
await page.goto('/admin') await page.goto('/admin');
expect(page.url()).toContain('/auth/sign-in'); expect(page.url()).toContain('/auth/sign-in');
}); });
}) });

View File

@@ -111,16 +111,18 @@ export class InvitationsPageObject {
async acceptInvitation() { async acceptInvitation() {
console.log('Accepting invitation...'); console.log('Accepting invitation...');
await this.page const click = this.page
.locator('[data-test="join-team-form"] button[type="submit"]') .locator('[data-test="join-team-form"] button[type="submit"]')
.click(); .click();
await this.page.waitForResponse((response) => { const response = this.page.waitForResponse((response) => {
return ( return (
response.url().includes('/join') && response.url().includes('/join') &&
response.request().method() === 'POST' response.request().method() === 'POST'
); );
}); });
await Promise.all([click, response]);
} }
private getInviteForm() { private getInviteForm() {

View File

@@ -28,23 +28,31 @@ export class TeamAccountsPageObject {
} }
goToSettings() { goToSettings() {
return this.page return expect(async () => {
.locator('a', { await this.page
hasText: 'Settings', .locator('a', {
}) hasText: 'Settings',
.click(); })
.click();
await this.page.waitForURL('**/home/*/settings');
}).toPass();
} }
goToBilling() { goToBilling() {
return this.page return expect(async () => {
.locator('a', { await this.page
hasText: 'Billing', .locator('a', {
}) hasText: 'Billing',
.click(); })
.click();
return await this.page.waitForURL('**/home/*/billing');
}).toPass();
} }
async openAccountsSelector() { openAccountsSelector() {
await expect(async () => { return expect(async () => {
await this.page.click('[data-test="account-selector-trigger"]'); await this.page.click('[data-test="account-selector-trigger"]');
return expect( return expect(
@@ -58,9 +66,14 @@ export class TeamAccountsPageObject {
await this.page.click('[data-test="create-team-account-trigger"]'); await this.page.click('[data-test="create-team-account-trigger"]');
await this.page.fill('[data-test="create-team-form"] input', teamName); await this.page.fill('[data-test="create-team-form"] input', teamName);
await this.page.click('[data-test="create-team-form"] button:last-child');
await this.page.waitForURL(`/home/${slug}`); const click = this.page.click(
'[data-test="create-team-form"] button:last-child',
);
const response = this.page.waitForURL(`/home/${slug}`);
await Promise.all([click, response]);
} }
async updateName(name: string, slug: string) { async updateName(name: string, slug: string) {
@@ -70,14 +83,14 @@ export class TeamAccountsPageObject {
name, name,
); );
await this.page.click( const click = this.page.click(
'[data-test="update-team-account-name-form"] button', '[data-test="update-team-account-name-form"] button',
); );
// the slug should be updated to match the new team name // the slug should be updated to match the new team name
await expect(this.page).toHaveURL( const response = this.page.waitForURL(`**/home/${slug}/settings`);
`http://localhost:3000/home/${slug}/settings`,
); return Promise.all([click, response]);
}).toPass(); }).toPass();
} }
@@ -94,9 +107,13 @@ export class TeamAccountsPageObject {
teamName, teamName,
); );
await this.page.click('[data-test="delete-team-form-confirm-button"]'); const click = this.page.click(
'[data-test="delete-team-form-confirm-button"]',
);
await this.page.waitForURL('http://localhost:3000/home'); const response = this.page.waitForURL('**/home');
return Promise.all([click, response]);
}).toPass(); }).toPass();
} }

View File

@@ -17,10 +17,13 @@ test.describe('Team Accounts', () => {
const { teamName, slug } = teamAccounts.createTeamName(); const { teamName, slug } = teamAccounts.createTeamName();
await teamAccounts.goToSettings(); await teamAccounts.goToSettings();
await teamAccounts.updateName(teamName, slug);
const request = teamAccounts.updateName(teamName, slug);
// the slug should be updated to match the new team name // the slug should be updated to match the new team name
await page.waitForURL(`http://localhost:3000/home/${slug}/settings`); const newUrl = page.waitForURL(`**/home/${slug}/settings`);
await Promise.all([request, newUrl]);
await teamAccounts.openAccountsSelector(); await teamAccounts.openAccountsSelector();