diff --git a/tests/base/account.spec.ts b/tests/base/account.spec.ts index fcc9949..f7f8c84 100644 --- a/tests/base/account.spec.ts +++ b/tests/base/account.spec.ts @@ -1,100 +1,18 @@ 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 {faker} from '@faker-js/faker'; +import {productTest} from './fixtures/fixtures'; + +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'; - -// 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 @@ -105,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 @@ -128,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 @@ -147,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(); }); @@ -172,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 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); +/** + * @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; - await newsletterLink.click(); - await expect(page.getByText(outcomeMarker.account.newsletterSubscriptionTitle, { exact: true })).toBeVisible(); + if(!changedPasswordValue || !passwordInputValue) { + throw new Error("Changed password or original password in your .env file is not defined or could not be read."); + } - let updateSubscription = await newsletterPage.updateNewsletterSubscription(); + await test.step('Create a throwaway account', async () =>{ + await registerPage.createNewAccount(faker.person.firstName(), faker.person.lastName(), emailInputValue, passwordInputValue); + }); - await newsletterLink.click(); + await test.step('Update the password', async () =>{ + await page.goto(slugs.account.changePasswordSlug); + await page.waitForLoadState(); + await accountPage.updatePassword(passwordInputValue, changedPasswordValue); + }); - if(updateSubscription){ - await expect(newsletterCheckElement).toBeChecked(); - } else { - await expect(newsletterCheckElement).not.toBeChecked(); - } + await test.step('Login with updated password', async () =>{ + await loginPage.login(emailInputValue, changedPasswordValue); + await loginPage.logout(); }); + }); + +/** + * @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 diff --git a/tests/base/checkout.spec.ts b/tests/base/checkout.spec.ts index 0cb876d..951ec3a 100644 --- a/tests/base/checkout.spec.ts +++ b/tests/base/checkout.spec.ts @@ -1,168 +1,125 @@ 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 {productTest} from './fixtures/fixtures'; +import {LoginPage} from './poms/login.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'; - +import outcomeMarker from './config/outcome-markers/outcome-markers.json'; /** - * @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 + * @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 */ -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; - - 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); +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); + + 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: UIReference.checkout.changeBillingAddressArrowLocator }).isVisible()) { + throw new Error(`Address has seemingly been added, but is not filled in.`); + } else { + const accountPage = new AccountPage(userProductPage.page); await accountPage.addNewAddress(); - } else { - throw new Error(`Address field is visible even though an address has been added to the account.`); - } + await userProductPage.page.goto(slugs.checkoutSlug); } + } - // 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}` }); - }); + let shippingRadioButton = userProductPage.page.locator(UIReference.checkout.shippingAddressRadioLocator).first(); + await expect(shippingRadioButton, 'Radio button to select address should be visible').toBeVisible(); }); -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}`]; +/** + * @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('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}` }); +}); - if(!discountCode) { - throw new Error(`MAGENTO_COUPON_CODE appears to not be set in .env file. Value reported: ${discountCode}`); - } +/** + * @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(); - }); - - /** - * @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"); - }); + } }); +/** + * @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"); +}); diff --git a/tests/base/config/element-identifiers/element-identifiers.json b/tests/base/config/element-identifiers/element-identifiers.json index 3e77c5d..a3cc470 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", @@ -79,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", @@ -96,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/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/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..0264a5a --- /dev/null +++ b/tests/base/fixtures/fixtures.ts @@ -0,0 +1,76 @@ +import {test as base} from '@playwright/test'; +import {ProductPage} from '../poms/product.page'; +import { CartPage } from '../poms/shoppingcart.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: any; + userPage: any; + userProductPage: any; +} + +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, cartPage, page}); + + // Teardown & Cleanup + 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); + 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, page}); + + // Teardown & Cleanup + 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(); + } +}); +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/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 66% rename from tests/base/fixtures/login.page.ts rename to tests/base/poms/login.page.ts index 06d0d7c..4ec15be 100644 --- a/tests/base/fixtures/login.page.ts +++ b/tests/base/poms/login.page.ts @@ -2,18 +2,23 @@ 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; 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 +30,10 @@ export class LoginPage { await expect(this.page.getByRole('link', { name: UIReference.mainMenu.myAccountLogoutItem })).toBeVisible(); } + + 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 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 73% rename from tests/base/fixtures/newsletter.page.ts rename to tests/base/poms/newsletter.page.ts index b557c1d..4123862 100644 --- a/tests/base/fixtures/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 redirects 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(); + } } } 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/fixtures/cart.page.ts b/tests/base/poms/shoppingcart.page.ts similarity index 89% rename from tests/base/fixtures/cart.page.ts rename to tests/base/poms/shoppingcart.page.ts index 9503f5c..a870c2c 100644 --- a/tests/base/fixtures/cart.page.ts +++ b/tests/base/poms/shoppingcart.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,21 @@ 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); + }; + + // 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(); @@ -54,6 +65,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; } // ============================================== @@ -84,9 +96,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/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; diff --git a/tests/base/cart.spec.ts b/tests/base/shoppingcart.spec.ts similarity index 66% rename from tests/base/cart.spec.ts rename to tests/base/shoppingcart.spec.ts index fb72aed..c94f0d4 100644 --- a/tests/base/cart.spec.ts +++ b/tests/base/shoppingcart.spec.ts @@ -1,42 +1,60 @@ 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/shoppingcart.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'; -test.describe('Cart functionalities (guest)', () => { +test.describe('Product in cart tests', {tag: '@cart-product-group'}, () => { /** - * @feature BeforeEach runs before each test in this group. - * @scenario Add a product to the cart and confirm it's there. + * @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 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 + * @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.beforeEach(async ({ page }) => { + 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 mainMenu.openMiniCart(); - // await expect(page.getByText(outcomeMarker.miniCart.simpleProductInCartTitle)).toBeVisible(); 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 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 + * @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('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(); + productTest('Change_quantity_of_simple_product_in_cart', {tag: '@cart'}, async ({productPage}) => { + await productPage.page.goto(slugs.cartSlug); + await productPage.cartPage.changeProductQuantity('2'); }); /** @@ -46,16 +64,11 @@ test.describe('Cart functionalities (guest)', () => { * @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); - }); + 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; @@ -66,42 +79,22 @@ test.describe('Cart functionalities (guest)', () => { 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(); - }); + 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(); + }); - /** - * @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); - }); + await test.step('Remove product, then Log out', async () =>{ + await productPage.cartPage.removeProduct(UIReference.productPage.simpleProductTitle); + await loginPage.logout(); + }); - /** - * @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'); }); +}); // End of product in cart group +test.describe('Coupon in cart tests', {tag: '@cart-coupon-group'}, () => { /** - * @feature Discount Code + * @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 @@ -112,16 +105,16 @@ test.describe('Cart functionalities (guest)', () => { * @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}) => { + productTest('Add_coupon_code_in_cart', {tag: ['@cart']}, async ({productPage, browserName}) => { + await productPage.page.goto(slugs.cartSlug); + 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 productPage.cartPage.applyDiscountCode(discountCode); }); /** @@ -137,17 +130,16 @@ test.describe('Cart functionalities (guest)', () => { * @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}) => { + productTest('Remove_coupon_code_from_cart', {tag: ['@cart']}, async ({productPage, browserName}) => { + await productPage.page.goto(slugs.cartSlug); 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(); + await productPage.cartPage.applyDiscountCode(discountCode); + await productPage.cartPage.removeDiscountCode(); }); /** @@ -158,20 +150,13 @@ test.describe('Cart functionalities (guest)', () => { * @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"); + 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"); }); -}) - -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 +}); // 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 @@ -181,7 +166,7 @@ test.describe('Price checking tests', () => { * @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}) => { + 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[]; @@ -223,7 +208,7 @@ test.describe('Price checking tests', () => { * @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}) => { + 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[]; @@ -255,4 +240,4 @@ test.describe('Price checking tests', () => { }); }); -}); +}); // End of price checking group \ No newline at end of file