From 1d5ed0816ae1d43eff62b21bc256fa4889557047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Heesen?= Date: Thu, 16 Jan 2025 14:48:28 +0100 Subject: [PATCH 1/6] Updated flaky tests to always run --- tests/base/account.spec.ts | 37 +++++++++++++++-------------- tests/base/fixtures/account.page.ts | 22 +++++++++++++++++ 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index a1ecf4b..7eca410 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -11,6 +11,8 @@ import verify from './config/expected/expected.json'; // no resetting storageState, mainmenu has more functionalities when logged in. // TODO: remove this beforeEach() once authentication as project set-up/fixture works. +let testLock = false; + // Before each test, log in test.beforeEach(async ({ page, browserName }) => { const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; @@ -67,12 +69,8 @@ test.describe('Account information actions', {annotation: {type: 'Account Dashbo }); }); - -// TODO: Add tests to check address can't be added/updated if the supplied information is incorrect -// TODO: Add tests to check address can't be deleted if it's the last/only one. -test.describe('Account address book actions', { annotation: {type: 'Account Dashboard', description: 'Tests for the Address Book'},}, () => { +test.describe.serial('Account address book actions', { annotation: {type: 'Account Dashboard', description: 'Tests for the Address Book'},}, () => { test.beforeEach(async ({page}) => { - // go to the Adress Book page await page.goto(slugs.account.addressBookSlug); await page.waitForLoadState(); }); @@ -91,12 +89,16 @@ test.describe('Account address book actions', { annotation: {type: 'Account Dash */ test('I can add my first address',{ tag: '@address-actions', }, async ({page}, testInfo) => { - // If account has no address, Address Book redirects to the 'Add New Address' page. - // We expect this to be true before continuing. - let addNewAddressTitle = page.getByRole('heading', {level: 1, name: selectors.newAddress.addNewAddressTitle}); - testInfo.skip(await addNewAddressTitle.isHidden(), `Heading "Add New Addres" is not found, please check if an address has already been added.`); const accountPage = new AccountPage(page); + let addNewAddressTitle = page.getByRole('heading', {level: 1, name: selectors.newAddress.addNewAddressTitle}); + if(await addNewAddressTitle.isHidden()) { + testLock = true; + await accountPage.deleteAllAddresses(); + await page.goto(slugs.account.addressNewSlug); + testLock = false; + } + let phoneNumberValue = inputvalues.firstAddress.firstPhoneNumberValue; let addressValue = inputvalues.firstAddress.firstStreetAddressValue; let zipCodeValue = inputvalues.firstAddress.firstZipCodeValue; @@ -104,7 +106,6 @@ test.describe('Account address book actions', { annotation: {type: 'Account Dash let stateValue = inputvalues.firstAddress.firstProvinceValue; await accountPage.addNewAddress(phoneNumberValue, addressValue, zipCodeValue, cityNameValue, stateValue); - }); /** @@ -128,7 +129,6 @@ test.describe('Account address book actions', { annotation: {type: 'Account Dash let stateValue = inputvalues.secondAddress.secondProvinceValue; await accountPage.addNewAddress(phoneNumberValue, addressValue, zipCodeValue, cityNameValue, stateValue); - }); /** @@ -153,11 +153,12 @@ test.describe('Account address book actions', { annotation: {type: 'Account Dash let newState = inputvalues.editedAddress.editStateValue; let editAddressButton = page.getByRole('link', {name: selectors.accountDashboard.editAddressIconButton}).first(); - testInfo.skip(await editAddressButton.isHidden(), `Button to edit Address is not found, please check if an address has been added.`); + if(await editAddressButton.isHidden()) { + await page.goto(slugs.account.addressNewSlug); + await accountPage.addNewAddress(inputvalues.firstAddress.firstPhoneNumberValue, inputvalues.firstAddress.firstStreetAddressValue, inputvalues.firstAddress.firstZipCodeValue, inputvalues.firstAddress.firstCityValue, inputvalues.firstAddress.firstProvinceValue); + } - await page.goto(slugs.account.addressBookSlug); await accountPage.editExistingAddress(newFirstName, newLastName, newStreet, newZipCode, newCity, newState); - }); /** @@ -175,8 +176,11 @@ test.describe('Account address book actions', { annotation: {type: 'Account Dash const accountPage = new AccountPage(page); let deleteAddressButton = page.getByRole('link', {name: selectors.accountDashboard.addressDeleteIconButton}).first(); - testInfo.skip(await deleteAddressButton.isHidden(), `Button to delete Address is not found, please check if an address has been added.`); + if(await deleteAddressButton.isHidden()) { + await page.goto(slugs.account.addressNewSlug); + await accountPage.addNewAddress(inputvalues.firstAddress.firstPhoneNumberValue, inputvalues.firstAddress.firstStreetAddressValue, inputvalues.firstAddress.firstZipCodeValue, inputvalues.firstAddress.firstCityValue, inputvalues.firstAddress.firstProvinceValue); + } await accountPage.deleteFirstAddressFromAddressBook(); }); }); @@ -184,7 +188,6 @@ test.describe('Account address book actions', { annotation: {type: 'Account Dash // TODO: move this to new spec file. test.describe('Newsletter actions', { annotation: {type: 'Account Dashboard', description: 'Newsletter tests'},}, () => { test.beforeEach(async ({page}) => { - // go to the Dashboard page await page.goto(slugs.account.accountOverviewSlug); }); @@ -220,6 +223,4 @@ test.describe('Newsletter actions', { annotation: {type: 'Account Dashboard', de await expect(newsletterCheckElement).not.toBeChecked(); } }); - }); - diff --git a/tests/base/fixtures/account.page.ts b/tests/base/fixtures/account.page.ts index 69aabd9..a1535bc 100644 --- a/tests/base/fixtures/account.page.ts +++ b/tests/base/fixtures/account.page.ts @@ -170,4 +170,26 @@ export class AccountPage { await this.accountCreationPasswordRepeatField.fill(password); await this.accountCreationConfirmButton.click(); } + + async deleteAllAddresses() { + let addressDeletedNotification = verify.address.addressDeletedNotification; + + // Handle the confirmation dialog for deleting addresses + this.page.on('dialog', async (dialog) => { + if (dialog.type() === 'confirm') { + await dialog.accept(); + } + }); + + // Continuously attempt to delete addresses until none are left + while (await this.deleteAddressButton.isVisible()) { + await this.deleteAddressButton.click(); + await this.page.waitForLoadState(); + + // Verify the notification for address deletion + await expect(this.page.getByText(addressDeletedNotification)).toBeVisible(); + } + + console.log("All addresses have been deleted."); + } } From 50ed5ea10ca812c6fb0ea7b53cdb960011be49ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Heesen?= Date: Thu, 16 Jan 2025 14:51:54 +0100 Subject: [PATCH 2/6] Removed unnecessary change --- tests/base/account.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index 7eca410..e94ebaa 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -11,8 +11,6 @@ import verify from './config/expected/expected.json'; // no resetting storageState, mainmenu has more functionalities when logged in. // TODO: remove this beforeEach() once authentication as project set-up/fixture works. -let testLock = false; - // Before each test, log in test.beforeEach(async ({ page, browserName }) => { const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; From 86413064620ec27ccbc85eb7fdfddb532460339d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Heesen?= Date: Thu, 16 Jan 2025 15:08:02 +0100 Subject: [PATCH 3/6] Removed unused testLock var --- tests/base/account.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index e94ebaa..4efca87 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -91,10 +91,8 @@ test.describe.serial('Account address book actions', { annotation: {type: 'Accou let addNewAddressTitle = page.getByRole('heading', {level: 1, name: selectors.newAddress.addNewAddressTitle}); if(await addNewAddressTitle.isHidden()) { - testLock = true; await accountPage.deleteAllAddresses(); await page.goto(slugs.account.addressNewSlug); - testLock = false; } let phoneNumberValue = inputvalues.firstAddress.firstPhoneNumberValue; From be958dd9c1604acee27abf5f23172a631a0fd643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Heesen?= Date: Wed, 22 Jan 2025 14:36:58 +0100 Subject: [PATCH 4/6] Fixed comments --- tests/base/account.spec.ts | 1 + tests/base/fixtures/account.page.ts | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index 4efca87..cbd3664 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -92,6 +92,7 @@ test.describe.serial('Account address book actions', { annotation: {type: 'Accou let addNewAddressTitle = page.getByRole('heading', {level: 1, name: selectors.newAddress.addNewAddressTitle}); if(await addNewAddressTitle.isHidden()) { await accountPage.deleteAllAddresses(); + testInfo.annotations.push({ type: 'Notification: deleted addresses', description: `All addresses are deleted to recreate the first address flow.` }); await page.goto(slugs.account.addressNewSlug); } diff --git a/tests/base/fixtures/account.page.ts b/tests/base/fixtures/account.page.ts index a1535bc..a5ae2f5 100644 --- a/tests/base/fixtures/account.page.ts +++ b/tests/base/fixtures/account.page.ts @@ -174,22 +174,17 @@ export class AccountPage { async deleteAllAddresses() { let addressDeletedNotification = verify.address.addressDeletedNotification; - // Handle the confirmation dialog for deleting addresses this.page.on('dialog', async (dialog) => { if (dialog.type() === 'confirm') { await dialog.accept(); } }); - // Continuously attempt to delete addresses until none are left while (await this.deleteAddressButton.isVisible()) { await this.deleteAddressButton.click(); await this.page.waitForLoadState(); - // Verify the notification for address deletion await expect(this.page.getByText(addressDeletedNotification)).toBeVisible(); } - - console.log("All addresses have been deleted."); } } From d944d9517dcc114f4d71413efa9ca8c130da349f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Heesen?= Date: Wed, 22 Jan 2025 15:25:18 +0100 Subject: [PATCH 5/6] Also add support for subdirectories --- playwright.config.example.ts | 41 ++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/playwright.config.example.ts b/playwright.config.example.ts index 880e5e3..c9e79db 100644 --- a/playwright.config.example.ts +++ b/playwright.config.example.ts @@ -5,22 +5,41 @@ import fs from "node:fs"; dotenv.config({ path: path.resolve(__dirname, '.env') }); function getTestFiles(baseDir: string, customDir: string): string[] { - const baseFiles = new Set(fs.readdirSync(baseDir).filter(file => file.endsWith('.spec.ts'))); - const customFiles = fs.readdirSync(customDir).filter(file => file.endsWith('.spec.ts')); const testFiles = new Set(); - // Get base files that have an override in custom - for (const file of baseFiles) { - const baseFilePath = path.join(baseDir, file); - const customFilePath = path.join(customDir, file); + function getAllFiles(dir: string): string[] { + const files = fs.readdirSync(dir, { withFileTypes: true }); + const allFiles: string[] = []; + for (const file of files) { + const fullPath = path.join(dir, file.name); + if (file.isDirectory()) { + allFiles.push(...getAllFiles(fullPath)); + } else { + allFiles.push(fullPath); + } + } + return allFiles; + } + + const baseFiles = getAllFiles(baseDir); + const customFiles = getAllFiles(customDir); + + const baseRelativePaths = new Map( + baseFiles.map((file) => [path.relative(baseDir, file), file]) + ); + + const customRelativePaths = new Map( + customFiles.map((file) => [path.relative(customDir, file), file]) + ); - testFiles.add(fs.existsSync(customFilePath) ? customFilePath : baseFilePath); + for (const [relativePath, baseFilePath] of baseRelativePaths.entries()) { + const customFilePath = customRelativePaths.get(relativePath); + testFiles.add(customFilePath || baseFilePath); } - // Add custom tests that aren't in base - for (const file of customFiles) { - if (!baseFiles.has(file)) { - testFiles.add(path.join(customDir, file)); + for (const [relativePath, customFilePath] of customRelativePaths.entries()) { + if (!baseRelativePaths.has(relativePath)) { + testFiles.add(customFilePath); } } From 6fbe1335daf9b88249f1c67f9f51f9b70c852ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Heesen?= Date: Wed, 22 Jan 2025 15:27:48 +0100 Subject: [PATCH 6/6] Remove wrong commit --- playwright.config.example.ts | 41 ++++++++++-------------------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/playwright.config.example.ts b/playwright.config.example.ts index c9e79db..880e5e3 100644 --- a/playwright.config.example.ts +++ b/playwright.config.example.ts @@ -5,41 +5,22 @@ import fs from "node:fs"; dotenv.config({ path: path.resolve(__dirname, '.env') }); function getTestFiles(baseDir: string, customDir: string): string[] { + const baseFiles = new Set(fs.readdirSync(baseDir).filter(file => file.endsWith('.spec.ts'))); + const customFiles = fs.readdirSync(customDir).filter(file => file.endsWith('.spec.ts')); const testFiles = new Set(); - function getAllFiles(dir: string): string[] { - const files = fs.readdirSync(dir, { withFileTypes: true }); - const allFiles: string[] = []; - for (const file of files) { - const fullPath = path.join(dir, file.name); - if (file.isDirectory()) { - allFiles.push(...getAllFiles(fullPath)); - } else { - allFiles.push(fullPath); - } - } - return allFiles; - } - - const baseFiles = getAllFiles(baseDir); - const customFiles = getAllFiles(customDir); - - const baseRelativePaths = new Map( - baseFiles.map((file) => [path.relative(baseDir, file), file]) - ); - - const customRelativePaths = new Map( - customFiles.map((file) => [path.relative(customDir, file), file]) - ); + // Get base files that have an override in custom + for (const file of baseFiles) { + const baseFilePath = path.join(baseDir, file); + const customFilePath = path.join(customDir, file); - for (const [relativePath, baseFilePath] of baseRelativePaths.entries()) { - const customFilePath = customRelativePaths.get(relativePath); - testFiles.add(customFilePath || baseFilePath); + testFiles.add(fs.existsSync(customFilePath) ? customFilePath : baseFilePath); } - for (const [relativePath, customFilePath] of customRelativePaths.entries()) { - if (!baseRelativePaths.has(relativePath)) { - testFiles.add(customFilePath); + // Add custom tests that aren't in base + for (const file of customFiles) { + if (!baseFiles.has(file)) { + testFiles.add(path.join(customDir, file)); } }