From 151eb5422ec726db47fd25ca2ffbf233dd3f0d8f Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 25 Feb 2025 11:43:24 +0100 Subject: [PATCH 01/19] Renamed fixtures to poms. Added actual fixtures to handle login and adding product to cart. WIP: Rewriting tests to utilize fixtures --- tests/base/account.spec.ts | 10 ++-- tests/base/cart.spec.ts | 22 ++++++-- tests/base/checkout.spec.ts | 8 +-- tests/base/contact.spec.ts | 2 +- tests/base/fixtures/fixtures.ts | 55 +++++++++++++++++++ tests/base/home.spec.ts | 4 +- tests/base/login.spec.ts | 2 +- tests/base/mainmenu.spec.ts | 6 +- tests/base/minicart.spec.ts | 6 +- tests/base/{fixtures => poms}/account.page.ts | 0 tests/base/{fixtures => poms}/cart.page.ts | 0 .../base/{fixtures => poms}/checkout.page.ts | 0 tests/base/{fixtures => poms}/contact.page.ts | 0 tests/base/{fixtures => poms}/home.page.ts | 0 tests/base/{fixtures => poms}/login.page.ts | 0 .../{fixtures => poms}/magentoAdmin.page.ts | 0 .../base/{fixtures => poms}/mainmenu.page.ts | 0 .../base/{fixtures => poms}/minicart.page.ts | 0 .../{fixtures => poms}/newsletter.page.ts | 0 tests/base/{fixtures => poms}/product.page.ts | 0 .../base/{fixtures => poms}/register.page.ts | 0 tests/base/product.spec.ts | 4 +- tests/base/register.spec.ts | 2 +- tests/base/setup.spec.ts | 15 +++-- 24 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 tests/base/fixtures/fixtures.ts rename tests/base/{fixtures => poms}/account.page.ts (100%) rename tests/base/{fixtures => poms}/cart.page.ts (100%) rename tests/base/{fixtures => poms}/checkout.page.ts (100%) rename tests/base/{fixtures => poms}/contact.page.ts (100%) rename tests/base/{fixtures => poms}/home.page.ts (100%) rename tests/base/{fixtures => poms}/login.page.ts (100%) rename tests/base/{fixtures => poms}/magentoAdmin.page.ts (100%) rename tests/base/{fixtures => poms}/mainmenu.page.ts (100%) rename tests/base/{fixtures => poms}/minicart.page.ts (100%) rename tests/base/{fixtures => poms}/newsletter.page.ts (100%) rename tests/base/{fixtures => poms}/product.page.ts (100%) rename tests/base/{fixtures => poms}/register.page.ts (100%) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index fcc9949..8fb278b 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -1,9 +1,9 @@ import {test, expect} from '@playwright/test'; -import {MainMenuPage} from './fixtures/mainmenu.page'; -import {LoginPage} from './fixtures/login.page'; -import {RegisterPage} from './fixtures/register.page'; -import {AccountPage} from './fixtures/account.page'; -import {NewsletterSubscriptionPage} from './fixtures/newsletter.page'; +import {MainMenuPage} from './poms/mainmenu.page'; +import {LoginPage} from './poms/login.page'; +import {RegisterPage} from './poms/register.page'; +import {AccountPage} from './poms/account.page'; +import {NewsletterSubscriptionPage} from './poms/newsletter.page'; import slugs from './config/slugs.json'; import inputvalues from './config/input-values/input-values.json'; diff --git a/tests/base/cart.spec.ts b/tests/base/cart.spec.ts index fb72aed..75fbd9d 100644 --- a/tests/base/cart.spec.ts +++ b/tests/base/cart.spec.ts @@ -1,12 +1,24 @@ import { test, expect } from '@playwright/test'; -import { ProductPage } from './fixtures/product.page'; -import { MainMenuPage } from './fixtures/mainmenu.page'; -import {LoginPage} from './fixtures/login.page'; -import { CartPage } from './fixtures/cart.page'; +import {productTest} from './fixtures/fixtures'; +import {ProductPage} from './poms/product.page'; +import {LoginPage} from './poms/login.page'; +import {CartPage} from './poms/cart.page'; import slugs from './config/slugs.json'; import UIReference from './config/element-identifiers/element-identifiers.json'; -import outcomeMarker from './config/outcome-markers/outcome-markers.json'; + +productTest('Fixture test', async ({productPage}) => { + const page = productPage.page; + await page.goto(slugs.cartSlug); + await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); +}); + +productTest('Other fixture test', async ({userProductPage, page}) => { + const { loginPage, productPage } = userProductPage; + await page.goto(slugs.cartSlug); + await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); +}); + test.describe('Cart functionalities (guest)', () => { /** diff --git a/tests/base/checkout.spec.ts b/tests/base/checkout.spec.ts index 0cb876d..c74695f 100644 --- a/tests/base/checkout.spec.ts +++ b/tests/base/checkout.spec.ts @@ -1,8 +1,8 @@ import {test, expect} from '@playwright/test'; -import {LoginPage} from './fixtures/login.page'; -import {ProductPage} from './fixtures/product.page'; -import {AccountPage} from './fixtures/account.page'; -import { CheckoutPage } from './fixtures/checkout.page'; +import {LoginPage} from './poms/login.page'; +import {ProductPage} from './poms/product.page'; +import {AccountPage} from './poms/account.page'; +import { CheckoutPage } from './poms/checkout.page'; import slugs from './config/slugs.json'; import UIReference from './config/element-identifiers/element-identifiers.json'; diff --git a/tests/base/contact.spec.ts b/tests/base/contact.spec.ts index a369e00..a909411 100644 --- a/tests/base/contact.spec.ts +++ b/tests/base/contact.spec.ts @@ -1,5 +1,5 @@ import {test} from '@playwright/test'; -import { ContactPage } from './fixtures/contact.page'; +import { ContactPage } from './poms/contact.page'; /** * @feature Magento 2 Contact Form diff --git a/tests/base/fixtures/fixtures.ts b/tests/base/fixtures/fixtures.ts new file mode 100644 index 0000000..cf54d0e --- /dev/null +++ b/tests/base/fixtures/fixtures.ts @@ -0,0 +1,55 @@ +import {test as base} from '@playwright/test'; +import {ProductPage} from '../poms/product.page'; +import { CartPage } from '../poms/cart.page'; +import { LoginPage } from '../poms/login.page'; +import {MainMenuPage} from '../poms/mainmenu.page'; + +import slugs from '../config/slugs.json'; + +import UIReference from '../config/element-identifiers/element-identifiers.json'; + +type CustomFixtures = { + productPage: ProductPage; + userProductPage: any; +} + +export const productTest = base.extend({ + productPage: async ({page}, use) => { + // Setup the fixture + const productPage = new ProductPage(page); + await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); + + // Use step (where the actual test takes place) + await use(productPage); + + // Teardown & Cleanup + const cartPage = new CartPage(page); + await cartPage.removeProduct(UIReference.productPage.simpleProductTitle); + }, + + userProductPage: async ({page, browserName}, use) => { + //Setup the fixture + const loginPage = new LoginPage(page); + const productPage = new ProductPage(page); + + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; + let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; + + if(!emailInputValue || !passwordInputValue) { + throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); + } + await loginPage.login(emailInputValue, passwordInputValue); + + await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); + // Use step (where the actual test takes place) + await use({productPage, loginPage}); + + // Teardown & Cleanup + const cartPage = new CartPage(page); + const mainMenu = new MainMenuPage(page); + await cartPage.removeProduct(UIReference.productPage.simpleProductTitle); + await mainMenu.logout(); + } +}); +export { expect } from '@playwright/test'; \ No newline at end of file diff --git a/tests/base/home.spec.ts b/tests/base/home.spec.ts index 8c987af..7dd9166 100644 --- a/tests/base/home.spec.ts +++ b/tests/base/home.spec.ts @@ -1,6 +1,6 @@ import {test, expect} from '@playwright/test'; -import {MainMenuPage} from './fixtures/mainmenu.page'; -import {HomePage} from './fixtures/home.page'; +import {MainMenuPage} from './poms/mainmenu.page'; +import {HomePage} from './poms/home.page'; import outcomeMarker from './config/outcome-markers/outcome-markers.json'; diff --git a/tests/base/login.spec.ts b/tests/base/login.spec.ts index e990163..23b0908 100644 --- a/tests/base/login.spec.ts +++ b/tests/base/login.spec.ts @@ -1,5 +1,5 @@ import {test as base} from '@playwright/test'; -import {LoginPage} from './fixtures/login.page'; +import {LoginPage} from './poms/login.page'; base('User can log in with valid credentials', async ({page, browserName}) => { const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; diff --git a/tests/base/mainmenu.spec.ts b/tests/base/mainmenu.spec.ts index 4e2ba13..ea80928 100644 --- a/tests/base/mainmenu.spec.ts +++ b/tests/base/mainmenu.spec.ts @@ -1,7 +1,7 @@ import {test} from '@playwright/test'; -import {LoginPage} from './fixtures/login.page'; -import {MainMenuPage} from './fixtures/mainmenu.page'; -import { ProductPage } from './fixtures/product.page'; +import {LoginPage} from './poms/login.page'; +import {MainMenuPage} from './poms/mainmenu.page'; +import { ProductPage } from './poms/product.page'; import UIReference from './config/element-identifiers/element-identifiers.json'; import slugs from './config/slugs.json'; diff --git a/tests/base/minicart.spec.ts b/tests/base/minicart.spec.ts index 52a82a5..9800376 100644 --- a/tests/base/minicart.spec.ts +++ b/tests/base/minicart.spec.ts @@ -1,7 +1,7 @@ import {test, expect} from '@playwright/test'; -import {MainMenuPage} from './fixtures/mainmenu.page'; -import {ProductPage} from './fixtures/product.page'; -import { MiniCartPage } from './fixtures/minicart.page'; +import {MainMenuPage} from './poms/mainmenu.page'; +import {ProductPage} from './poms/product.page'; +import { MiniCartPage } from './poms/minicart.page'; import slugs from './config/slugs.json'; import UIReference from './config/element-identifiers/element-identifiers.json'; diff --git a/tests/base/fixtures/account.page.ts b/tests/base/poms/account.page.ts similarity index 100% rename from tests/base/fixtures/account.page.ts rename to tests/base/poms/account.page.ts diff --git a/tests/base/fixtures/cart.page.ts b/tests/base/poms/cart.page.ts similarity index 100% rename from tests/base/fixtures/cart.page.ts rename to tests/base/poms/cart.page.ts diff --git a/tests/base/fixtures/checkout.page.ts b/tests/base/poms/checkout.page.ts similarity index 100% rename from tests/base/fixtures/checkout.page.ts rename to tests/base/poms/checkout.page.ts diff --git a/tests/base/fixtures/contact.page.ts b/tests/base/poms/contact.page.ts similarity index 100% rename from tests/base/fixtures/contact.page.ts rename to tests/base/poms/contact.page.ts diff --git a/tests/base/fixtures/home.page.ts b/tests/base/poms/home.page.ts similarity index 100% rename from tests/base/fixtures/home.page.ts rename to tests/base/poms/home.page.ts diff --git a/tests/base/fixtures/login.page.ts b/tests/base/poms/login.page.ts similarity index 100% rename from tests/base/fixtures/login.page.ts rename to tests/base/poms/login.page.ts diff --git a/tests/base/fixtures/magentoAdmin.page.ts b/tests/base/poms/magentoAdmin.page.ts similarity index 100% rename from tests/base/fixtures/magentoAdmin.page.ts rename to tests/base/poms/magentoAdmin.page.ts diff --git a/tests/base/fixtures/mainmenu.page.ts b/tests/base/poms/mainmenu.page.ts similarity index 100% rename from tests/base/fixtures/mainmenu.page.ts rename to tests/base/poms/mainmenu.page.ts diff --git a/tests/base/fixtures/minicart.page.ts b/tests/base/poms/minicart.page.ts similarity index 100% rename from tests/base/fixtures/minicart.page.ts rename to tests/base/poms/minicart.page.ts diff --git a/tests/base/fixtures/newsletter.page.ts b/tests/base/poms/newsletter.page.ts similarity index 100% rename from tests/base/fixtures/newsletter.page.ts rename to tests/base/poms/newsletter.page.ts diff --git a/tests/base/fixtures/product.page.ts b/tests/base/poms/product.page.ts similarity index 100% rename from tests/base/fixtures/product.page.ts rename to tests/base/poms/product.page.ts diff --git a/tests/base/fixtures/register.page.ts b/tests/base/poms/register.page.ts similarity index 100% rename from tests/base/fixtures/register.page.ts rename to tests/base/poms/register.page.ts diff --git a/tests/base/product.spec.ts b/tests/base/product.spec.ts index 55e0988..e82b619 100644 --- a/tests/base/product.spec.ts +++ b/tests/base/product.spec.ts @@ -1,7 +1,7 @@ import { test, expect } from '@playwright/test'; -import {ProductPage} from './fixtures/product.page'; -import {LoginPage} from './fixtures/login.page'; +import {ProductPage} from './poms/product.page'; +import {LoginPage} from './poms/login.page'; import slugs from './config/slugs.json'; import UIReference from './config/element-identifiers/element-identifiers.json'; diff --git a/tests/base/register.spec.ts b/tests/base/register.spec.ts index 670e26f..173ee41 100644 --- a/tests/base/register.spec.ts +++ b/tests/base/register.spec.ts @@ -1,5 +1,5 @@ import {test} from '@playwright/test'; -import {RegisterPage} from './fixtures/register.page'; +import {RegisterPage} from './poms/register.page'; import inputvalues from './config/input-values/input-values.json'; diff --git a/tests/base/setup.spec.ts b/tests/base/setup.spec.ts index 8c4f718..d5d965e 100644 --- a/tests/base/setup.spec.ts +++ b/tests/base/setup.spec.ts @@ -1,12 +1,15 @@ import { test as base } from '@playwright/test'; -import { MagentoAdminPage } from './fixtures/magentoAdmin.page'; -import { AccountPage } from './fixtures/account.page'; +import { MagentoAdminPage } from './poms/magentoAdmin.page'; +import { AccountPage } from './poms/account.page'; import values from './config/input-values/input-values.json'; import fs from 'fs'; import path from 'path'; base('Enable multiple Magento admin logins', {tag: '@setup',}, async ({ page, browserName }, testInfo) => { + // mark test as slow to double timeout + base.slow(); + const magentoAdminUsername = process.env.MAGENTO_ADMIN_USERNAME; const magentoAdminPassword = process.env.MAGENTO_ADMIN_PASSWORD; @@ -29,11 +32,13 @@ base('Enable multiple Magento admin logins', {tag: '@setup',}, async ({ page, br }); base('Setup Magento environment for tests', {tag: '@setup',}, async ({ page, browserName }, testInfo) => { + if (process.env[`SETUP_COMPLETE_${browserName?.toUpperCase()}`]) { testInfo.skip(true, `Skipping because configuration is only needed once.`); } await base.step(`Step 1: Perform actions`, async() =>{ + // Login to admin page const magentoAdminUsername = process.env.MAGENTO_ADMIN_USERNAME; const magentoAdminPassword = process.env.MAGENTO_ADMIN_PASSWORD; @@ -44,17 +49,19 @@ base('Setup Magento environment for tests', {tag: '@setup',}, async ({ page, bro const magentoAdminPage = new MagentoAdminPage(page); await magentoAdminPage.login(magentoAdminUsername, magentoAdminPassword); + // Add coupon code const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - const couponCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; if (!couponCode) { throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} is not defined in your .env file.`); } await magentoAdminPage.addCartPriceRule(couponCode); + + // Disable login Captcha await magentoAdminPage.disableLoginCaptcha(); + // Create account const accountPage = new AccountPage(page); - const accountEmail = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; const accountPassword = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; From f8736c6660cb93fa189aed228dbca1706f4e6b93 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 25 Feb 2025 15:06:54 +0100 Subject: [PATCH 02/19] Created fixtures. WIP: update cart.spec.ts to utilize fixtures, rewrite helper functions to accomodate fixtures --- tests/base/cart.spec.ts | 77 ++++++++++++++++++++++++++++++++- tests/base/fixtures/fixtures.ts | 8 ++-- tests/base/poms/cart.page.ts | 18 +++++--- 3 files changed, 91 insertions(+), 12 deletions(-) diff --git a/tests/base/cart.spec.ts b/tests/base/cart.spec.ts index 75fbd9d..9d924ad 100644 --- a/tests/base/cart.spec.ts +++ b/tests/base/cart.spec.ts @@ -13,12 +13,85 @@ productTest('Fixture test', async ({productPage}) => { await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); }); -productTest('Other fixture test', async ({userProductPage, page}) => { - const { loginPage, productPage } = userProductPage; +productTest('Other fixture test', async ({userProductPage}) => { + await userProductPage.page.goto(slugs.cartSlug); + await expect(userProductPage.page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); +}); + +/** + * @feature Add product to cart + * @scenario User adds a product to their cart + * @given I am on any page + * @when I navigate to a (simple) product page + * @and I click the 'add to cart' button + * @then I should see a notification the product has been added + * @and when I navigate to the cart + * @then I should see the product in my cart + */ +test('Add_simple_product_to_cart', {tag: '@cart'}, async ({page}) => { + const productPage = new ProductPage(page); + await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); await page.goto(slugs.cartSlug); await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); }); +/** + * @feature Remove product from cart + * @scenario User adds a product to their cart, then deletes it + * @given I add a product to my cart + * @when I navigate to the cart page + * @and I click the delete button + * @then I should see a notification that the product has been removed from my cart + * @and I should no longer see the product in my cart + */ +productTest('Remove_simple_product_from_cart', async ({productPage}) => { + const cartPage = new CartPage(productPage.page); + await cartPage.removeProduct(UIReference.productPage.simpleProductTitle); + // Add product again for the teardown + // TODO: Create fix so that the product does not have to be added again + await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); +}); + +/** + * @feature Change quantity of products in cart + * @scenario User has added a product and changes the quantity + * @given I have a product in my cart + * @and I am on the cart page + * @when I change the quantity of the product + * @and I click the update button + * @then the quantity field should have the new amount + * @and the subtotal/grand total should update + */ +productTest('Change_quantity_of_product_in_cart', async ({productPage}) => { + await productPage.page.goto(slugs.cartSlug); + await productPage.cartPage.changeProductQuantity('2'); +}); + +/** + * @feature Discount Code (cart) + * @scenario User adds a discount code to their cart + * @given I have a product in my cart + * @and I am on my cart page + * @when I click on the 'add discount code' button + * @then I fill in a code + * @and I click on 'apply code' + * @then I should see a confirmation that my code has been added + * @and the code should be visible in the cart + * @and a discount should be applied to the product + */ +productTest('Add_coupon_code_in_cart', async ({productPage, browserName}) => { + await productPage.page.goto(slugs.cartSlug); + + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + if(!discountCode) { + throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); + } + + await productPage.cartPage.applyDiscountCode(discountCode); +}); + + test.describe('Cart functionalities (guest)', () => { /** diff --git a/tests/base/fixtures/fixtures.ts b/tests/base/fixtures/fixtures.ts index cf54d0e..59a0bfd 100644 --- a/tests/base/fixtures/fixtures.ts +++ b/tests/base/fixtures/fixtures.ts @@ -9,7 +9,7 @@ import slugs from '../config/slugs.json'; import UIReference from '../config/element-identifiers/element-identifiers.json'; type CustomFixtures = { - productPage: ProductPage; + productPage: any; userProductPage: any; } @@ -17,13 +17,13 @@ export const productTest = base.extend({ productPage: async ({page}, use) => { // Setup the fixture const productPage = new ProductPage(page); + const cartPage = new CartPage(page); await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); // Use step (where the actual test takes place) - await use(productPage); + await use({productPage, cartPage, page}); // Teardown & Cleanup - const cartPage = new CartPage(page); await cartPage.removeProduct(UIReference.productPage.simpleProductTitle); }, @@ -43,7 +43,7 @@ export const productTest = base.extend({ await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); // Use step (where the actual test takes place) - await use({productPage, loginPage}); + await use({productPage, loginPage, page}); // Teardown & Cleanup const cartPage = new CartPage(page); diff --git a/tests/base/poms/cart.page.ts b/tests/base/poms/cart.page.ts index 9503f5c..24fbe99 100644 --- a/tests/base/poms/cart.page.ts +++ b/tests/base/poms/cart.page.ts @@ -1,5 +1,6 @@ import {expect, type Locator, type Page} from '@playwright/test'; +import slugs from '../config/slugs.json'; import UIReference from '../config/element-identifiers/element-identifiers.json'; import outcomeMarker from '../config/outcome-markers/outcome-markers.json'; @@ -17,6 +18,10 @@ export class CartPage { this.showDiscountButton = this.page.getByRole('button', { name: UIReference.cart.showDiscountFormButtonLabel }); } + // ============================================== + // Product-related methods + // ============================================== + async changeProductQuantity(amount: string){ const productRow = this.page.getByRole('row', {name: UIReference.productPage.simpleProductTitle}); let currentQuantity = await productRow.getByLabel(UIReference.cart.cartQuantityLabel).inputValue(); @@ -38,15 +43,16 @@ export class CartPage { let subTotalAfterUpdate = await productRow.getByText(UIReference.general.genericPriceSymbol).last().innerText(); // Assertions: subtotals are different, and quantity field is still the new amount. - expect(subTotalAfterUpdate, `Subtotals should not be the same`).not.toEqual(subTotalBeforeUpdate); - expect(currentQuantity, `quantity should be the new value`).toEqual(amount); + expect(subTotalAfterUpdate, `Subtotal before (${subTotalBeforeUpdate}) should not be the same as after update (${subTotalAfterUpdate}).`).not.toEqual(subTotalBeforeUpdate); + expect(currentQuantity, `Current quantity (${currentQuantity}) should not be the same as before (${amount}).`).toEqual(amount); } - // ============================================== - // Product-related methods - // ============================================== - async removeProduct(productTitle: string){ + // Ensure we are on the cart page + if(this.page.url() !== slugs.cartSlug){ + await this.page.goto(slugs.cartSlug); + }; + let removeButton = this.page.getByLabel(`${UIReference.general.removeLabel} ${productTitle}`); await removeButton.click(); await this.page.waitForLoadState(); From 120715121f766991f670491b0ece7038e8d6d063 Mon Sep 17 00:00:00 2001 From: Shay Date: Mon, 3 Mar 2025 15:27:05 +0100 Subject: [PATCH 03/19] WIP: Implementing fixtures --- tests/base/cart.spec.ts | 343 ------------ tests/base/fixtures/fixtures.ts | 2 +- .../{cart.page.ts => shoppingcart.page.ts} | 5 + tests/base/shoppingcart.spec.ts | 492 ++++++++++++++++++ 4 files changed, 498 insertions(+), 344 deletions(-) delete mode 100644 tests/base/cart.spec.ts rename tests/base/poms/{cart.page.ts => shoppingcart.page.ts} (96%) create mode 100644 tests/base/shoppingcart.spec.ts diff --git a/tests/base/cart.spec.ts b/tests/base/cart.spec.ts deleted file mode 100644 index 9d924ad..0000000 --- a/tests/base/cart.spec.ts +++ /dev/null @@ -1,343 +0,0 @@ -import { test, expect } from '@playwright/test'; -import {productTest} from './fixtures/fixtures'; -import {ProductPage} from './poms/product.page'; -import {LoginPage} from './poms/login.page'; -import {CartPage} from './poms/cart.page'; - -import slugs from './config/slugs.json'; -import UIReference from './config/element-identifiers/element-identifiers.json'; - -productTest('Fixture test', async ({productPage}) => { - const page = productPage.page; - await page.goto(slugs.cartSlug); - await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); -}); - -productTest('Other fixture test', async ({userProductPage}) => { - await userProductPage.page.goto(slugs.cartSlug); - await expect(userProductPage.page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); -}); - -/** - * @feature Add product to cart - * @scenario User adds a product to their cart - * @given I am on any page - * @when I navigate to a (simple) product page - * @and I click the 'add to cart' button - * @then I should see a notification the product has been added - * @and when I navigate to the cart - * @then I should see the product in my cart - */ -test('Add_simple_product_to_cart', {tag: '@cart'}, async ({page}) => { - const productPage = new ProductPage(page); - await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); - await page.goto(slugs.cartSlug); - await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); -}); - -/** - * @feature Remove product from cart - * @scenario User adds a product to their cart, then deletes it - * @given I add a product to my cart - * @when I navigate to the cart page - * @and I click the delete button - * @then I should see a notification that the product has been removed from my cart - * @and I should no longer see the product in my cart - */ -productTest('Remove_simple_product_from_cart', async ({productPage}) => { - const cartPage = new CartPage(productPage.page); - await cartPage.removeProduct(UIReference.productPage.simpleProductTitle); - // Add product again for the teardown - // TODO: Create fix so that the product does not have to be added again - await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); -}); - -/** - * @feature Change quantity of products in cart - * @scenario User has added a product and changes the quantity - * @given I have a product in my cart - * @and I am on the cart page - * @when I change the quantity of the product - * @and I click the update button - * @then the quantity field should have the new amount - * @and the subtotal/grand total should update - */ -productTest('Change_quantity_of_product_in_cart', async ({productPage}) => { - await productPage.page.goto(slugs.cartSlug); - await productPage.cartPage.changeProductQuantity('2'); -}); - -/** - * @feature Discount Code (cart) - * @scenario User adds a discount code to their cart - * @given I have a product in my cart - * @and I am on my cart page - * @when I click on the 'add discount code' button - * @then I fill in a code - * @and I click on 'apply code' - * @then I should see a confirmation that my code has been added - * @and the code should be visible in the cart - * @and a discount should be applied to the product - */ -productTest('Add_coupon_code_in_cart', async ({productPage, browserName}) => { - await productPage.page.goto(slugs.cartSlug); - - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - if(!discountCode) { - throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); - } - - await productPage.cartPage.applyDiscountCode(discountCode); -}); - - - -test.describe('Cart functionalities (guest)', () => { - /** - * @feature BeforeEach runs before each test in this group. - * @scenario Add a product to the cart and confirm it's there. - * @given I am on any page - * @when I navigate to a (simple) product page - * @and I add it to my cart - * @then I should see a notification - * @when I click the cart in the main menu - * @then the minicart should become visible - * @and I should see the product in the minicart - */ - test.beforeEach(async ({ page }) => { - const productPage = new ProductPage(page); - await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); - // await mainMenu.openMiniCart(); - // await expect(page.getByText(outcomeMarker.miniCart.simpleProductInCartTitle)).toBeVisible(); - await page.goto(slugs.cartSlug); - }); - - /** - * @feature Product can be added to cart - * @scenario User adds a product to their cart - * @given I have added a product to my cart - * @and I am on the cart page - * @then I should see the name of the product in my cart - */ - test('Product can be added to cart',{ tag: '@cart',}, async ({page}) => { - await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); - }); - - /** - * @feature Product permanence after login - * @scenario A product added to the cart should still be there after user has logged in - * @given I have a product in my cart - * @when I log in - * @then I should still have that product in my cart - */ - test('Product should remain in cart after logging in',{ tag: ['@cart', '@account']}, async ({page, browserName}) => { - await test.step('Add another product to cart', async () =>{ - const productpage = new ProductPage(page); - await page.goto(slugs.productpage.secondSimpleProductSlug); - await productpage.addSimpleProductToCart(UIReference.productPage.secondSimpleProducTitle, slugs.productpage.secondSimpleProductSlug); - }); - - await test.step('Log in with account', async () =>{ - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - const loginPage = new LoginPage(page); - let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; - let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; - - if(!emailInputValue || !passwordInputValue) { - throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); - } - - await loginPage.login(emailInputValue, passwordInputValue); - }); - - await page.goto(slugs.cartSlug); - await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.simpleProductTitle }),`${UIReference.productPage.simpleProductTitle} should still be in cart`).toBeVisible(); - await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.secondSimpleProducTitle }),`${UIReference.productPage.secondSimpleProducTitle} should still be in cart`).toBeVisible(); - }); - - /** - * @feature Remove product from cart - * @scenario User has added a product and wants to remove it from the cart page - * @given I have added a product to my cart - * @and I am on the cart page - * @when I click the delete button - * @then I should see a notification that the product has been removed from my cart - * @and I should no longer see the product in my cart - */ - test('Remove product from cart',{ tag: '@cart',}, async ({page}) => { - const cart = new CartPage(page); - await cart.removeProduct(UIReference.productPage.simpleProductTitle); - }); - - /** - * @feature Change quantity of products in cart - * @scenario User has added a product and changes the quantity - * @given I have a product in my cart - * @and I am on the cart page - * @when I change the quantity of the product - * @and I click the update button - * @then the quantity field should have the new amount - * @and the subtotal/grand total should update - */ - test('Change quantity of products in cart',{ tag: '@cart',}, async ({page}) => { - const cart = new CartPage(page); - await cart.changeProductQuantity('2'); - }); - - /** - * @feature Discount Code - * @scenario User adds a discount code to their cart - * @given I have a product in my cart - * @and I am on my cart page - * @when I click on the 'add discount code' button - * @then I fill in a code - * @and I click on 'apply code' - * @then I should see a confirmation that my code has been added - * @and the code should be visible in the cart - * @and a discount should be applied to the product - */ - test('Add coupon code in cart',{ tag: ['@cart', '@coupon-code']}, async ({page, browserName}) => { - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - const cart = new CartPage(page); - let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - - if(!discountCode) { - throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); - } - - await cart.applyDiscountCode(discountCode); - }); - - /** - * @feature Remove discount code from cart - * @scenario User has added a discount code, then removes it - * @given I have a product in my cart - * @and I am on my cart page - * @when I add a discount code - * @then I should see a notification - * @and the code should be visible in the cart - * @and a discount should be applied to a product - * @when I click the 'cancel coupon' button - * @then I should see a notification the discount has been removed - * @and the discount should no longer be visible. - */ - test('Remove coupon code from cart',{ tag: ['@cart', '@coupon-code'] }, async ({page, browserName}) => { - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - const cart = new CartPage(page); - let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - - if(!discountCode) { - throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); - } - - await cart.applyDiscountCode(discountCode); - await cart.removeDiscountCode(); - }); - - /** - * @feature Incorrect discount code check - * @scenario The user provides an incorrect discount code, the system should reflect that - * @given I have a product in my cart - * @and I am on the cart page - * @when I enter a wrong discount code - * @then I should get a notification that the code did not work. - */ - - test('Using an invalid coupon code should give an error',{ tag: ['@cart', '@coupon-code'] }, async ({page}) => { - const cart = new CartPage(page); - await cart.enterWrongCouponCode("Incorrect Coupon Code"); - }); -}) - -test.describe('Price checking tests', () => { - - // Test: Configurable Product Input check from PDP to checkout - // test.step: add configurable product to cart, return priceOnPDP and productAmount as variables - // test.step: call function retrieveCheckoutPrices() to go to checkout, retrieve values - // test.step: call function compareRetrievedPrices() to compare price on PDP to price in checkout - - /** - * @feature Simple Product price/amount check from PDP to Checkout - * @given none - * @when I go to a (simple) product page - * @and I add one or more to my cart - * @when I go to the checkout - * @then the amount of the product should be the same - * @and the price in the checkout should equal the price of the product * the amount of the product - */ - test('Simple product input to cart is consistent from PDP to checkout',{ tag: '@cart-price-check',}, async ({page}) => { - var productPagePrice: string; - var productPageAmount: string; - var checkoutProductDetails: string[]; - - const cart = new CartPage(page); - - await test.step('Step: Add simple product to cart', async () =>{ - const productPage = new ProductPage(page); - await page.goto(slugs.productpage.simpleProductSlug); - // set quantity to 2 so we can see that the math works - await page.getByLabel(UIReference.productPage.quantityFieldLabel).fill('2'); - - productPagePrice = await page.locator(UIReference.productPage.simpleProductPrice).innerText(); - productPageAmount = await page.getByLabel(UIReference.productPage.quantityFieldLabel).inputValue(); - await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug, '2'); - - }); - - await test.step('Step: go to checkout, get values', async () =>{ - await page.goto(slugs.checkoutSlug); - await page.waitForLoadState(); - - // returns productPriceInCheckout and productQuantityInCheckout - checkoutProductDetails = await cart.getCheckoutValues(UIReference.productPage.simpleProductTitle, productPagePrice, productPageAmount); - }); - - await test.step('Step: Calculate and check expectations', async () =>{ - await cart.calculateProductPricesAndCompare(productPagePrice, productPageAmount, checkoutProductDetails[0], checkoutProductDetails[1]); - }); - - }); - - /** - * @feature Configurable Product price/amount check from PDP to Checkout - * @given none - * @when I go to a (configurable) product page - * @and I add one or more to my cart - * @when I go to the checkout - * @then the amount of the product should be the same - * @and the price in the checkout should equal the price of the product * the amount of the product - */ - test('Configurable product input to cart is consistent from PDP to checkout',{ tag: '@cart-price-check',}, async ({page}) => { - var productPagePrice: string; - var productPageAmount: string; - var checkoutProductDetails: string[]; - - const cart = new CartPage(page); - - await test.step('Step: Add configurable product to cart', async () =>{ - const productPage = new ProductPage(page); - await page.goto(slugs.productpage.configurableProductSlug); - // set quantity to 2 so we can see that the math works - await page.getByLabel('Quantity').fill('2'); - - productPagePrice = await page.locator(UIReference.productPage.simpleProductPrice).innerText(); - productPageAmount = await page.getByLabel(UIReference.productPage.quantityFieldLabel).inputValue(); - await productPage.addConfigurableProductToCart(); - - }); - - await test.step('Step: go to checkout, get values', async () =>{ - await page.goto(slugs.checkoutSlug); - await page.waitForLoadState(); - - // returns productPriceInCheckout and productQuantityInCheckout - checkoutProductDetails = await cart.getCheckoutValues(UIReference.productPage.configurableProductTitle, productPagePrice, productPageAmount); - }); - - await test.step('Step: Calculate and check expectations', async () =>{ - await cart.calculateProductPricesAndCompare(productPagePrice, productPageAmount, checkoutProductDetails[0], checkoutProductDetails[1]); - }); - - }); -}); diff --git a/tests/base/fixtures/fixtures.ts b/tests/base/fixtures/fixtures.ts index 59a0bfd..8e0c568 100644 --- a/tests/base/fixtures/fixtures.ts +++ b/tests/base/fixtures/fixtures.ts @@ -1,6 +1,6 @@ import {test as base} from '@playwright/test'; import {ProductPage} from '../poms/product.page'; -import { CartPage } from '../poms/cart.page'; +import { CartPage } from '../poms/shoppingcart.page'; import { LoginPage } from '../poms/login.page'; import {MainMenuPage} from '../poms/mainmenu.page'; diff --git a/tests/base/poms/cart.page.ts b/tests/base/poms/shoppingcart.page.ts similarity index 96% rename from tests/base/poms/cart.page.ts rename to tests/base/poms/shoppingcart.page.ts index 24fbe99..33bf67e 100644 --- a/tests/base/poms/cart.page.ts +++ b/tests/base/poms/shoppingcart.page.ts @@ -60,6 +60,7 @@ export class CartPage { // Expect product to no longer be visible in the cart await expect (this.page.getByRole('cell', { name: productTitle }), `Product is not visible in cart`).toBeHidden(); + return; } // ============================================== @@ -90,9 +91,13 @@ export class CartPage { } let cancelCouponButton = this.page.getByRole('button', {name: UIReference.cart.cancelCouponButtonLabel}); + // Firefox specifc fix: wait for element to be visible. + await cancelCouponButton.waitFor(); await cancelCouponButton.click(); await this.page.waitForLoadState(); + // Wait for notification to become visible + await this.page.getByText(outcomeMarker.cart.discountRemovedNotification).waitFor(); await expect.soft(this.page.getByText(outcomeMarker.cart.discountRemovedNotification),`Notification should be visible`).toBeVisible(); await expect(this.page.getByText(outcomeMarker.cart.priceReducedSymbols),`'- $' should not be on the page`).toBeHidden(); } diff --git a/tests/base/shoppingcart.spec.ts b/tests/base/shoppingcart.spec.ts new file mode 100644 index 0000000..b55dcdd --- /dev/null +++ b/tests/base/shoppingcart.spec.ts @@ -0,0 +1,492 @@ +import { test, expect } from '@playwright/test'; +import {productTest} from './fixtures/fixtures'; +import {ProductPage} from './poms/product.page'; +import {LoginPage} from './poms/login.page'; +import {CartPage} from './poms/shoppingcart.page'; + +import slugs from './config/slugs.json'; +import UIReference from './config/element-identifiers/element-identifiers.json'; + +test.describe('Product in cart tests', {tag: '@cart-product-group'}, () => { + /** + * @feature Add product to cart + * @scenario User adds a product to their cart + * @given I am on any page + * @when I navigate to a (simple) product page + * @and I click the 'add to cart' button + * @then I should see a notification the product has been added + * @and when I navigate to the cart + * @then I should see the product in my cart + */ + test('Add_simple_product_to_cart', {tag: '@cart'}, async ({page}) => { + const productPage = new ProductPage(page); + await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); + await page.goto(slugs.cartSlug); + await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); + }); + + /** + * @feature Remove product from cart + * @scenario User adds a product to their cart, then deletes it + * @given I add a product to my cart + * @when I navigate to the cart page + * @and I click the delete button + * @then I should see a notification that the product has been removed from my cart + * @and I should no longer see the product in my cart + */ + productTest('Remove_simple_product_from_cart', {tag: '@cart'}, async ({productPage}) => { + const cartPage = new CartPage(productPage.page); + await cartPage.removeProduct(UIReference.productPage.simpleProductTitle); + // Add product again for the teardown + // TODO: Create fix so that the product does not have to be added again + await productPage.productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); + }); + + /** + * @feature Change quantity of products in cart + * @scenario User has added a product and changes the quantity + * @given I have a product in my cart + * @and I am on the cart page + * @when I change the quantity of the product + * @and I click the update button + * @then the quantity field should have the new amount + * @and the subtotal/grand total should update + */ + productTest('Change_quantity_of_simple_product_in_cart', {tag: '@cart'}, async ({productPage}) => { + await productPage.page.goto(slugs.cartSlug); + await productPage.cartPage.changeProductQuantity('2'); + }); + + /** + * @feature Product permanence after login + * @scenario A product added to the cart should still be there after user has logged in + * @given I have a product in my cart + * @when I log in + * @then I should still have that product in my cart + */ + test('Product_remains_in_cart_after_login',{ tag: ['@cart', '@account']}, async ({page, browserName}) => { + await test.step('Add product to cart', async () =>{ + const productpage = new ProductPage(page); + await page.goto(slugs.productpage.simpleProductSlug); + await productpage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); + }); + + await test.step('Log in with account', async () =>{ + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + const loginPage = new LoginPage(page); + let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; + let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; + + if(!emailInputValue || !passwordInputValue) { + throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); + } + + await loginPage.login(emailInputValue, passwordInputValue); + }); + + await page.goto(slugs.cartSlug); + await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.simpleProductTitle }),`${UIReference.productPage.simpleProductTitle} should still be in cart`).toBeVisible(); + await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.secondSimpleProducTitle }),`${UIReference.productPage.secondSimpleProducTitle} should still be in cart`).toBeVisible(); + }); +}); // End of product in cart group + +test.describe('Coupon in cart tests', {tag: '@cart-coupon-group'}, () => { + /** + * @feature Discount Code (cart) + * @scenario User adds a discount code to their cart + * @given I have a product in my cart + * @and I am on my cart page + * @when I click on the 'add discount code' button + * @then I fill in a code + * @and I click on 'apply code' + * @then I should see a confirmation that my code has been added + * @and the code should be visible in the cart + * @and a discount should be applied to the product + */ + productTest('Add_coupon_code_in_cart', {tag: ['@cart']}, async ({productPage, browserName}) => { + await productPage.page.goto(slugs.cartSlug); + + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + if(!discountCode) { + throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); + } + + await productPage.cartPage.applyDiscountCode(discountCode); + }); + + /** + * @feature Remove discount code from cart + * @scenario User has added a discount code, then removes it + * @given I have a product in my cart + * @and I am on my cart page + * @when I add a discount code + * @then I should see a notification + * @and the code should be visible in the cart + * @and a discount should be applied to a product + * @when I click the 'cancel coupon' button + * @then I should see a notification the discount has been removed + * @and the discount should no longer be visible. + */ + productTest('Remove_coupon_code_from_cart', {tag: ['@cart']}, async ({productPage, browserName}) => { + await productPage.page.goto(slugs.cartSlug); + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + if(!discountCode) { + throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); + } + + await productPage.cartPage.applyDiscountCode(discountCode); + await productPage.cartPage.removeDiscountCode(); + }); + + /** + * @feature Incorrect discount code check + * @scenario The user provides an incorrect discount code, the system should reflect that + * @given I have a product in my cart + * @and I am on the cart page + * @when I enter a wrong discount code + * @then I should get a notification that the code did not work. + */ + productTest('Invalid_coupon_code_is_rejected', {tag: ['@cart']}, async ({productPage}) => { + await productPage.page.goto(slugs.cartSlug); + await productPage.cartPage.enterWrongCouponCode("Incorrect Coupon Code"); + }); +}); // end of coupon in cart group + +test.describe('Price checking tests', {tag: '@cart-price-group'}, () => { + /** + * @feature Simple Product price/amount check from PDP to Checkout + * @given none + * @when I go to a (simple) product page + * @and I add one or more to my cart + * @when I go to the checkout + * @then the amount of the product should be the same + * @and the price in the checkout should equal the price of the product * the amount of the product + */ + test('Simple_product_values_are_consistent_from_PDP_to_checkout',{ tag: '@cart-price-check',}, async ({page}) => { + var productPagePrice: string; + var productPageAmount: string; + var checkoutProductDetails: string[]; + + const cart = new CartPage(page); + + await test.step('Step: Add simple product to cart', async () =>{ + const productPage = new ProductPage(page); + await page.goto(slugs.productpage.simpleProductSlug); + // set quantity to 2 so we can see that the math works + await page.getByLabel(UIReference.productPage.quantityFieldLabel).fill('2'); + + productPagePrice = await page.locator(UIReference.productPage.simpleProductPrice).innerText(); + productPageAmount = await page.getByLabel(UIReference.productPage.quantityFieldLabel).inputValue(); + await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug, '2'); + + }); + + await test.step('Step: go to checkout, get values', async () =>{ + await page.goto(slugs.checkoutSlug); + await page.waitForLoadState(); + + // returns productPriceInCheckout and productQuantityInCheckout + checkoutProductDetails = await cart.getCheckoutValues(UIReference.productPage.simpleProductTitle, productPagePrice, productPageAmount); + }); + + await test.step('Step: Calculate and check expectations', async () =>{ + await cart.calculateProductPricesAndCompare(productPagePrice, productPageAmount, checkoutProductDetails[0], checkoutProductDetails[1]); + }); + + }); + + /** + * @feature Configurable Product price/amount check from PDP to Checkout + * @given none + * @when I go to a (configurable) product page + * @and I add one or more to my cart + * @when I go to the checkout + * @then the amount of the product should be the same + * @and the price in the checkout should equal the price of the product * the amount of the product + */ + test('Configurable_product_values_are_consistent_from_PDP_to_checkout',{ tag: '@cart-price-check',}, async ({page}) => { + var productPagePrice: string; + var productPageAmount: string; + var checkoutProductDetails: string[]; + + const cart = new CartPage(page); + + await test.step('Step: Add configurable product to cart', async () =>{ + const productPage = new ProductPage(page); + await page.goto(slugs.productpage.configurableProductSlug); + // set quantity to 2 so we can see that the math works + await page.getByLabel('Quantity').fill('2'); + + productPagePrice = await page.locator(UIReference.productPage.simpleProductPrice).innerText(); + productPageAmount = await page.getByLabel(UIReference.productPage.quantityFieldLabel).inputValue(); + await productPage.addConfigurableProductToCart(); + + }); + + await test.step('Step: go to checkout, get values', async () =>{ + await page.goto(slugs.checkoutSlug); + await page.waitForLoadState(); + + // returns productPriceInCheckout and productQuantityInCheckout + checkoutProductDetails = await cart.getCheckoutValues(UIReference.productPage.configurableProductTitle, productPagePrice, productPageAmount); + }); + + await test.step('Step: Calculate and check expectations', async () =>{ + await cart.calculateProductPricesAndCompare(productPagePrice, productPageAmount, checkoutProductDetails[0], checkoutProductDetails[1]); + }); + + }); +}); // End of price checking group + + + +// test.describe('Cart functionalities (guest)', () => { +// /** +// * @feature BeforeEach runs before each test in this group. +// * @scenario Add a product to the cart and confirm it's there. +// * @given I am on any page +// * @when I navigate to a (simple) product page +// * @and I add it to my cart +// * @then I should see a notification +// * @when I click the cart in the main menu +// * @then the minicart should become visible +// * @and I should see the product in the minicart +// */ +// test.beforeEach(async ({ page }) => { +// const productPage = new ProductPage(page); +// await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); +// // await mainMenu.openMiniCart(); +// // await expect(page.getByText(outcomeMarker.miniCart.simpleProductInCartTitle)).toBeVisible(); +// await page.goto(slugs.cartSlug); +// }); + +// /** +// * @feature Product can be added to cart +// * @scenario User adds a product to their cart +// * @given I have added a product to my cart +// * @and I am on the cart page +// * @then I should see the name of the product in my cart +// */ +// test('Product can be added to cart',{ tag: '@cart',}, async ({page}) => { +// await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); +// }); + +// /** +// * @feature Product permanence after login +// * @scenario A product added to the cart should still be there after user has logged in +// * @given I have a product in my cart +// * @when I log in +// * @then I should still have that product in my cart +// */ +// test('Product should remain in cart after logging in',{ tag: ['@cart', '@account']}, async ({page, browserName}) => { +// await test.step('Add another product to cart', async () =>{ +// const productpage = new ProductPage(page); +// await page.goto(slugs.productpage.secondSimpleProductSlug); +// await productpage.addSimpleProductToCart(UIReference.productPage.secondSimpleProducTitle, slugs.productpage.secondSimpleProductSlug); +// }); + +// await test.step('Log in with account', async () =>{ +// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; +// const loginPage = new LoginPage(page); +// let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; +// let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; + +// if(!emailInputValue || !passwordInputValue) { +// throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); +// } + +// await loginPage.login(emailInputValue, passwordInputValue); +// }); + +// await page.goto(slugs.cartSlug); +// await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.simpleProductTitle }),`${UIReference.productPage.simpleProductTitle} should still be in cart`).toBeVisible(); +// await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.secondSimpleProducTitle }),`${UIReference.productPage.secondSimpleProducTitle} should still be in cart`).toBeVisible(); +// }); + +// /** +// * @feature Remove product from cart +// * @scenario User has added a product and wants to remove it from the cart page +// * @given I have added a product to my cart +// * @and I am on the cart page +// * @when I click the delete button +// * @then I should see a notification that the product has been removed from my cart +// * @and I should no longer see the product in my cart +// */ +// test('Remove product from cart',{ tag: '@cart',}, async ({page}) => { +// const cart = new CartPage(page); +// await cart.removeProduct(UIReference.productPage.simpleProductTitle); +// }); + +// /** +// * @feature Change quantity of products in cart +// * @scenario User has added a product and changes the quantity +// * @given I have a product in my cart +// * @and I am on the cart page +// * @when I change the quantity of the product +// * @and I click the update button +// * @then the quantity field should have the new amount +// * @and the subtotal/grand total should update +// */ +// test('Change quantity of products in cart',{ tag: '@cart',}, async ({page}) => { +// const cart = new CartPage(page); +// await cart.changeProductQuantity('2'); +// }); + +// /** +// * @feature Discount Code +// * @scenario User adds a discount code to their cart +// * @given I have a product in my cart +// * @and I am on my cart page +// * @when I click on the 'add discount code' button +// * @then I fill in a code +// * @and I click on 'apply code' +// * @then I should see a confirmation that my code has been added +// * @and the code should be visible in the cart +// * @and a discount should be applied to the product +// */ +// test('Add coupon code in cart',{ tag: ['@cart', '@coupon-code']}, async ({page, browserName}) => { +// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; +// const cart = new CartPage(page); +// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + +// if(!discountCode) { +// throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); +// } + +// await cart.applyDiscountCode(discountCode); +// }); + +// /** +// * @feature Remove discount code from cart +// * @scenario User has added a discount code, then removes it +// * @given I have a product in my cart +// * @and I am on my cart page +// * @when I add a discount code +// * @then I should see a notification +// * @and the code should be visible in the cart +// * @and a discount should be applied to a product +// * @when I click the 'cancel coupon' button +// * @then I should see a notification the discount has been removed +// * @and the discount should no longer be visible. +// */ +// test('Remove coupon code from cart',{ tag: ['@cart', '@coupon-code'] }, async ({page, browserName}) => { +// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; +// const cart = new CartPage(page); +// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + +// if(!discountCode) { +// throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); +// } + +// await cart.applyDiscountCode(discountCode); +// await cart.removeDiscountCode(); +// }); + +// /** +// * @feature Incorrect discount code check +// * @scenario The user provides an incorrect discount code, the system should reflect that +// * @given I have a product in my cart +// * @and I am on the cart page +// * @when I enter a wrong discount code +// * @then I should get a notification that the code did not work. +// */ + +// test('Using an invalid coupon code should give an error',{ tag: ['@cart', '@coupon-code'] }, async ({page}) => { +// const cart = new CartPage(page); +// await cart.enterWrongCouponCode("Incorrect Coupon Code"); +// }); +// }) + +// test.describe('Price checking tests', () => { + +// // Test: Configurable Product Input check from PDP to checkout +// // test.step: add configurable product to cart, return priceOnPDP and productAmount as variables +// // test.step: call function retrieveCheckoutPrices() to go to checkout, retrieve values +// // test.step: call function compareRetrievedPrices() to compare price on PDP to price in checkout + +// /** +// * @feature Simple Product price/amount check from PDP to Checkout +// * @given none +// * @when I go to a (simple) product page +// * @and I add one or more to my cart +// * @when I go to the checkout +// * @then the amount of the product should be the same +// * @and the price in the checkout should equal the price of the product * the amount of the product +// */ +// test('Simple product input to cart is consistent from PDP to checkout',{ tag: '@cart-price-check',}, async ({page}) => { +// var productPagePrice: string; +// var productPageAmount: string; +// var checkoutProductDetails: string[]; + +// const cart = new CartPage(page); + +// await test.step('Step: Add simple product to cart', async () =>{ +// const productPage = new ProductPage(page); +// await page.goto(slugs.productpage.simpleProductSlug); +// // set quantity to 2 so we can see that the math works +// await page.getByLabel(UIReference.productPage.quantityFieldLabel).fill('2'); + +// productPagePrice = await page.locator(UIReference.productPage.simpleProductPrice).innerText(); +// productPageAmount = await page.getByLabel(UIReference.productPage.quantityFieldLabel).inputValue(); +// await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug, '2'); + +// }); + +// await test.step('Step: go to checkout, get values', async () =>{ +// await page.goto(slugs.checkoutSlug); +// await page.waitForLoadState(); + +// // returns productPriceInCheckout and productQuantityInCheckout +// checkoutProductDetails = await cart.getCheckoutValues(UIReference.productPage.simpleProductTitle, productPagePrice, productPageAmount); +// }); + +// await test.step('Step: Calculate and check expectations', async () =>{ +// await cart.calculateProductPricesAndCompare(productPagePrice, productPageAmount, checkoutProductDetails[0], checkoutProductDetails[1]); +// }); + +// }); + +// /** +// * @feature Configurable Product price/amount check from PDP to Checkout +// * @given none +// * @when I go to a (configurable) product page +// * @and I add one or more to my cart +// * @when I go to the checkout +// * @then the amount of the product should be the same +// * @and the price in the checkout should equal the price of the product * the amount of the product +// */ +// test('Configurable product input to cart is consistent from PDP to checkout',{ tag: '@cart-price-check',}, async ({page}) => { +// var productPagePrice: string; +// var productPageAmount: string; +// var checkoutProductDetails: string[]; + +// const cart = new CartPage(page); + +// await test.step('Step: Add configurable product to cart', async () =>{ +// const productPage = new ProductPage(page); +// await page.goto(slugs.productpage.configurableProductSlug); +// // set quantity to 2 so we can see that the math works +// await page.getByLabel('Quantity').fill('2'); + +// productPagePrice = await page.locator(UIReference.productPage.simpleProductPrice).innerText(); +// productPageAmount = await page.getByLabel(UIReference.productPage.quantityFieldLabel).inputValue(); +// await productPage.addConfigurableProductToCart(); + +// }); + +// await test.step('Step: go to checkout, get values', async () =>{ +// await page.goto(slugs.checkoutSlug); +// await page.waitForLoadState(); + +// // returns productPriceInCheckout and productQuantityInCheckout +// checkoutProductDetails = await cart.getCheckoutValues(UIReference.productPage.configurableProductTitle, productPagePrice, productPageAmount); +// }); + +// await test.step('Step: Calculate and check expectations', async () =>{ +// await cart.calculateProductPricesAndCompare(productPagePrice, productPageAmount, checkoutProductDetails[0], checkoutProductDetails[1]); +// }); + +// }); +// }); From f297d2ac30e2ffea882515042c34cea17dedcbe9 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 09:49:26 +0100 Subject: [PATCH 04/19] Logout() added to login.page.ts, WIP: Rewriting account.spec.ts to include fixtures --- tests/base/account.spec.ts | 126 ++++++++++++++++++++--------- tests/base/config/slugs.json | 3 +- tests/base/fixtures/fixtures.ts | 21 +++++ tests/base/poms/login.page.ts | 9 +++ tests/base/poms/newsletter.page.ts | 20 +++-- 5 files changed, 134 insertions(+), 45 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index 8fb278b..abfd787 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -1,4 +1,6 @@ import {test, expect} from '@playwright/test'; +import {productTest} from './fixtures/fixtures'; + import {MainMenuPage} from './poms/mainmenu.page'; import {LoginPage} from './poms/login.page'; import {RegisterPage} from './poms/register.page'; @@ -10,20 +12,66 @@ import inputvalues from './config/input-values/input-values.json'; import UIReference from './config/element-identifiers/element-identifiers.json'; import outcomeMarker from './config/outcome-markers/outcome-markers.json'; -// Before each test, log in -test.beforeEach(async ({ page, browserName }) => { - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; - let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; +/** + * @feature Magento 2 newsletter subscriptions + * @scenario User (un)subscribes from a newsletter + * @given I am logged in + * @and I am on the account dashboard page + * @when I click on the newsletter link in the sidebar + * @then I should navigate to the newsletter subscription page + * @when I (un)check the subscription button + * @then I should see a message confirming my action + * @and My subscription option should be updated. + */ +productTest('Update_newsletter_subscription', {tag: ['@fixture', '@newsletter']}, async ({userPage, browserName}) => { + test.skip(browserName === 'webkit', '.click() does not work, will be fixed later'); + const newsLetterPage = new NewsletterSubscriptionPage(userPage.page); + await newsLetterPage.page.goto(slugs.account.newsLetterSlug); + + await newsLetterPage.updateNewsletterSubscription(); +}); - if(!emailInputValue || !passwordInputValue) { - throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); +/** + * @feature Magento 2 Add First Address to Account + * @scenario User adds a first address to their account + * @given I am logged in + * @and I am on the account dashboard page + * @when I go to the page where I can add my address + * @and I haven't added an address yet + * @when I fill in the required information + * @and I click the save button + * @then I should see a notification my address has been updated. + * @and The new address should be selected as default and shipping address + */ + +test('I can add my first address',{ tag: '@address-actions', }, async ({page}, testInfo) => { + const accountPage = new AccountPage(page); + let addNewAddressTitle = page.getByRole('heading', {level: 1, name: UIReference.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); } - const loginPage = new LoginPage(page); - await loginPage.login(emailInputValue, passwordInputValue); + await accountPage.addNewAddress(); }); + +// // Before each test, log in +// test.beforeEach(async ({ page, browserName }) => { +// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; +// let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; +// let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; + +// if(!emailInputValue || !passwordInputValue) { +// throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); +// } + +// const loginPage = new LoginPage(page); +// await loginPage.login(emailInputValue, passwordInputValue); +// }); + test.describe('Account information actions', {annotation: {type: 'Account Dashboard', description: 'Test for Account Information'},}, () => { test.beforeEach(async ({page}) => { @@ -190,34 +238,34 @@ test.describe('Newsletter actions', { annotation: {type: 'Account Dashboard', de await page.goto(slugs.account.accountOverviewSlug); }); - /** - * @feature Magento 2 newsletter subscriptions - * @scenario User (un)subscribes from a newsletter - * @given I am logged in - * @and I am on the account dashboard page - * @when I click on the newsletter link in the sidebar - * @then I should navigate to the newsletter subscription page - * @when I (un)check the subscription button - * @then I should see a message confirming my action - * @and My subscription option should be updated. - */ - test('I can update my newsletter subscription',{ tag: '@newsletter-actions', }, async ({page, browserName}) => { - test.skip(browserName === 'webkit', '.click() does not work, still searching for a workaround'); - const newsletterPage = new NewsletterSubscriptionPage(page); - let newsletterLink = page.getByRole('link', { name: UIReference.accountDashboard.links.newsletterLink }); - const newsletterCheckElement = page.getByLabel(UIReference.newsletterSubscriptions.generalSubscriptionCheckLabel); - - await newsletterLink.click(); - await expect(page.getByText(outcomeMarker.account.newsletterSubscriptionTitle, { exact: true })).toBeVisible(); - - let updateSubscription = await newsletterPage.updateNewsletterSubscription(); - - await newsletterLink.click(); - - if(updateSubscription){ - await expect(newsletterCheckElement).toBeChecked(); - } else { - await expect(newsletterCheckElement).not.toBeChecked(); - } - }); + // /** + // * @feature Magento 2 newsletter subscriptions + // * @scenario User (un)subscribes from a newsletter + // * @given I am logged in + // * @and I am on the account dashboard page + // * @when I click on the newsletter link in the sidebar + // * @then I should navigate to the newsletter subscription page + // * @when I (un)check the subscription button + // * @then I should see a message confirming my action + // * @and My subscription option should be updated. + // */ + // test('I can update my newsletter subscription',{ tag: '@newsletter-actions', }, async ({page, browserName}) => { + // test.skip(browserName === 'webkit', '.click() does not work, still searching for a workaround'); + // const newsletterPage = new NewsletterSubscriptionPage(page); + // let newsletterLink = page.getByRole('link', { name: UIReference.accountDashboard.links.newsletterLink }); + // const newsletterCheckElement = page.getByLabel(UIReference.newsletterSubscriptions.generalSubscriptionCheckLabel); + + // await newsletterLink.click(); + // await expect(page.getByText(outcomeMarker.account.newsletterSubscriptionTitle, { exact: true })).toBeVisible(); + + // let updateSubscription = await newsletterPage.updateNewsletterSubscription(); + + // await newsletterLink.click(); + + // if(updateSubscription){ + // await expect(newsletterCheckElement).toBeChecked(); + // } else { + // await expect(newsletterCheckElement).not.toBeChecked(); + // } + // }); }); diff --git a/tests/base/config/slugs.json b/tests/base/config/slugs.json index cf4d59a..8c865cb 100644 --- a/tests/base/config/slugs.json +++ b/tests/base/config/slugs.json @@ -6,7 +6,8 @@ "addressBookSlug": "/customer/address", "addressIndexSlug": "/customer/address/index", "addressNewSlug": "customer/address/new", - "changePasswordSlug": "/customer/account/edit/changepass/1/" + "changePasswordSlug": "/customer/account/edit/changepass/1/", + "newsLetterSlug": "newsletter/manage/" }, "productpage": { "simpleProductSlug": "/push-it-messenger-bag.html", diff --git a/tests/base/fixtures/fixtures.ts b/tests/base/fixtures/fixtures.ts index 8e0c568..ff589de 100644 --- a/tests/base/fixtures/fixtures.ts +++ b/tests/base/fixtures/fixtures.ts @@ -10,6 +10,7 @@ import UIReference from '../config/element-identifiers/element-identifiers.json' type CustomFixtures = { productPage: any; + userPage: any; userProductPage: any; } @@ -27,6 +28,26 @@ export const productTest = base.extend({ await cartPage.removeProduct(UIReference.productPage.simpleProductTitle); }, + userPage: async({page, browserName}, use) => { + // Setup the fixture + const loggedInPage = new LoginPage(page); + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; + let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; + + if(!emailInputValue || !passwordInputValue) { + throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); + } + await loggedInPage.login(emailInputValue, passwordInputValue); + + // Use step (where the actual test takes place) + await use({loggedInPage, page}); + + // Teardown & Cleanup + await loggedInPage.logout(); + + }, + userProductPage: async ({page, browserName}, use) => { //Setup the fixture const loginPage = new LoginPage(page); diff --git a/tests/base/poms/login.page.ts b/tests/base/poms/login.page.ts index 06d0d7c..c88f2a9 100644 --- a/tests/base/poms/login.page.ts +++ b/tests/base/poms/login.page.ts @@ -8,12 +8,16 @@ export class LoginPage { readonly loginEmailField: Locator; readonly loginPasswordField: Locator; readonly loginButton: Locator; + readonly mainMenuAccountButton: Locator; + readonly mainMenuLogoutItem: Locator; constructor(page: Page) { this.page = page; this.loginEmailField = page.getByLabel(UIReference.credentials.emailFieldLabel, {exact: true}); this.loginPasswordField = page.getByLabel(UIReference.credentials.passwordFieldLabel, {exact: true}); this.loginButton = page.getByRole('button', { name: UIReference.credentials.loginButtonLabel }); + this.mainMenuAccountButton = page.getByLabel(UIReference.mainMenu.myAccountButtonLabel); + this.mainMenuLogoutItem = page.getByTitle(UIReference.mainMenu.myAccountLogoutItem); } async login(email: string, password: string){ @@ -25,4 +29,9 @@ export class LoginPage { await expect(this.page.getByRole('link', { name: UIReference.mainMenu.myAccountLogoutItem })).toBeVisible(); } + + async logout(){ + await this.mainMenuAccountButton.click(); + await this.mainMenuLogoutItem.click(); + } } \ No newline at end of file diff --git a/tests/base/poms/newsletter.page.ts b/tests/base/poms/newsletter.page.ts index b557c1d..ff4b34c 100644 --- a/tests/base/poms/newsletter.page.ts +++ b/tests/base/poms/newsletter.page.ts @@ -1,4 +1,7 @@ import {expect, type Locator, type Page} from '@playwright/test'; + +import slugs from '../config/slugs.json'; + import UIReference from '../config/element-identifiers/element-identifiers.json'; import outcomeMarker from '../config/outcome-markers/outcome-markers.json'; @@ -14,27 +17,34 @@ export class NewsletterSubscriptionPage { } async updateNewsletterSubscription(){ - if(await this.newsletterCheckElement.isChecked()) { // user is already subscribed, test runs unsubscribe var subscriptionUpdatedNotification = outcomeMarker.account.newsletterRemovedNotification; await this.newsletterCheckElement.uncheck(); await this.saveSubscriptionsButton.click(); - var subscribed = false; - + await this.page.waitForLoadState(); } else { // user is not yet subscribed, test runs subscribe subscriptionUpdatedNotification = outcomeMarker.account.newsletterSavedNotification; await this.newsletterCheckElement.check(); await this.saveSubscriptionsButton.click(); - subscribed = true; + await this.page.waitForLoadState(); } + await this.page.getByText(subscriptionUpdatedNotification).waitFor(); await expect(this.page.getByText(subscriptionUpdatedNotification)).toBeVisible(); - return subscribed; + + // Navigate to newsletter page, because saving the subscription refreshes to the account page + await this.page.goto(slugs.account.newsLetterSlug); + + if(subscribed){ + await expect(this.newsletterCheckElement).toBeChecked(); + } else { + await expect(this.newsletterCheckElement).not.toBeChecked(); + } } } From caba62b8b65b6425def610e66822c28e07b03d6a Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 10:10:58 +0100 Subject: [PATCH 05/19] Rewrote test 'Add_the_first_address' to use fixture and skip if default address has been set --- tests/base/account.spec.ts | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index abfd787..2299771 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -43,20 +43,35 @@ productTest('Update_newsletter_subscription', {tag: ['@fixture', '@newsletter']} * @then I should see a notification my address has been updated. * @and The new address should be selected as default and shipping address */ +productTest('Add_the_first_address', {tag: ['@fixture', '@address']}, async ({userPage}, testInfo) => { + const accountPage = new AccountPage(userPage.page); + await userPage.page.goto(slugs.account.accountOverviewSlug); -test('I can add my first address',{ tag: '@address-actions', }, async ({page}, testInfo) => { - const accountPage = new AccountPage(page); - let addNewAddressTitle = page.getByRole('heading', {level: 1, name: UIReference.newAddress.addNewAddressTitle}); + let defaultBillingAddressSet = userPage.page.getByText("You have not set a default billing address."); - 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); + if(await defaultBillingAddressSet.isVisible()) { + // No default address set, so no address has been added yet. + await userPage.page.goto(slugs.account.addressNewSlug); + await accountPage.addNewAddress(); + } else { + testInfo.annotations.push({ type: 'Test skipped', description: `A default address has been set, adding a first address has already been done.`}); + test.skip(true,'Default billing address found.'); } - - await accountPage.addNewAddress(); }); +// test('I can add my first address',{ tag: '@address-actions', }, async ({page}, testInfo) => { +// const accountPage = new AccountPage(page); +// let addNewAddressTitle = page.getByRole('heading', {level: 1, name: UIReference.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); +// } + +// await accountPage.addNewAddress(); +// }); + // // Before each test, log in // test.beforeEach(async ({ page, browserName }) => { From 1371e9d0773246415403b0fd3eecd5136b8fb888 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 10:12:14 +0100 Subject: [PATCH 06/19] Moved element identifier to appropriate JSON file --- tests/base/account.spec.ts | 2 +- tests/base/config/element-identifiers/element-identifiers.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index 2299771..b72fd2c 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -47,7 +47,7 @@ productTest('Add_the_first_address', {tag: ['@fixture', '@address']}, async ({us const accountPage = new AccountPage(userPage.page); await userPage.page.goto(slugs.account.accountOverviewSlug); - let defaultBillingAddressSet = userPage.page.getByText("You have not set a default billing address."); + let defaultBillingAddressSet = userPage.page.getByText(UIReference.accountDashboard.defaultAddressNotSetLabel); if(await defaultBillingAddressSet.isVisible()) { // No default address set, so no address has been added yet. diff --git a/tests/base/config/element-identifiers/element-identifiers.json b/tests/base/config/element-identifiers/element-identifiers.json index 3e77c5d..eafbead 100644 --- a/tests/base/config/element-identifiers/element-identifiers.json +++ b/tests/base/config/element-identifiers/element-identifiers.json @@ -35,6 +35,7 @@ "accountDashboardTitleLabel": "Account Information", "accountSideBarLabel": "Sidebar Main", "addAddressButtonLabel": "ADD NEW ADDRESS", + "defaultAddressNotSetLabel": "You have not set a default billing address.", "addressDeleteIconButton": "trash", "editAddressIconButton": "pencil-alt", "addressBookArea": ".block-addresses-list", From e2a744ef914cbf03b5aaafa417edb901f0496eb5 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 10:31:06 +0100 Subject: [PATCH 07/19] Rewrote 'Add_another_address' to use a fixture --- tests/base/account.spec.ts | 29 ++++++++++++++++++++--------- tests/base/poms/login.page.ts | 2 ++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index b72fd2c..8ed08b4 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -59,20 +59,31 @@ productTest('Add_the_first_address', {tag: ['@fixture', '@address']}, async ({us } }); -// test('I can add my first address',{ tag: '@address-actions', }, async ({page}, testInfo) => { +/** + * @given I am logged in + * @and I am on the account dashboard page + * @when I go to the page where I can add another address + * @when I fill in the required information + * @and I click the save button + * @then I should see a notification my address has been updated. + * @and The new address should be listed + */ +productTest('Add_another_address', {tag: ['@fixture', '@address']}, async ({userPage}, testInfo) => { + await userPage.page.goto(slugs.account.addressNewSlug); + const accountPage = new AccountPage(userPage.page); + + await accountPage.addNewAddress(); +}); +// test('I can add another address',{ tag: '@address-actions', }, async ({page}) => { +// await page.goto(slugs.account.addressNewSlug); // const accountPage = new AccountPage(page); -// let addNewAddressTitle = page.getByRole('heading', {level: 1, name: UIReference.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); -// } - + // await accountPage.addNewAddress(); // }); + + // // Before each test, log in // test.beforeEach(async ({ page, browserName }) => { // const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; diff --git a/tests/base/poms/login.page.ts b/tests/base/poms/login.page.ts index c88f2a9..4ec15be 100644 --- a/tests/base/poms/login.page.ts +++ b/tests/base/poms/login.page.ts @@ -2,6 +2,7 @@ import {expect, type Locator, type Page} from '@playwright/test'; import slugs from '../config/slugs.json'; import UIReference from '../config/element-identifiers/element-identifiers.json'; +import outcomeMarker from '../config/outcome-markers/outcome-markers.json'; export class LoginPage { readonly page: Page; @@ -33,5 +34,6 @@ export class LoginPage { async logout(){ await this.mainMenuAccountButton.click(); await this.mainMenuLogoutItem.click(); + await expect(this.page.getByText(outcomeMarker.logout.logoutConfirmationText, { exact: true })).toBeVisible(); } } \ No newline at end of file From cab8d3e5fd7a0727252427a0cb6d5c65191f6d2a Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 10:42:35 +0100 Subject: [PATCH 08/19] Rewrote test 'Update_existing_address' to include a fixture --- tests/base/account.spec.ts | 69 ++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index 8ed08b4..9f199c8 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -13,7 +13,7 @@ import UIReference from './config/element-identifiers/element-identifiers.json'; import outcomeMarker from './config/outcome-markers/outcome-markers.json'; /** - * @feature Magento 2 newsletter subscriptions + * @feature Newsletter subscriptions * @scenario User (un)subscribes from a newsletter * @given I am logged in * @and I am on the account dashboard page @@ -32,7 +32,7 @@ productTest('Update_newsletter_subscription', {tag: ['@fixture', '@newsletter']} }); /** - * @feature Magento 2 Add First Address to Account + * @feature Add First Address to Account * @scenario User adds a first address to their account * @given I am logged in * @and I am on the account dashboard page @@ -60,6 +60,7 @@ productTest('Add_the_first_address', {tag: ['@fixture', '@address']}, async ({us }); /** + * @feature Add another address * @given I am logged in * @and I am on the account dashboard page * @when I go to the page where I can add another address @@ -68,19 +69,73 @@ productTest('Add_the_first_address', {tag: ['@fixture', '@address']}, async ({us * @then I should see a notification my address has been updated. * @and The new address should be listed */ -productTest('Add_another_address', {tag: ['@fixture', '@address']}, async ({userPage}, testInfo) => { +productTest('Add_another_address', {tag: ['@fixture', '@address']}, async ({userPage}) => { await userPage.page.goto(slugs.account.addressNewSlug); const accountPage = new AccountPage(userPage.page); await accountPage.addNewAddress(); }); -// test('I can add another address',{ tag: '@address-actions', }, async ({page}) => { -// await page.goto(slugs.account.addressNewSlug); + +/** + * @feature Update Address in Account + * @scenario User updates an existing address to their account + * @given I am logged in + * @and I am on the account dashboard page + * @when I go to the page where I can see my address(es) + * @when I click on the button to edit the address + * @and I fill in the required information correctly + * @then I click the save button + * @then I should see a notification my address has been updated. + * @and The updated address should be visible in the addres book page. + */ +productTest('Update_existing_address', {tag: ['@fixture', '@address']}, async ({userPage}) => { + const accountPage = new AccountPage(userPage.page); + await userPage.page.goto(slugs.account.addressBookSlug); + let editAddressButton = userPage.page.getByRole('link', {name: UIReference.accountDashboard.editAddressIconButton}).first(); + if(await editAddressButton.isHidden()){ + // The edit address button was not found, add another address first. + await accountPage.addNewAddress(); + } + await accountPage.editExistingAddress(); +}); + + +// test('I can edit an existing address',{ tag: '@address-actions', }, async ({page}) => { // const accountPage = new AccountPage(page); - -// await accountPage.addNewAddress(); +// await page.goto(slugs.account.addressNewSlug); +// let editAddressButton = page.getByRole('link', {name: UIReference.accountDashboard.editAddressIconButton}).first(); + +// if(await editAddressButton.isHidden()){ +// // The edit address button was not found, add another address first. +// await accountPage.addNewAddress(); +// } + +// await page.goto(slugs.account.addressBookSlug); +// await accountPage.editExistingAddress(); // }); +/** + * @feature Magento 2 Delete Address from account + * @scenario User removes an address from their account + * @given I am logged in + * @and I am on the account dashboard page + * @when I go to the page where I can see my address(es) + * @when I click the trash button for the address I want to delete + * @and I click the confirmation button + * @then I should see a notification my address has been deleted. + * @and The address should be removed from the overview. + */ +test('I can delete an address',{ tag: '@address-actions', }, async ({page}, testInfo) => { + const accountPage = new AccountPage(page); + + let deleteAddressButton = page.getByRole('link', {name: UIReference.accountDashboard.addressDeleteIconButton}).first(); + + if(await deleteAddressButton.isHidden()) { + await page.goto(slugs.account.addressNewSlug); + await accountPage.addNewAddress(); + } + await accountPage.deleteFirstAddressFromAddressBook(); +}); From 47069eed6e9cf251be6a4f61bd916fa67fd4de9e Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 10:47:10 +0100 Subject: [PATCH 09/19] Rewrote 'Delete_existing_address' to include fixture --- tests/base/account.spec.ts | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index 9f199c8..e6f8b40 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -99,21 +99,6 @@ productTest('Update_existing_address', {tag: ['@fixture', '@address']}, async ({ await accountPage.editExistingAddress(); }); - -// test('I can edit an existing address',{ tag: '@address-actions', }, async ({page}) => { -// const accountPage = new AccountPage(page); -// await page.goto(slugs.account.addressNewSlug); -// let editAddressButton = page.getByRole('link', {name: UIReference.accountDashboard.editAddressIconButton}).first(); - -// if(await editAddressButton.isHidden()){ -// // The edit address button was not found, add another address first. -// await accountPage.addNewAddress(); -// } - -// await page.goto(slugs.account.addressBookSlug); -// await accountPage.editExistingAddress(); -// }); - /** * @feature Magento 2 Delete Address from account * @scenario User removes an address from their account @@ -125,17 +110,27 @@ productTest('Update_existing_address', {tag: ['@fixture', '@address']}, async ({ * @then I should see a notification my address has been deleted. * @and The address should be removed from the overview. */ -test('I can delete an address',{ tag: '@address-actions', }, async ({page}, testInfo) => { - const accountPage = new AccountPage(page); - - let deleteAddressButton = page.getByRole('link', {name: UIReference.accountDashboard.addressDeleteIconButton}).first(); - +productTest('Delete_existing_address', {tag: ['@fixture', '@address']}, async ({userPage}) => { + const accountPage = new AccountPage(userPage.page); + await userPage.page.goto(slugs.account.addressBookSlug); + let deleteAddressButton = userPage.page.getByRole('link', {name: UIReference.accountDashboard.addressDeleteIconButton}).first(); if(await deleteAddressButton.isHidden()) { - await page.goto(slugs.account.addressNewSlug); + await userPage.page.goto(slugs.account.addressNewSlug); await accountPage.addNewAddress(); } await accountPage.deleteFirstAddressFromAddressBook(); }); +// test('I can delete an address',{ tag: '@address-actions', }, async ({page}, testInfo) => { +// const accountPage = new AccountPage(page); + +// let deleteAddressButton = page.getByRole('link', {name: UIReference.accountDashboard.addressDeleteIconButton}).first(); + +// if(await deleteAddressButton.isHidden()) { +// await page.goto(slugs.account.addressNewSlug); +// await accountPage.addNewAddress(); +// } +// await accountPage.deleteFirstAddressFromAddressBook(); +// }); From 336ee24823969847f1f3e164f44d193318a3a74b Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 11:10:34 +0100 Subject: [PATCH 10/19] Reworked 'Password_change' test to include fixture --- tests/base/account.spec.ts | 50 +++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index e6f8b40..59dde76 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -1,4 +1,5 @@ import {test, expect} from '@playwright/test'; +import {faker} from '@faker-js/faker'; import {productTest} from './fixtures/fixtures'; import {MainMenuPage} from './poms/mainmenu.page'; @@ -120,18 +121,49 @@ productTest('Delete_existing_address', {tag: ['@fixture', '@address']}, async ({ } await accountPage.deleteFirstAddressFromAddressBook(); }); -// test('I can delete an address',{ tag: '@address-actions', }, async ({page}, testInfo) => { -// const accountPage = new AccountPage(page); -// let deleteAddressButton = page.getByRole('link', {name: UIReference.accountDashboard.addressDeleteIconButton}).first(); +/** + * @feature Magento 2 Change Password + * @scenario User changes their password + * @given I am logged in + * @and I am on the Account Dashboard page + * @when I navigate to the Account Information page + * @and I check the 'change password' option + * @when I fill in the new credentials + * @and I click Save + * @then I should see a notification that my password has been updated + * @and I should be able to login with my new credentials. + */ +test('Password_change',{ tag: '@password',}, async ({page, browserName}, testInfo) => { + // Create instances and set variables + const registerPage = new RegisterPage(page); + const accountPage = new AccountPage(page); + const loginPage = new LoginPage(page); + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + let emailInputValue = `passwordupdate-${browserEngine}@example.com`; + let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; + let changedPasswordValue = process.env.MAGENTO_EXISTING_ACCOUNT_CHANGED_PASSWORD; + + if(!changedPasswordValue || !passwordInputValue) { + throw new Error("Changed password or original password in your .env file is not defined or could not be read."); + } -// if(await deleteAddressButton.isHidden()) { -// await page.goto(slugs.account.addressNewSlug); -// await accountPage.addNewAddress(); -// } -// await accountPage.deleteFirstAddressFromAddressBook(); -// }); + await test.step('Create a throwaway account', async () =>{ + await registerPage.createNewAccount(faker.person.firstName(), faker.person.lastName(), emailInputValue, passwordInputValue); + }); + await test.step('Update the password', async () =>{ + await page.goto(slugs.account.changePasswordSlug); + await page.waitForLoadState(); + await accountPage.updatePassword(passwordInputValue, changedPasswordValue); + }); + + await test.step('Login with updated password', async () =>{ + await loginPage.login(emailInputValue, changedPasswordValue); + await loginPage.logout(); + }); + +}); // // Before each test, log in From 850772aae693bec69d27566d4d3d830f10d67bb5 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 11:15:07 +0100 Subject: [PATCH 11/19] Rewrote account.spec.ts to use fixtures --- tests/base/account.spec.ts | 380 ++++++++----------------------------- 1 file changed, 83 insertions(+), 297 deletions(-) diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index 59dde76..f7f8c84 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -2,255 +2,17 @@ import {test, expect} from '@playwright/test'; import {faker} from '@faker-js/faker'; import {productTest} from './fixtures/fixtures'; -import {MainMenuPage} from './poms/mainmenu.page'; import {LoginPage} from './poms/login.page'; import {RegisterPage} from './poms/register.page'; import {AccountPage} from './poms/account.page'; import {NewsletterSubscriptionPage} from './poms/newsletter.page'; import slugs from './config/slugs.json'; -import inputvalues from './config/input-values/input-values.json'; import UIReference from './config/element-identifiers/element-identifiers.json'; -import outcomeMarker from './config/outcome-markers/outcome-markers.json'; - -/** - * @feature Newsletter subscriptions - * @scenario User (un)subscribes from a newsletter - * @given I am logged in - * @and I am on the account dashboard page - * @when I click on the newsletter link in the sidebar - * @then I should navigate to the newsletter subscription page - * @when I (un)check the subscription button - * @then I should see a message confirming my action - * @and My subscription option should be updated. - */ -productTest('Update_newsletter_subscription', {tag: ['@fixture', '@newsletter']}, async ({userPage, browserName}) => { - test.skip(browserName === 'webkit', '.click() does not work, will be fixed later'); - const newsLetterPage = new NewsletterSubscriptionPage(userPage.page); - await newsLetterPage.page.goto(slugs.account.newsLetterSlug); - - await newsLetterPage.updateNewsletterSubscription(); -}); - -/** - * @feature Add First Address to Account - * @scenario User adds a first address to their account - * @given I am logged in - * @and I am on the account dashboard page - * @when I go to the page where I can add my address - * @and I haven't added an address yet - * @when I fill in the required information - * @and I click the save button - * @then I should see a notification my address has been updated. - * @and The new address should be selected as default and shipping address - */ -productTest('Add_the_first_address', {tag: ['@fixture', '@address']}, async ({userPage}, testInfo) => { - const accountPage = new AccountPage(userPage.page); - await userPage.page.goto(slugs.account.accountOverviewSlug); - - let defaultBillingAddressSet = userPage.page.getByText(UIReference.accountDashboard.defaultAddressNotSetLabel); - - if(await defaultBillingAddressSet.isVisible()) { - // No default address set, so no address has been added yet. - await userPage.page.goto(slugs.account.addressNewSlug); - await accountPage.addNewAddress(); - } else { - testInfo.annotations.push({ type: 'Test skipped', description: `A default address has been set, adding a first address has already been done.`}); - test.skip(true,'Default billing address found.'); - } -}); - -/** - * @feature Add another address - * @given I am logged in - * @and I am on the account dashboard page - * @when I go to the page where I can add another address - * @when I fill in the required information - * @and I click the save button - * @then I should see a notification my address has been updated. - * @and The new address should be listed - */ -productTest('Add_another_address', {tag: ['@fixture', '@address']}, async ({userPage}) => { - await userPage.page.goto(slugs.account.addressNewSlug); - const accountPage = new AccountPage(userPage.page); - - await accountPage.addNewAddress(); -}); - -/** - * @feature Update Address in Account - * @scenario User updates an existing address to their account - * @given I am logged in - * @and I am on the account dashboard page - * @when I go to the page where I can see my address(es) - * @when I click on the button to edit the address - * @and I fill in the required information correctly - * @then I click the save button - * @then I should see a notification my address has been updated. - * @and The updated address should be visible in the addres book page. - */ -productTest('Update_existing_address', {tag: ['@fixture', '@address']}, async ({userPage}) => { - const accountPage = new AccountPage(userPage.page); - await userPage.page.goto(slugs.account.addressBookSlug); - let editAddressButton = userPage.page.getByRole('link', {name: UIReference.accountDashboard.editAddressIconButton}).first(); - if(await editAddressButton.isHidden()){ - // The edit address button was not found, add another address first. - await accountPage.addNewAddress(); - } - await accountPage.editExistingAddress(); -}); - -/** - * @feature Magento 2 Delete Address from account - * @scenario User removes an address from their account - * @given I am logged in - * @and I am on the account dashboard page - * @when I go to the page where I can see my address(es) - * @when I click the trash button for the address I want to delete - * @and I click the confirmation button - * @then I should see a notification my address has been deleted. - * @and The address should be removed from the overview. - */ -productTest('Delete_existing_address', {tag: ['@fixture', '@address']}, async ({userPage}) => { - const accountPage = new AccountPage(userPage.page); - await userPage.page.goto(slugs.account.addressBookSlug); - let deleteAddressButton = userPage.page.getByRole('link', {name: UIReference.accountDashboard.addressDeleteIconButton}).first(); - if(await deleteAddressButton.isHidden()) { - await userPage.page.goto(slugs.account.addressNewSlug); - await accountPage.addNewAddress(); - } - await accountPage.deleteFirstAddressFromAddressBook(); -}); - -/** - * @feature Magento 2 Change Password - * @scenario User changes their password - * @given I am logged in - * @and I am on the Account Dashboard page - * @when I navigate to the Account Information page - * @and I check the 'change password' option - * @when I fill in the new credentials - * @and I click Save - * @then I should see a notification that my password has been updated - * @and I should be able to login with my new credentials. - */ -test('Password_change',{ tag: '@password',}, async ({page, browserName}, testInfo) => { - // Create instances and set variables - const registerPage = new RegisterPage(page); - const accountPage = new AccountPage(page); - const loginPage = new LoginPage(page); - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - let emailInputValue = `passwordupdate-${browserEngine}@example.com`; - let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; - let changedPasswordValue = process.env.MAGENTO_EXISTING_ACCOUNT_CHANGED_PASSWORD; - - if(!changedPasswordValue || !passwordInputValue) { - throw new Error("Changed password or original password in your .env file is not defined or could not be read."); - } - - await test.step('Create a throwaway account', async () =>{ - await registerPage.createNewAccount(faker.person.firstName(), faker.person.lastName(), emailInputValue, passwordInputValue); - }); - - await test.step('Update the password', async () =>{ - await page.goto(slugs.account.changePasswordSlug); - await page.waitForLoadState(); - await accountPage.updatePassword(passwordInputValue, changedPasswordValue); - }); - - await test.step('Login with updated password', async () =>{ - await loginPage.login(emailInputValue, changedPasswordValue); - await loginPage.logout(); - }); - -}); - - -// // Before each test, log in -// test.beforeEach(async ({ page, browserName }) => { -// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; -// let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; -// let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; - -// if(!emailInputValue || !passwordInputValue) { -// throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); -// } - -// const loginPage = new LoginPage(page); -// await loginPage.login(emailInputValue, passwordInputValue); -// }); - -test.describe('Account information actions', {annotation: {type: 'Account Dashboard', description: 'Test for Account Information'},}, () => { - - test.beforeEach(async ({page}) => { - await page.goto(slugs.account.accountOverviewSlug); - await page.waitForLoadState(); - }); - - /** - * @feature Magento 2 Change Password - * @scenario User changes their password - * @given I am logged in - * @and I am on the Account Dashboard page - * @when I navigate to the Account Information page - * @and I check the 'change password' option - * @when I fill in the new credentials - * @and I click Save - * @then I should see a notification that my password has been updated - * @and I should be able to login with my new credentials. - */ - test('I can change my password',{ tag: '@account-credentials', }, async ({page, browserName}, testInfo) => { - - // Create instances and set variables - const mainMenu = new MainMenuPage(page); - const registerPage = new RegisterPage(page); - const accountPage = new AccountPage(page); - const loginPage = new LoginPage(page); - - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - let randomNumberforEmail = Math.floor(Math.random() * 101); - let emailPasswordUpdatevalue = `passwordupdate-${randomNumberforEmail}-${browserEngine}@example.com`; - let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; - let changedPasswordValue = process.env.MAGENTO_EXISTING_ACCOUNT_CHANGED_PASSWORD; - - // Log out of current account - if(await page.getByRole('link', { name: UIReference.mainMenu.myAccountLogoutItem }).isVisible()){ - await mainMenu.logout(); - } - - // Create account - if(!changedPasswordValue || !passwordInputValue) { - throw new Error("Changed password or original password in your .env file is not defined or could not be read."); - } - - await registerPage.createNewAccount(inputvalues.accountCreation.firstNameValue, inputvalues.accountCreation.lastNameValue, emailPasswordUpdatevalue, passwordInputValue); - - // Update password - await page.goto(slugs.account.changePasswordSlug); - await page.waitForLoadState(); - await accountPage.updatePassword(passwordInputValue, changedPasswordValue); - - // If login with changePasswordValue is possible, then password change was succesful. - await loginPage.login(emailPasswordUpdatevalue, changedPasswordValue); - - // Logout again, login with original account - await mainMenu.logout(); - let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; - if(!emailInputValue) { - throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); - } - await loginPage.login(emailInputValue, passwordInputValue); - }); -}); - -test.describe.serial('Account address book actions', { annotation: {type: 'Account Dashboard', description: 'Tests for the Address Book'},}, () => { - test.beforeEach(async ({page}) => { - await page.goto(slugs.account.addressBookSlug); - await page.waitForLoadState(); - }); +test.describe('Address actions', () => { /** - * @feature Magento 2 Add First Address to Account + * @feature Add First Address to Account * @scenario User adds a first address to their account * @given I am logged in * @and I am on the account dashboard page @@ -261,21 +23,24 @@ test.describe.serial('Account address book actions', { annotation: {type: 'Accou * @then I should see a notification my address has been updated. * @and The new address should be selected as default and shipping address */ + productTest('Add_the_first_address', {tag: ['@address']}, async ({userPage}, testInfo) => { + const accountPage = new AccountPage(userPage.page); + await userPage.page.goto(slugs.account.accountOverviewSlug); - test('I can add my first address',{ tag: '@address-actions', }, async ({page}, testInfo) => { - const accountPage = new AccountPage(page); - let addNewAddressTitle = page.getByRole('heading', {level: 1, name: UIReference.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); - } + let defaultBillingAddressSet = userPage.page.getByText(UIReference.accountDashboard.defaultAddressNotSetLabel); - await accountPage.addNewAddress(); + if(await defaultBillingAddressSet.isVisible()) { + // No default address set, so no address has been added yet. + await userPage.page.goto(slugs.account.addressNewSlug); + await accountPage.addNewAddress(); + } else { + testInfo.annotations.push({ type: 'Test skipped', description: `A default address has been set, adding a first address has already been done.`}); + test.skip(true,'Default billing address found.'); + } }); /** + * @feature Add another address * @given I am logged in * @and I am on the account dashboard page * @when I go to the page where I can add another address @@ -284,15 +49,15 @@ test.describe.serial('Account address book actions', { annotation: {type: 'Accou * @then I should see a notification my address has been updated. * @and The new address should be listed */ - test('I can add another address',{ tag: '@address-actions', }, async ({page}) => { - await page.goto(slugs.account.addressNewSlug); - const accountPage = new AccountPage(page); + productTest('Add_another_address', {tag: ['@address']}, async ({userPage}) => { + await userPage.page.goto(slugs.account.addressNewSlug); + const accountPage = new AccountPage(userPage.page); await accountPage.addNewAddress(); }); /** - * @feature Magento 2 Update Address in Account + * @feature Update Address in Account * @scenario User updates an existing address to their account * @given I am logged in * @and I am on the account dashboard page @@ -303,17 +68,14 @@ test.describe.serial('Account address book actions', { annotation: {type: 'Accou * @then I should see a notification my address has been updated. * @and The updated address should be visible in the addres book page. */ - test('I can edit an existing address',{ tag: '@address-actions', }, async ({page}) => { - const accountPage = new AccountPage(page); - await page.goto(slugs.account.addressNewSlug); - let editAddressButton = page.getByRole('link', {name: UIReference.accountDashboard.editAddressIconButton}).first(); - + productTest('Update_existing_address', {tag: ['@address']}, async ({userPage}) => { + const accountPage = new AccountPage(userPage.page); + await userPage.page.goto(slugs.account.addressBookSlug); + let editAddressButton = userPage.page.getByRole('link', {name: UIReference.accountDashboard.editAddressIconButton}).first(); if(await editAddressButton.isHidden()){ // The edit address button was not found, add another address first. await accountPage.addNewAddress(); } - - await page.goto(slugs.account.addressBookSlug); await accountPage.editExistingAddress(); }); @@ -328,52 +90,76 @@ test.describe.serial('Account address book actions', { annotation: {type: 'Accou * @then I should see a notification my address has been deleted. * @and The address should be removed from the overview. */ - test('I can delete an address',{ tag: '@address-actions', }, async ({page}, testInfo) => { - const accountPage = new AccountPage(page); - - let deleteAddressButton = page.getByRole('link', {name: UIReference.accountDashboard.addressDeleteIconButton}).first(); - + productTest('Delete_existing_address', {tag: ['@address']}, async ({userPage}) => { + const accountPage = new AccountPage(userPage.page); + await userPage.page.goto(slugs.account.addressBookSlug); + let deleteAddressButton = userPage.page.getByRole('link', {name: UIReference.accountDashboard.addressDeleteIconButton}).first(); if(await deleteAddressButton.isHidden()) { - await page.goto(slugs.account.addressNewSlug); + await userPage.page.goto(slugs.account.addressNewSlug); await accountPage.addNewAddress(); } await accountPage.deleteFirstAddressFromAddressBook(); }); }); -test.describe('Newsletter actions', { annotation: {type: 'Account Dashboard', description: 'Newsletter tests'},}, () => { - test.beforeEach(async ({page}) => { - await page.goto(slugs.account.accountOverviewSlug); - }); +/** + * @feature Magento 2 Change Password + * @scenario User changes their password + * @given I am logged in + * @and I am on the Account Dashboard page + * @when I navigate to the Account Information page + * @and I check the 'change password' option + * @when I fill in the new credentials + * @and I click Save + * @then I should see a notification that my password has been updated + * @and I should be able to login with my new credentials. + */ +test('Password_change',{ tag: '@password',}, async ({page, browserName}, testInfo) => { + // Create instances and set variables + const registerPage = new RegisterPage(page); + const accountPage = new AccountPage(page); + const loginPage = new LoginPage(page); + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + let emailInputValue = `passwordupdate-${browserEngine}@example.com`; + let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; + let changedPasswordValue = process.env.MAGENTO_EXISTING_ACCOUNT_CHANGED_PASSWORD; - // /** - // * @feature Magento 2 newsletter subscriptions - // * @scenario User (un)subscribes from a newsletter - // * @given I am logged in - // * @and I am on the account dashboard page - // * @when I click on the newsletter link in the sidebar - // * @then I should navigate to the newsletter subscription page - // * @when I (un)check the subscription button - // * @then I should see a message confirming my action - // * @and My subscription option should be updated. - // */ - // test('I can update my newsletter subscription',{ tag: '@newsletter-actions', }, async ({page, browserName}) => { - // test.skip(browserName === 'webkit', '.click() does not work, still searching for a workaround'); - // const newsletterPage = new NewsletterSubscriptionPage(page); - // let newsletterLink = page.getByRole('link', { name: UIReference.accountDashboard.links.newsletterLink }); - // const newsletterCheckElement = page.getByLabel(UIReference.newsletterSubscriptions.generalSubscriptionCheckLabel); + if(!changedPasswordValue || !passwordInputValue) { + throw new Error("Changed password or original password in your .env file is not defined or could not be read."); + } - // await newsletterLink.click(); - // await expect(page.getByText(outcomeMarker.account.newsletterSubscriptionTitle, { exact: true })).toBeVisible(); + await test.step('Create a throwaway account', async () =>{ + await registerPage.createNewAccount(faker.person.firstName(), faker.person.lastName(), emailInputValue, passwordInputValue); + }); - // let updateSubscription = await newsletterPage.updateNewsletterSubscription(); + await test.step('Update the password', async () =>{ + await page.goto(slugs.account.changePasswordSlug); + await page.waitForLoadState(); + await accountPage.updatePassword(passwordInputValue, changedPasswordValue); + }); - // await newsletterLink.click(); + await test.step('Login with updated password', async () =>{ + await loginPage.login(emailInputValue, changedPasswordValue); + await loginPage.logout(); + }); - // if(updateSubscription){ - // await expect(newsletterCheckElement).toBeChecked(); - // } else { - // await expect(newsletterCheckElement).not.toBeChecked(); - // } - // }); }); + +/** + * @feature Newsletter subscriptions + * @scenario User (un)subscribes from a newsletter + * @given I am logged in + * @and I am on the account dashboard page + * @when I click on the newsletter link in the sidebar + * @then I should navigate to the newsletter subscription page + * @when I (un)check the subscription button + * @then I should see a message confirming my action + * @and My subscription option should be updated. + */ +productTest('Update_newsletter_subscription', {tag: ['@newsletter']}, async ({userPage, browserName}) => { + test.skip(browserName === 'webkit', '.click() does not work, will be fixed later'); + const newsLetterPage = new NewsletterSubscriptionPage(userPage.page); + await newsLetterPage.page.goto(slugs.account.newsLetterSlug); + + await newsLetterPage.updateNewsletterSubscription(); +}); \ No newline at end of file From 649edcdec6870e661f19b79d574bea87182d30c2 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 15:12:11 +0100 Subject: [PATCH 12/19] Rewrote test 'Address_is_filled_in_checkout' to utilize fixtures --- tests/base/checkout.spec.ts | 42 +++++- tests/base/shoppingcart.spec.ts | 254 +------------------------------- 2 files changed, 36 insertions(+), 260 deletions(-) diff --git a/tests/base/checkout.spec.ts b/tests/base/checkout.spec.ts index c74695f..3854eba 100644 --- a/tests/base/checkout.spec.ts +++ b/tests/base/checkout.spec.ts @@ -1,12 +1,40 @@ import {test, expect} from '@playwright/test'; +import {productTest} from './fixtures/fixtures'; import {LoginPage} from './poms/login.page'; -import {ProductPage} from './poms/product.page'; import {AccountPage} from './poms/account.page'; import { CheckoutPage } from './poms/checkout.page'; import slugs from './config/slugs.json'; import UIReference from './config/element-identifiers/element-identifiers.json'; +/** + * @feature User address data automatically filled in checkout + * @scenario When the user navigates to the checkout (with a product), their name and address should be filled in. + * @given I am logged in + * @and I have a product in my cart + * @and I have navigated to the checkout page + * @then My name and address should already be filled in + */ +productTest('Address_is_filled_in_checkout', {tag: '@checkout'}, async ({userProductPage}) => { + await userProductPage.page.goto(slugs.checkoutSlug); + let addressField = userProductPage.page.getByLabel(UIReference.newAddress.streetAddressLabel); + + if(await addressField.isVisible()) { + // Add an address field is visible + await userProductPage.page.goto(slugs.account.addressBookSlug); + // Check if the button 'Change billing address' is visible. + if(await userProductPage.page.getByRole('link', { name: 'Change Billing Address arrow-' }).isVisible()) { + throw new Error(`Address has seemingly been added, but is not filled in.`); + } else { + const accountPage = new AccountPage(userProductPage.page); + await accountPage.addNewAddress(); + await userProductPage.page.goto(slugs.checkoutSlug); + } + } + + let shippingRadioButton = userProductPage.page.locator(UIReference.checkout.shippingAddressRadioLocator).first(); + await expect(shippingRadioButton, 'Radio button to select address should be visible').toBeVisible(); +}); /** * @feature BeforeEach runs before each test in this group. @@ -19,13 +47,13 @@ import UIReference from './config/element-identifiers/element-identifiers.json'; * @then the checkout page should be shown * @and I should see the product in the minicart */ -test.beforeEach(async ({ page }) => { - const productPage = new ProductPage(page); +// test.beforeEach(async ({ page }) => { +// const productPage = new ProductPage(page); - await page.goto(slugs.productpage.simpleProductSlug); - await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); - await page.goto(slugs.checkoutSlug); -}); +// await page.goto(slugs.productpage.simpleProductSlug); +// await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); +// await page.goto(slugs.checkoutSlug); +// }); test.describe('Checkout (login required)', () => { diff --git a/tests/base/shoppingcart.spec.ts b/tests/base/shoppingcart.spec.ts index b55dcdd..cd1a35f 100644 --- a/tests/base/shoppingcart.spec.ts +++ b/tests/base/shoppingcart.spec.ts @@ -86,7 +86,6 @@ test.describe('Product in cart tests', {tag: '@cart-product-group'}, () => { await page.goto(slugs.cartSlug); await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.simpleProductTitle }),`${UIReference.productPage.simpleProductTitle} should still be in cart`).toBeVisible(); - await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.secondSimpleProducTitle }),`${UIReference.productPage.secondSimpleProducTitle} should still be in cart`).toBeVisible(); }); }); // End of product in cart group @@ -238,255 +237,4 @@ test.describe('Price checking tests', {tag: '@cart-price-group'}, () => { }); }); -}); // End of price checking group - - - -// test.describe('Cart functionalities (guest)', () => { -// /** -// * @feature BeforeEach runs before each test in this group. -// * @scenario Add a product to the cart and confirm it's there. -// * @given I am on any page -// * @when I navigate to a (simple) product page -// * @and I add it to my cart -// * @then I should see a notification -// * @when I click the cart in the main menu -// * @then the minicart should become visible -// * @and I should see the product in the minicart -// */ -// test.beforeEach(async ({ page }) => { -// const productPage = new ProductPage(page); -// await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); -// // await mainMenu.openMiniCart(); -// // await expect(page.getByText(outcomeMarker.miniCart.simpleProductInCartTitle)).toBeVisible(); -// await page.goto(slugs.cartSlug); -// }); - -// /** -// * @feature Product can be added to cart -// * @scenario User adds a product to their cart -// * @given I have added a product to my cart -// * @and I am on the cart page -// * @then I should see the name of the product in my cart -// */ -// test('Product can be added to cart',{ tag: '@cart',}, async ({page}) => { -// await expect(page.getByRole('strong').getByRole('link', {name: UIReference.productPage.simpleProductTitle}), `Product is visible in cart`).toBeVisible(); -// }); - -// /** -// * @feature Product permanence after login -// * @scenario A product added to the cart should still be there after user has logged in -// * @given I have a product in my cart -// * @when I log in -// * @then I should still have that product in my cart -// */ -// test('Product should remain in cart after logging in',{ tag: ['@cart', '@account']}, async ({page, browserName}) => { -// await test.step('Add another product to cart', async () =>{ -// const productpage = new ProductPage(page); -// await page.goto(slugs.productpage.secondSimpleProductSlug); -// await productpage.addSimpleProductToCart(UIReference.productPage.secondSimpleProducTitle, slugs.productpage.secondSimpleProductSlug); -// }); - -// await test.step('Log in with account', async () =>{ -// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; -// const loginPage = new LoginPage(page); -// let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; -// let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; - -// if(!emailInputValue || !passwordInputValue) { -// throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); -// } - -// await loginPage.login(emailInputValue, passwordInputValue); -// }); - -// await page.goto(slugs.cartSlug); -// await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.simpleProductTitle }),`${UIReference.productPage.simpleProductTitle} should still be in cart`).toBeVisible(); -// await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.secondSimpleProducTitle }),`${UIReference.productPage.secondSimpleProducTitle} should still be in cart`).toBeVisible(); -// }); - -// /** -// * @feature Remove product from cart -// * @scenario User has added a product and wants to remove it from the cart page -// * @given I have added a product to my cart -// * @and I am on the cart page -// * @when I click the delete button -// * @then I should see a notification that the product has been removed from my cart -// * @and I should no longer see the product in my cart -// */ -// test('Remove product from cart',{ tag: '@cart',}, async ({page}) => { -// const cart = new CartPage(page); -// await cart.removeProduct(UIReference.productPage.simpleProductTitle); -// }); - -// /** -// * @feature Change quantity of products in cart -// * @scenario User has added a product and changes the quantity -// * @given I have a product in my cart -// * @and I am on the cart page -// * @when I change the quantity of the product -// * @and I click the update button -// * @then the quantity field should have the new amount -// * @and the subtotal/grand total should update -// */ -// test('Change quantity of products in cart',{ tag: '@cart',}, async ({page}) => { -// const cart = new CartPage(page); -// await cart.changeProductQuantity('2'); -// }); - -// /** -// * @feature Discount Code -// * @scenario User adds a discount code to their cart -// * @given I have a product in my cart -// * @and I am on my cart page -// * @when I click on the 'add discount code' button -// * @then I fill in a code -// * @and I click on 'apply code' -// * @then I should see a confirmation that my code has been added -// * @and the code should be visible in the cart -// * @and a discount should be applied to the product -// */ -// test('Add coupon code in cart',{ tag: ['@cart', '@coupon-code']}, async ({page, browserName}) => { -// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; -// const cart = new CartPage(page); -// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - -// if(!discountCode) { -// throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); -// } - -// await cart.applyDiscountCode(discountCode); -// }); - -// /** -// * @feature Remove discount code from cart -// * @scenario User has added a discount code, then removes it -// * @given I have a product in my cart -// * @and I am on my cart page -// * @when I add a discount code -// * @then I should see a notification -// * @and the code should be visible in the cart -// * @and a discount should be applied to a product -// * @when I click the 'cancel coupon' button -// * @then I should see a notification the discount has been removed -// * @and the discount should no longer be visible. -// */ -// test('Remove coupon code from cart',{ tag: ['@cart', '@coupon-code'] }, async ({page, browserName}) => { -// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; -// const cart = new CartPage(page); -// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - -// if(!discountCode) { -// throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); -// } - -// await cart.applyDiscountCode(discountCode); -// await cart.removeDiscountCode(); -// }); - -// /** -// * @feature Incorrect discount code check -// * @scenario The user provides an incorrect discount code, the system should reflect that -// * @given I have a product in my cart -// * @and I am on the cart page -// * @when I enter a wrong discount code -// * @then I should get a notification that the code did not work. -// */ - -// test('Using an invalid coupon code should give an error',{ tag: ['@cart', '@coupon-code'] }, async ({page}) => { -// const cart = new CartPage(page); -// await cart.enterWrongCouponCode("Incorrect Coupon Code"); -// }); -// }) - -// test.describe('Price checking tests', () => { - -// // Test: Configurable Product Input check from PDP to checkout -// // test.step: add configurable product to cart, return priceOnPDP and productAmount as variables -// // test.step: call function retrieveCheckoutPrices() to go to checkout, retrieve values -// // test.step: call function compareRetrievedPrices() to compare price on PDP to price in checkout - -// /** -// * @feature Simple Product price/amount check from PDP to Checkout -// * @given none -// * @when I go to a (simple) product page -// * @and I add one or more to my cart -// * @when I go to the checkout -// * @then the amount of the product should be the same -// * @and the price in the checkout should equal the price of the product * the amount of the product -// */ -// test('Simple product input to cart is consistent from PDP to checkout',{ tag: '@cart-price-check',}, async ({page}) => { -// var productPagePrice: string; -// var productPageAmount: string; -// var checkoutProductDetails: string[]; - -// const cart = new CartPage(page); - -// await test.step('Step: Add simple product to cart', async () =>{ -// const productPage = new ProductPage(page); -// await page.goto(slugs.productpage.simpleProductSlug); -// // set quantity to 2 so we can see that the math works -// await page.getByLabel(UIReference.productPage.quantityFieldLabel).fill('2'); - -// productPagePrice = await page.locator(UIReference.productPage.simpleProductPrice).innerText(); -// productPageAmount = await page.getByLabel(UIReference.productPage.quantityFieldLabel).inputValue(); -// await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug, '2'); - -// }); - -// await test.step('Step: go to checkout, get values', async () =>{ -// await page.goto(slugs.checkoutSlug); -// await page.waitForLoadState(); - -// // returns productPriceInCheckout and productQuantityInCheckout -// checkoutProductDetails = await cart.getCheckoutValues(UIReference.productPage.simpleProductTitle, productPagePrice, productPageAmount); -// }); - -// await test.step('Step: Calculate and check expectations', async () =>{ -// await cart.calculateProductPricesAndCompare(productPagePrice, productPageAmount, checkoutProductDetails[0], checkoutProductDetails[1]); -// }); - -// }); - -// /** -// * @feature Configurable Product price/amount check from PDP to Checkout -// * @given none -// * @when I go to a (configurable) product page -// * @and I add one or more to my cart -// * @when I go to the checkout -// * @then the amount of the product should be the same -// * @and the price in the checkout should equal the price of the product * the amount of the product -// */ -// test('Configurable product input to cart is consistent from PDP to checkout',{ tag: '@cart-price-check',}, async ({page}) => { -// var productPagePrice: string; -// var productPageAmount: string; -// var checkoutProductDetails: string[]; - -// const cart = new CartPage(page); - -// await test.step('Step: Add configurable product to cart', async () =>{ -// const productPage = new ProductPage(page); -// await page.goto(slugs.productpage.configurableProductSlug); -// // set quantity to 2 so we can see that the math works -// await page.getByLabel('Quantity').fill('2'); - -// productPagePrice = await page.locator(UIReference.productPage.simpleProductPrice).innerText(); -// productPageAmount = await page.getByLabel(UIReference.productPage.quantityFieldLabel).inputValue(); -// await productPage.addConfigurableProductToCart(); - -// }); - -// await test.step('Step: go to checkout, get values', async () =>{ -// await page.goto(slugs.checkoutSlug); -// await page.waitForLoadState(); - -// // returns productPriceInCheckout and productQuantityInCheckout -// checkoutProductDetails = await cart.getCheckoutValues(UIReference.productPage.configurableProductTitle, productPagePrice, productPageAmount); -// }); - -// await test.step('Step: Calculate and check expectations', async () =>{ -// await cart.calculateProductPricesAndCompare(productPagePrice, productPageAmount, checkoutProductDetails[0], checkoutProductDetails[1]); -// }); - -// }); -// }); +}); // End of price checking group \ No newline at end of file From a0bed4d33a9c650fea2fa87b7d6194c3fc03943d Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 15:41:20 +0100 Subject: [PATCH 13/19] Reworked 'Place_order_for_simple_product' to include fixtures. Updated removeProduct() to first check if there actually is a product to remove --- tests/base/checkout.spec.ts | 25 ++++++++++++++++++- .../element-identifiers.json | 3 ++- tests/base/poms/shoppingcart.page.ts | 5 ++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/base/checkout.spec.ts b/tests/base/checkout.spec.ts index 3854eba..96d2b52 100644 --- a/tests/base/checkout.spec.ts +++ b/tests/base/checkout.spec.ts @@ -15,7 +15,7 @@ import UIReference from './config/element-identifiers/element-identifiers.json'; * @and I have navigated to the checkout page * @then My name and address should already be filled in */ -productTest('Address_is_filled_in_checkout', {tag: '@checkout'}, async ({userProductPage}) => { +productTest('Address_is_filled_in_at_checkout_for_user', {tag: '@checkout'}, async ({userProductPage}) => { await userProductPage.page.goto(slugs.checkoutSlug); let addressField = userProductPage.page.getByLabel(UIReference.newAddress.streetAddressLabel); @@ -36,6 +36,29 @@ productTest('Address_is_filled_in_checkout', {tag: '@checkout'}, async ({userPro await expect(shippingRadioButton, 'Radio button to select address should be visible').toBeVisible(); }); +/** + * @feature Place order for simple product + * @scenario User places an order for a simple product + * @given I have a product in my cart + * @and I am on any page + * @when I navigate to the checkout + * @and I fill in the required fields + * @and I click the button to place my order + * @then I should see a confirmation that my order has been placed + * @and a order number should be created and show to me + */ +productTest('Place_order_for_simple_product', {tag: '@checkout'}, async ({userProductPage}, testInfo) => { + const checkoutPage = new CheckoutPage(userProductPage.page); + let orderNumber = await checkoutPage.placeOrder(); + testInfo.annotations.push({ type: 'Order number', description: `${orderNumber}` }); +}); + +// test('Place order for simple product',{ tag: '@simple-product-order',}, async ({page}, testInfo) => { +// const checkoutPage = new CheckoutPage(page); +// let orderNumber = await checkoutPage.placeOrder(); +// testInfo.annotations.push({ type: 'Order number', description: `${orderNumber}` }); +// }); + /** * @feature BeforeEach runs before each test in this group. * @scenario Add product to the cart, confirm it's there, then move to checkout. diff --git a/tests/base/config/element-identifiers/element-identifiers.json b/tests/base/config/element-identifiers/element-identifiers.json index eafbead..45b7dc7 100644 --- a/tests/base/config/element-identifiers/element-identifiers.json +++ b/tests/base/config/element-identifiers/element-identifiers.json @@ -80,7 +80,8 @@ "discountInputFieldLabel": "Enter discount code", "applyDiscountButtonLabel": "Apply Discount", "cancelCouponButtonLabel": "Cancel Coupon", - "updateShoppingCartButtonLabel": "Update Shopping Cart" + "updateShoppingCartButtonLabel": "Update Shopping Cart", + "cartEmptyLabel": "You have no items in your shopping cart." }, "checkout": { "shippingMethodFixedLabel": "Fixed", diff --git a/tests/base/poms/shoppingcart.page.ts b/tests/base/poms/shoppingcart.page.ts index 33bf67e..a870c2c 100644 --- a/tests/base/poms/shoppingcart.page.ts +++ b/tests/base/poms/shoppingcart.page.ts @@ -53,6 +53,11 @@ export class CartPage { await this.page.goto(slugs.cartSlug); }; + // If cart is already empty, return + if(await this.page.getByText(UIReference.cart.cartEmptyLabel).isVisible()){ + return; + } + let removeButton = this.page.getByLabel(`${UIReference.general.removeLabel} ${productTitle}`); await removeButton.click(); await this.page.waitForLoadState(); From 522ca772de919068a45599c44564e176a91ff76a Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 15:57:42 +0100 Subject: [PATCH 14/19] Rewrite 'Remove_coupon_code_in_checkout' to utilize fixtures --- tests/base/checkout.spec.ts | 74 +++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/tests/base/checkout.spec.ts b/tests/base/checkout.spec.ts index 96d2b52..3a331fd 100644 --- a/tests/base/checkout.spec.ts +++ b/tests/base/checkout.spec.ts @@ -6,6 +6,7 @@ import { CheckoutPage } from './poms/checkout.page'; import slugs from './config/slugs.json'; import UIReference from './config/element-identifiers/element-identifiers.json'; +import outcomeMarker from './config/outcome-markers/outcome-markers.json'; /** * @feature User address data automatically filled in checkout @@ -53,12 +54,77 @@ productTest('Place_order_for_simple_product', {tag: '@checkout'}, async ({userPr testInfo.annotations.push({ type: 'Order number', description: `${orderNumber}` }); }); -// test('Place order for simple product',{ tag: '@simple-product-order',}, async ({page}, testInfo) => { -// const checkoutPage = new CheckoutPage(page); -// let orderNumber = await checkoutPage.placeOrder(); -// testInfo.annotations.push({ type: 'Order number', description: `${orderNumber}` }); +/** + * @feature Discount Code + * @scenario User adds a discount code to their cart + * @given I have a product in my cart + * @and I am on my cart page + * @when I click on the 'add discount code' button + * @then I fill in a code + * @and I click on 'apply code' + * @then I should see a confirmation that my code has been added + * @and the code should be visible in the cart + * @and a discount should be applied to the product + */ +productTest('Add_coupon_code_in_checkout', {tag: ['@checkout', '@coupon-code']}, async ({productPage, browserName}) => { + await productPage.page.goto(slugs.checkoutSlug); + const checkout = new CheckoutPage(productPage.page); + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + if(!discountCode) { + throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); + } + + await checkout.applyDiscountCodeCheckout(discountCode); +}); + +/** + * @feature Remove discount code from checkout + * @scenario User has added a discount code, then removes it + * @given I have a product in my cart + * @and I am on the checkout page + * @when I add a discount code + * @then I should see a notification + * @and the code should be visible in the cart + * @and a discount should be applied to a product + * @when I click the 'cancel coupon' button + * @then I should see a notification the discount has been removed + * @and the discount should no longer be visible. + */ +productTest('Remove_coupon_code_in_checkout', {tag: ['@checkout', '@coupon-code']}, async ({productPage, browserName}) => { + await productPage.page.goto(slugs.checkoutSlug); + const checkout = new CheckoutPage(productPage.page); + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; + let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + if(!discountCode) { + throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); + } + + // Check if coupon code has somehow been added already + if(await productPage.page.getByText(outcomeMarker.checkout.checkoutPriceReducedSymbol).isVisible()){ + await checkout.removeDiscountCode(); + } else { + await checkout.applyDiscountCodeCheckout(discountCode); + await checkout.removeDiscountCode(); + } +}); + +// test('Remove coupon code from checkout',{ tag: ['@checkout', '@coupon-code']}, async ({page, browserName}) => { +// const checkout = new CheckoutPage(page); +// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; +// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + +// if(!discountCode) { +// throw new Error(`MAGENTO_COUPON_CODE appears to not be set in .env file. Value reported: ${discountCode}`); +// } + +// await checkout.applyDiscountCodeCheckout(discountCode); +// await checkout.removeDiscountCode(); // }); + + + /** * @feature BeforeEach runs before each test in this group. * @scenario Add product to the cart, confirm it's there, then move to checkout. From f1d0c54bd88042de8f4dc0cc71a6a40c1ed018ed Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 4 Mar 2025 16:03:31 +0100 Subject: [PATCH 15/19] Rewrote 'Invalid_coupon_code_in_checkout_is_rejected' to utilize fixture. Renamed both tests to keep test names unique --- tests/base/checkout.spec.ts | 29 ++++++++++++++--------------- tests/base/shoppingcart.spec.ts | 2 +- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/tests/base/checkout.spec.ts b/tests/base/checkout.spec.ts index 3a331fd..f5dc7a6 100644 --- a/tests/base/checkout.spec.ts +++ b/tests/base/checkout.spec.ts @@ -109,21 +109,20 @@ productTest('Remove_coupon_code_in_checkout', {tag: ['@checkout', '@coupon-code' } }); -// test('Remove coupon code from checkout',{ tag: ['@checkout', '@coupon-code']}, async ({page, browserName}) => { -// const checkout = new CheckoutPage(page); -// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; -// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - -// if(!discountCode) { -// throw new Error(`MAGENTO_COUPON_CODE appears to not be set in .env file. Value reported: ${discountCode}`); -// } - -// await checkout.applyDiscountCodeCheckout(discountCode); -// await checkout.removeDiscountCode(); -// }); - - - +/** + * @feature Incorrect discount code check + * @scenario The user provides an incorrect discount code, the system should reflect that + * @given I have a product in my cart + * @and I am on the cart page + * @when I enter a wrong discount code + * @then I should get a notification that the code did not work. + */ +productTest('Invalid_coupon_code_in_checkout_is_rejected', {tag: ['@checkout', '@coupon-code']}, async ({productPage, browserName}) => { +// test('Using an invalid coupon code should give an error',{ tag: ['@checkout', '@coupon-code'] }, async ({page}) => { + await productPage.page.goto(slugs.checkoutSlug); + const checkout = new CheckoutPage(productPage.page); + await checkout.enterWrongCouponCode("incorrect discount code"); +}); /** * @feature BeforeEach runs before each test in this group. diff --git a/tests/base/shoppingcart.spec.ts b/tests/base/shoppingcart.spec.ts index cd1a35f..adf0664 100644 --- a/tests/base/shoppingcart.spec.ts +++ b/tests/base/shoppingcart.spec.ts @@ -147,7 +147,7 @@ test.describe('Coupon in cart tests', {tag: '@cart-coupon-group'}, () => { * @when I enter a wrong discount code * @then I should get a notification that the code did not work. */ - productTest('Invalid_coupon_code_is_rejected', {tag: ['@cart']}, async ({productPage}) => { + productTest('Invalid_coupon_code_in_cart_is_rejected', {tag: ['@cart']}, async ({productPage}) => { await productPage.page.goto(slugs.cartSlug); await productPage.cartPage.enterWrongCouponCode("Incorrect Coupon Code"); }); From a069d7e805ab15bfee8ac0f2c7e11be421f4db5c Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 11 Mar 2025 09:14:25 +0100 Subject: [PATCH 16/19] WIP: implementing fixtures --- tests/base/checkout.spec.ts | 284 +++++++++++++++++------------------- 1 file changed, 133 insertions(+), 151 deletions(-) diff --git a/tests/base/checkout.spec.ts b/tests/base/checkout.spec.ts index f5dc7a6..9aa5b42 100644 --- a/tests/base/checkout.spec.ts +++ b/tests/base/checkout.spec.ts @@ -124,161 +124,143 @@ productTest('Invalid_coupon_code_in_checkout_is_rejected', {tag: ['@checkout', ' await checkout.enterWrongCouponCode("incorrect discount code"); }); -/** - * @feature BeforeEach runs before each test in this group. - * @scenario Add product to the cart, confirm it's there, then move to checkout. - * @given I am on any page - * @when I navigate to a (simple) product page - * @and I add it to my cart - * @then I should see a notification - * @when I navigate to the checkout - * @then the checkout page should be shown - * @and I should see the product in the minicart - */ -// test.beforeEach(async ({ page }) => { -// const productPage = new ProductPage(page); - -// await page.goto(slugs.productpage.simpleProductSlug); -// await productPage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); -// await page.goto(slugs.checkoutSlug); -// }); -test.describe('Checkout (login required)', () => { - // Before each test, log in - test.beforeEach(async ({ page, browserName }) => { - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; - let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; +// test.describe('Checkout (login required)', () => { +// // Before each test, log in +// test.beforeEach(async ({ page, browserName }) => { +// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; +// let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; +// let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; - if(!emailInputValue || !passwordInputValue) { - throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); - } +// if(!emailInputValue || !passwordInputValue) { +// throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); +// } - const loginPage = new LoginPage(page); - await loginPage.login(emailInputValue, passwordInputValue); - await page.goto(slugs.checkoutSlug); - }); +// const loginPage = new LoginPage(page); +// await loginPage.login(emailInputValue, passwordInputValue); +// await page.goto(slugs.checkoutSlug); +// }); - /** - * @feature Automatically fill in certain data in checkout (if user is logged in) - * @scenario When the user navigates to the checkout (with a product), their name and address should be filled in. - * @given I am logged in - * @and I have a product in my cart - * @and I have navigated to the checkout page - * @then My name and address should already be filled in - */ - test('My address should be already filled in at the checkout',{ tag: '@checkout',}, async ({page}) => { - let signInLink = page.getByRole('link', { name: UIReference.credentials.loginButtonLabel }); - let addressField = page.getByLabel(UIReference.newAddress.streetAddressLabel); - let addressAlreadyAdded = false; - - if(await signInLink.isVisible()) { - throw new Error(`Sign in link found, user is not logged in. Please check the test setup.`); - } - - // name field should NOT be on the page - await expect(page.getByLabel(UIReference.personalInformation.firstNameLabel)).toBeHidden(); - - if(await addressField.isVisible()) { - if(!addressAlreadyAdded){ - // Address field is visible and addressalreadyAdded is not true, so we need to add an address to the account. - const accountPage = new AccountPage(page); - await accountPage.addNewAddress(); - } else { - throw new Error(`Address field is visible even though an address has been added to the account.`); - } - } - - // expect to see radio button to select existing address - let shippingRadioButton = page.locator(UIReference.checkout.shippingAddressRadioLocator).first(); - await expect(shippingRadioButton, 'Radio button to select address should be visible').toBeVisible(); - - }); - - - /** - * @feature Place order for simple product - * @scenario User places an order for a simple product - * @given I have a product in my cart - * @and I am on any page - * @when I navigate to the checkout - * @and I fill in the required fields - * @and I click the button to place my order - * @then I should see a confirmation that my order has been placed - * @and a order number should be created and show to me - */ - test('Place order for simple product',{ tag: '@simple-product-order',}, async ({page}, testInfo) => { - const checkoutPage = new CheckoutPage(page); - let orderNumber = await checkoutPage.placeOrder(); - testInfo.annotations.push({ type: 'Order number', description: `${orderNumber}` }); - }); -}); - -test.describe('Checkout (guest)', () => { - /** - * @feature Discount Code - * @scenario User adds a discount code to their cart - * @given I have a product in my cart - * @and I am on my cart page - * @when I click on the 'add discount code' button - * @then I fill in a code - * @and I click on 'apply code' - * @then I should see a confirmation that my code has been added - * @and the code should be visible in the cart - * @and a discount should be applied to the product - */ - test('Add coupon code in checkout',{ tag: ['@checkout', '@coupon-code']}, async ({page, browserName}) => { - const checkout = new CheckoutPage(page); - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - - if(!discountCode) { - throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); - } - - await checkout.applyDiscountCodeCheckout(discountCode); - }); - - /** - * @feature Remove discount code from checkout - * @scenario User has added a discount code, then removes it - * @given I have a product in my cart - * @and I am on the checkout page - * @when I add a discount code - * @then I should see a notification - * @and the code should be visible in the cart - * @and a discount should be applied to a product - * @when I click the 'cancel coupon' button - * @then I should see a notification the discount has been removed - * @and the discount should no longer be visible. - */ - - test('Remove coupon code from checkout',{ tag: ['@checkout', '@coupon-code']}, async ({page, browserName}) => { - const checkout = new CheckoutPage(page); - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - - if(!discountCode) { - throw new Error(`MAGENTO_COUPON_CODE appears to not be set in .env file. Value reported: ${discountCode}`); - } +// /** +// * @feature Automatically fill in certain data in checkout (if user is logged in) +// * @scenario When the user navigates to the checkout (with a product), their name and address should be filled in. +// * @given I am logged in +// * @and I have a product in my cart +// * @and I have navigated to the checkout page +// * @then My name and address should already be filled in +// */ +// test('My address should be already filled in at the checkout',{ tag: '@checkout',}, async ({page}) => { +// let signInLink = page.getByRole('link', { name: UIReference.credentials.loginButtonLabel }); +// let addressField = page.getByLabel(UIReference.newAddress.streetAddressLabel); +// let addressAlreadyAdded = false; + +// if(await signInLink.isVisible()) { +// throw new Error(`Sign in link found, user is not logged in. Please check the test setup.`); +// } + +// // name field should NOT be on the page +// await expect(page.getByLabel(UIReference.personalInformation.firstNameLabel)).toBeHidden(); + +// if(await addressField.isVisible()) { +// if(!addressAlreadyAdded){ +// // Address field is visible and addressalreadyAdded is not true, so we need to add an address to the account. +// const accountPage = new AccountPage(page); +// await accountPage.addNewAddress(); +// } else { +// throw new Error(`Address field is visible even though an address has been added to the account.`); +// } +// } + +// // expect to see radio button to select existing address +// let shippingRadioButton = page.locator(UIReference.checkout.shippingAddressRadioLocator).first(); +// await expect(shippingRadioButton, 'Radio button to select address should be visible').toBeVisible(); + +// }); + + +// /** +// * @feature Place order for simple product +// * @scenario User places an order for a simple product +// * @given I have a product in my cart +// * @and I am on any page +// * @when I navigate to the checkout +// * @and I fill in the required fields +// * @and I click the button to place my order +// * @then I should see a confirmation that my order has been placed +// * @and a order number should be created and show to me +// */ +// test('Place order for simple product',{ tag: '@simple-product-order',}, async ({page}, testInfo) => { +// const checkoutPage = new CheckoutPage(page); +// let orderNumber = await checkoutPage.placeOrder(); +// testInfo.annotations.push({ type: 'Order number', description: `${orderNumber}` }); +// }); +// }); - await checkout.applyDiscountCodeCheckout(discountCode); - await checkout.removeDiscountCode(); - }); - - /** - * @feature Incorrect discount code check - * @scenario The user provides an incorrect discount code, the system should reflect that - * @given I have a product in my cart - * @and I am on the cart page - * @when I enter a wrong discount code - * @then I should get a notification that the code did not work. - */ - - test('Using an invalid coupon code should give an error',{ tag: ['@checkout', '@coupon-code'] }, async ({page}) => { - const checkout = new CheckoutPage(page); - await checkout.enterWrongCouponCode("incorrect discount code"); - }); -}); +// test.describe('Checkout (guest)', () => { +// /** +// * @feature Discount Code +// * @scenario User adds a discount code to their cart +// * @given I have a product in my cart +// * @and I am on my cart page +// * @when I click on the 'add discount code' button +// * @then I fill in a code +// * @and I click on 'apply code' +// * @then I should see a confirmation that my code has been added +// * @and the code should be visible in the cart +// * @and a discount should be applied to the product +// */ +// test('Add coupon code in checkout',{ tag: ['@checkout', '@coupon-code']}, async ({page, browserName}) => { +// const checkout = new CheckoutPage(page); +// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; +// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + +// if(!discountCode) { +// throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); +// } + +// await checkout.applyDiscountCodeCheckout(discountCode); +// }); + +// /** +// * @feature Remove discount code from checkout +// * @scenario User has added a discount code, then removes it +// * @given I have a product in my cart +// * @and I am on the checkout page +// * @when I add a discount code +// * @then I should see a notification +// * @and the code should be visible in the cart +// * @and a discount should be applied to a product +// * @when I click the 'cancel coupon' button +// * @then I should see a notification the discount has been removed +// * @and the discount should no longer be visible. +// */ + +// test('Remove coupon code from checkout',{ tag: ['@checkout', '@coupon-code']}, async ({page, browserName}) => { +// const checkout = new CheckoutPage(page); +// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; +// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; + +// if(!discountCode) { +// throw new Error(`MAGENTO_COUPON_CODE appears to not be set in .env file. Value reported: ${discountCode}`); +// } + +// await checkout.applyDiscountCodeCheckout(discountCode); +// await checkout.removeDiscountCode(); +// }); + +// /** +// * @feature Incorrect discount code check +// * @scenario The user provides an incorrect discount code, the system should reflect that +// * @given I have a product in my cart +// * @and I am on the cart page +// * @when I enter a wrong discount code +// * @then I should get a notification that the code did not work. +// */ + +// test('Using an invalid coupon code should give an error',{ tag: ['@checkout', '@coupon-code'] }, async ({page}) => { +// const checkout = new CheckoutPage(page); +// await checkout.enterWrongCouponCode("incorrect discount code"); +// }); +// }); From 535c8178af0807fdc9b815f4d23cbe95c3ed899d Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 18 Mar 2025 10:05:48 +0100 Subject: [PATCH 17/19] updated checkout.spec.ts to utilize fixtures --- tests/base/checkout.spec.ts | 145 +------------------------------- tests/base/fixtures/fixtures.ts | 2 +- 2 files changed, 3 insertions(+), 144 deletions(-) diff --git a/tests/base/checkout.spec.ts b/tests/base/checkout.spec.ts index 9aa5b42..577ecd6 100644 --- a/tests/base/checkout.spec.ts +++ b/tests/base/checkout.spec.ts @@ -16,7 +16,7 @@ import outcomeMarker from './config/outcome-markers/outcome-markers.json'; * @and I have navigated to the checkout page * @then My name and address should already be filled in */ -productTest('Address_is_filled_in_at_checkout_for_user', {tag: '@checkout'}, async ({userProductPage}) => { +productTest('Address_is_filled_in_at_checkout_for_user', {tag: ['@checkout', '@user']}, async ({userProductPage}) => { await userProductPage.page.goto(slugs.checkoutSlug); let addressField = userProductPage.page.getByLabel(UIReference.newAddress.streetAddressLabel); @@ -48,7 +48,7 @@ productTest('Address_is_filled_in_at_checkout_for_user', {tag: '@checkout'}, asy * @then I should see a confirmation that my order has been placed * @and a order number should be created and show to me */ -productTest('Place_order_for_simple_product', {tag: '@checkout'}, async ({userProductPage}, testInfo) => { +productTest('User_place_order_for_simple_product', {tag: ['@checkout', '@user']}, async ({userProductPage, page}, testInfo) => { const checkoutPage = new CheckoutPage(userProductPage.page); let orderNumber = await checkoutPage.placeOrder(); testInfo.annotations.push({ type: 'Order number', description: `${orderNumber}` }); @@ -123,144 +123,3 @@ productTest('Invalid_coupon_code_in_checkout_is_rejected', {tag: ['@checkout', ' const checkout = new CheckoutPage(productPage.page); await checkout.enterWrongCouponCode("incorrect discount code"); }); - - - -// test.describe('Checkout (login required)', () => { -// // Before each test, log in -// test.beforeEach(async ({ page, browserName }) => { -// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; -// let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; -// let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; - -// if(!emailInputValue || !passwordInputValue) { -// throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); -// } - -// const loginPage = new LoginPage(page); -// await loginPage.login(emailInputValue, passwordInputValue); -// await page.goto(slugs.checkoutSlug); -// }); - -// /** -// * @feature Automatically fill in certain data in checkout (if user is logged in) -// * @scenario When the user navigates to the checkout (with a product), their name and address should be filled in. -// * @given I am logged in -// * @and I have a product in my cart -// * @and I have navigated to the checkout page -// * @then My name and address should already be filled in -// */ -// test('My address should be already filled in at the checkout',{ tag: '@checkout',}, async ({page}) => { -// let signInLink = page.getByRole('link', { name: UIReference.credentials.loginButtonLabel }); -// let addressField = page.getByLabel(UIReference.newAddress.streetAddressLabel); -// let addressAlreadyAdded = false; - -// if(await signInLink.isVisible()) { -// throw new Error(`Sign in link found, user is not logged in. Please check the test setup.`); -// } - -// // name field should NOT be on the page -// await expect(page.getByLabel(UIReference.personalInformation.firstNameLabel)).toBeHidden(); - -// if(await addressField.isVisible()) { -// if(!addressAlreadyAdded){ -// // Address field is visible and addressalreadyAdded is not true, so we need to add an address to the account. -// const accountPage = new AccountPage(page); -// await accountPage.addNewAddress(); -// } else { -// throw new Error(`Address field is visible even though an address has been added to the account.`); -// } -// } - -// // expect to see radio button to select existing address -// let shippingRadioButton = page.locator(UIReference.checkout.shippingAddressRadioLocator).first(); -// await expect(shippingRadioButton, 'Radio button to select address should be visible').toBeVisible(); - -// }); - - -// /** -// * @feature Place order for simple product -// * @scenario User places an order for a simple product -// * @given I have a product in my cart -// * @and I am on any page -// * @when I navigate to the checkout -// * @and I fill in the required fields -// * @and I click the button to place my order -// * @then I should see a confirmation that my order has been placed -// * @and a order number should be created and show to me -// */ -// test('Place order for simple product',{ tag: '@simple-product-order',}, async ({page}, testInfo) => { -// const checkoutPage = new CheckoutPage(page); -// let orderNumber = await checkoutPage.placeOrder(); -// testInfo.annotations.push({ type: 'Order number', description: `${orderNumber}` }); -// }); -// }); - -// test.describe('Checkout (guest)', () => { -// /** -// * @feature Discount Code -// * @scenario User adds a discount code to their cart -// * @given I have a product in my cart -// * @and I am on my cart page -// * @when I click on the 'add discount code' button -// * @then I fill in a code -// * @and I click on 'apply code' -// * @then I should see a confirmation that my code has been added -// * @and the code should be visible in the cart -// * @and a discount should be applied to the product -// */ -// test('Add coupon code in checkout',{ tag: ['@checkout', '@coupon-code']}, async ({page, browserName}) => { -// const checkout = new CheckoutPage(page); -// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; -// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - -// if(!discountCode) { -// throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`); -// } - -// await checkout.applyDiscountCodeCheckout(discountCode); -// }); - -// /** -// * @feature Remove discount code from checkout -// * @scenario User has added a discount code, then removes it -// * @given I have a product in my cart -// * @and I am on the checkout page -// * @when I add a discount code -// * @then I should see a notification -// * @and the code should be visible in the cart -// * @and a discount should be applied to a product -// * @when I click the 'cancel coupon' button -// * @then I should see a notification the discount has been removed -// * @and the discount should no longer be visible. -// */ - -// test('Remove coupon code from checkout',{ tag: ['@checkout', '@coupon-code']}, async ({page, browserName}) => { -// const checkout = new CheckoutPage(page); -// const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; -// let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`]; - -// if(!discountCode) { -// throw new Error(`MAGENTO_COUPON_CODE appears to not be set in .env file. Value reported: ${discountCode}`); -// } - -// await checkout.applyDiscountCodeCheckout(discountCode); -// await checkout.removeDiscountCode(); -// }); - -// /** -// * @feature Incorrect discount code check -// * @scenario The user provides an incorrect discount code, the system should reflect that -// * @given I have a product in my cart -// * @and I am on the cart page -// * @when I enter a wrong discount code -// * @then I should get a notification that the code did not work. -// */ - -// test('Using an invalid coupon code should give an error',{ tag: ['@checkout', '@coupon-code'] }, async ({page}) => { -// const checkout = new CheckoutPage(page); -// await checkout.enterWrongCouponCode("incorrect discount code"); -// }); -// }); - diff --git a/tests/base/fixtures/fixtures.ts b/tests/base/fixtures/fixtures.ts index ff589de..e0c8c33 100644 --- a/tests/base/fixtures/fixtures.ts +++ b/tests/base/fixtures/fixtures.ts @@ -56,7 +56,6 @@ export const productTest = base.extend({ const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; - if(!emailInputValue || !passwordInputValue) { throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); } @@ -70,6 +69,7 @@ export const productTest = base.extend({ const cartPage = new CartPage(page); const mainMenu = new MainMenuPage(page); await cartPage.removeProduct(UIReference.productPage.simpleProductTitle); + await cartPage.removeProduct(UIReference.productPage.configurableProductTitle); await mainMenu.logout(); } }); From fc604d901b8a992ec50d97717d943efea288c45e Mon Sep 17 00:00:00 2001 From: Shay Date: Mon, 24 Mar 2025 08:53:33 +0100 Subject: [PATCH 18/19] WIP: fixtures --- tests/base/checkout.spec.ts | 2 +- tests/base/config/element-identifiers/element-identifiers.json | 3 ++- tests/base/poms/newsletter.page.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/base/checkout.spec.ts b/tests/base/checkout.spec.ts index 577ecd6..951ec3a 100644 --- a/tests/base/checkout.spec.ts +++ b/tests/base/checkout.spec.ts @@ -24,7 +24,7 @@ productTest('Address_is_filled_in_at_checkout_for_user', {tag: ['@checkout', '@u // Add an address field is visible await userProductPage.page.goto(slugs.account.addressBookSlug); // Check if the button 'Change billing address' is visible. - if(await userProductPage.page.getByRole('link', { name: 'Change Billing Address arrow-' }).isVisible()) { + if(await userProductPage.page.getByRole('link', { name: UIReference.checkout.changeBillingAddressArrowLocator }).isVisible()) { throw new Error(`Address has seemingly been added, but is not filled in.`); } else { const accountPage = new AccountPage(userProductPage.page); diff --git a/tests/base/config/element-identifiers/element-identifiers.json b/tests/base/config/element-identifiers/element-identifiers.json index 45b7dc7..a3cc470 100644 --- a/tests/base/config/element-identifiers/element-identifiers.json +++ b/tests/base/config/element-identifiers/element-identifiers.json @@ -98,7 +98,8 @@ "openCartButtonLabelCont": "item", "openCartButtonLabelContMultiple": "items", "cartDetailsLocator": "#checkout-cart-details div", - "shippingAddressRadioLocator": "#shipping-details input[type='radio']" + "shippingAddressRadioLocator": "#shipping-details input[type='radio']", + "changeBillingAddressArrowLocator": "Change Billing Address arrow-" }, "miniCart": { "checkOutButtonLabel": "Checkout", diff --git a/tests/base/poms/newsletter.page.ts b/tests/base/poms/newsletter.page.ts index ff4b34c..4123862 100644 --- a/tests/base/poms/newsletter.page.ts +++ b/tests/base/poms/newsletter.page.ts @@ -38,7 +38,7 @@ export class NewsletterSubscriptionPage { await this.page.getByText(subscriptionUpdatedNotification).waitFor(); await expect(this.page.getByText(subscriptionUpdatedNotification)).toBeVisible(); - // Navigate to newsletter page, because saving the subscription refreshes to the account page + // Navigate to newsletter page, because saving the subscription redirects to the account page await this.page.goto(slugs.account.newsLetterSlug); if(subscribed){ From 23475a3d31337bb3e1f15889048f7077b6195579 Mon Sep 17 00:00:00 2001 From: Shay Date: Mon, 24 Mar 2025 10:56:03 +0100 Subject: [PATCH 19/19] Fixes based on code review --- tests/base/fixtures/fixtures.ts | 2 +- tests/base/shoppingcart.spec.ts | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/tests/base/fixtures/fixtures.ts b/tests/base/fixtures/fixtures.ts index e0c8c33..0264a5a 100644 --- a/tests/base/fixtures/fixtures.ts +++ b/tests/base/fixtures/fixtures.ts @@ -57,7 +57,7 @@ export const productTest = base.extend({ let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; if(!emailInputValue || !passwordInputValue) { - throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet."); + throw new Error(`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet.`); } await loginPage.login(emailInputValue, passwordInputValue); diff --git a/tests/base/shoppingcart.spec.ts b/tests/base/shoppingcart.spec.ts index adf0664..c94f0d4 100644 --- a/tests/base/shoppingcart.spec.ts +++ b/tests/base/shoppingcart.spec.ts @@ -64,16 +64,11 @@ test.describe('Product in cart tests', {tag: '@cart-product-group'}, () => { * @when I log in * @then I should still have that product in my cart */ - test('Product_remains_in_cart_after_login',{ tag: ['@cart', '@account']}, async ({page, browserName}) => { - await test.step('Add product to cart', async () =>{ - const productpage = new ProductPage(page); - await page.goto(slugs.productpage.simpleProductSlug); - await productpage.addSimpleProductToCart(UIReference.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug); - }); + productTest('Product_remains_in_cart_after_login',{ tag: ['@cart', '@account']}, async ({page, productPage, browserName}) => { + const loginPage = new LoginPage(productPage.page); await test.step('Log in with account', async () =>{ - const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; - const loginPage = new LoginPage(page); + const browserEngine = browserName?.toUpperCase() || "UNKNOWN"; let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`]; let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD; @@ -84,8 +79,16 @@ test.describe('Product in cart tests', {tag: '@cart-product-group'}, () => { await loginPage.login(emailInputValue, passwordInputValue); }); - await page.goto(slugs.cartSlug); - await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.simpleProductTitle }),`${UIReference.productPage.simpleProductTitle} should still be in cart`).toBeVisible(); + await test.step('Check product it still in cart', async () =>{ + await productPage.page.goto(slugs.cartSlug); + await expect(page.getByRole('strong').getByRole('link', { name: UIReference.productPage.simpleProductTitle }),`${UIReference.productPage.simpleProductTitle} should still be in cart`).toBeVisible(); + }); + + await test.step('Remove product, then Log out', async () =>{ + await productPage.cartPage.removeProduct(UIReference.productPage.simpleProductTitle); + await loginPage.logout(); + }); + }); }); // End of product in cart group