From d1de7ca12120500bc6646e28ac68ae65083178b0 Mon Sep 17 00:00:00 2001 From: Marjan Petkovski <72909665+pmarjan-onestic@users.noreply.github.com> Date: Fri, 9 Aug 2024 10:26:21 +0200 Subject: [PATCH] Card, Stored Card, Paypal E2E tests for huva-default checkout (#113) * Hyva 6#: CC without 3ds * Hyva 6#: CC 3ds variations * Hyva 6#: Vault with and without 3ds * Hyva 6#: Move to directory hyva-default * Hyva 6#: Paypal --- projects/hyva-default/README.md | 12 +++ .../hyva-default/helpers/PaymentHelper.js | 22 ++++ .../hyva-default/helpers/ScenarioHelper.js | 83 ++++++++++++++ projects/hyva-default/magento.hyva.config.cjs | 77 +++++++++++++ .../checkout/CreditCardComponentsMagento.js | 22 ++++ .../checkout/SuccessfulCheckout.page.js | 13 +++ .../pageObjects/plugin/Base.page.js | 38 +++++++ .../pageObjects/plugin/Login.page.js | 23 ++++ .../pageObjects/plugin/PaymentDetails.page.js | 64 +++++++++++ .../pageObjects/plugin/ProductDetail.page.js | 52 +++++++++ .../plugin/ShippingDetails.page.js | 78 ++++++++++++++ .../pageObjects/plugin/ShoppingCart.page.js | 24 +++++ .../pageObjects/plugin/TopBar.page.js | 7 ++ .../tests/CreditCardPayment.spec.js | 84 +++++++++++++++ .../hyva-default/tests/PayPalPayment.spec.js | 44 ++++++++ .../tests/loggedIn/LoggedInUser.spec.js | 2 + .../tests/loggedIn/StoredCardPayment.js | 102 ++++++++++++++++++ 17 files changed, 747 insertions(+) create mode 100644 projects/hyva-default/README.md create mode 100644 projects/hyva-default/helpers/PaymentHelper.js create mode 100644 projects/hyva-default/helpers/ScenarioHelper.js create mode 100644 projects/hyva-default/magento.hyva.config.cjs create mode 100644 projects/hyva-default/pageObjects/checkout/CreditCardComponentsMagento.js create mode 100644 projects/hyva-default/pageObjects/checkout/SuccessfulCheckout.page.js create mode 100644 projects/hyva-default/pageObjects/plugin/Base.page.js create mode 100644 projects/hyva-default/pageObjects/plugin/Login.page.js create mode 100644 projects/hyva-default/pageObjects/plugin/PaymentDetails.page.js create mode 100644 projects/hyva-default/pageObjects/plugin/ProductDetail.page.js create mode 100644 projects/hyva-default/pageObjects/plugin/ShippingDetails.page.js create mode 100644 projects/hyva-default/pageObjects/plugin/ShoppingCart.page.js create mode 100644 projects/hyva-default/pageObjects/plugin/TopBar.page.js create mode 100644 projects/hyva-default/tests/CreditCardPayment.spec.js create mode 100644 projects/hyva-default/tests/PayPalPayment.spec.js create mode 100644 projects/hyva-default/tests/loggedIn/LoggedInUser.spec.js create mode 100644 projects/hyva-default/tests/loggedIn/StoredCardPayment.js diff --git a/projects/hyva-default/README.md b/projects/hyva-default/README.md new file mode 100644 index 00000000..cacc65d8 --- /dev/null +++ b/projects/hyva-default/README.md @@ -0,0 +1,12 @@ +## Tests scope: Hyva Default + +These tests are based on the `Hyva Default` theme. + +More importantly, they are based on the Checkout option/variation called `Hyva Default`. + +This checkout option splits the checkout processes into two steps: shipping and billing. +This means, somewhere in the tests, after filling in shipping info, we are expecting to locate and click a button that roughly suggests that we need to proceed to the next page. + +For comparison, selecting `Hyva One Page` yield a view where there both shipping and billing segments appear, as the name suggests, on one page. +This view will render these tests invalid. + diff --git a/projects/hyva-default/helpers/PaymentHelper.js b/projects/hyva-default/helpers/PaymentHelper.js new file mode 100644 index 00000000..85b783c1 --- /dev/null +++ b/projects/hyva-default/helpers/PaymentHelper.js @@ -0,0 +1,22 @@ +import { PaymentDetailsPage } from "../pageObjects/plugin/PaymentDetails.page.js"; +import { placeOrder } from "./ScenarioHelper.js"; + +export async function makeCreditCardPayment( + page, + user, + creditCardNumber, + expDate, + cvc +) { + const paymentDetailPage = new PaymentDetailsPage(page); + const creditCardSection = await paymentDetailPage.selectCreditCard(); + await creditCardSection.fillCreditCardInfo( + user.firstName, + user.lastName, + creditCardNumber, + expDate, + cvc + ); + + await placeOrder(page); +} diff --git a/projects/hyva-default/helpers/ScenarioHelper.js b/projects/hyva-default/helpers/ScenarioHelper.js new file mode 100644 index 00000000..6ce7d768 --- /dev/null +++ b/projects/hyva-default/helpers/ScenarioHelper.js @@ -0,0 +1,83 @@ +import { expect } from "@playwright/test"; +import { ProductDetailsPage } from "../pageObjects/plugin/ProductDetail.page.js"; +import { ShippingDetails } from "../pageObjects/plugin/ShippingDetails.page.js"; +import { SuccessfulCheckoutPage } from "../pageObjects/checkout/SuccessfulCheckout.page.js"; +import { ShoppingCartPage } from "../pageObjects/plugin/ShoppingCart.page.js"; +import { LoginPage } from "../pageObjects/plugin/Login.page.js"; + +export async function goToShippingWithFullCart(page, additionalItemCount = 0, itemURL="joust-duffle-bag.html") { + const productDetailsPage = new ProductDetailsPage(page); + await productDetailsPage.addItemToCart(itemURL); + + if (additionalItemCount >= 1) { + await productDetailsPage.addItemWithOptionsToCart( + "breathe-easy-tank.html", + "M", + additionalItemCount + ); + } + + await page.waitForLoadState("networkidle", { timeout: 20000 }); +} + +export async function loginAs(page, user) { + const loginPage = new LoginPage(page); + await loginPage.goTo(); + await loginPage.login(user); +} + +export async function proceedToPaymentAs(page, user, isGuest = true) { + const shippingDetailsPage = new ShippingDetails(page); + await shippingDetailsPage.goTo(); + + isGuest?await shippingDetailsPage.fillShippingDetailsAndProceedToPayment(user) + :await shippingDetailsPage.proceedToPaymentWithSavedAddress(); +} + +/* This method should only be used for cases where the user should fill the billing +details only on payment page; e.g. For virtual products */ +export async function fillBillingAddress(page, user){ + await new ShippingDetails(page, page.locator(".payment-method._active")) + .fillShippingDetails(user, false); + await page.getByRole('button', { name: 'Update' }).click(); +} + +export async function verifySuccessfulPayment(page, redirect = true, timeout) { + const successfulCheckoutPage = new SuccessfulCheckoutPage(page); + if (redirect !== false) { + await successfulCheckoutPage.waitForRedirection(timeout); + } + expect(await successfulCheckoutPage.pageTitle.innerText()).toContain( + "Thank you for your purchase!" + ); +} + +export async function getOrderNumber(page){ + return (await new SuccessfulCheckoutPage(page).orderNumber()); +} + + +export async function verifyVoucherCouponGeneration(page) { + const successfulCheckoutPage = new SuccessfulCheckoutPage(page); + await expect(successfulCheckoutPage.voucherCodeContainer).toBeVisible(); +} + +export async function verifyFailedPayment(page) { + const errorMessage = await new ShoppingCartPage( + page + ).errorMessage.innerText(); + expect(errorMessage).toContain( + "Your payment failed, Please try again later" + ); +} + +export async function placeOrder(page) { + const placeOrderButton = page.locator("button[x-bind='buttonPlaceOrder()']").last(); + + await placeOrderButton.click({timeout: 10000}); +} + +export async function proceedToPaymentWithoutShipping(page) { + await page.goto("/checkout#payment"); + await new AnimationHelper(page).waitForAnimation(); +} diff --git a/projects/hyva-default/magento.hyva.config.cjs b/projects/hyva-default/magento.hyva.config.cjs new file mode 100644 index 00000000..8c131dc0 --- /dev/null +++ b/projects/hyva-default/magento.hyva.config.cjs @@ -0,0 +1,77 @@ +// @ts-check +const { devices } = require("@playwright/test"); +const dotenv = require('dotenv'); +const VIEWPORT_WIDTH = 1600; +const VIEWPORT_HEIGHT = 900; + +dotenv.config(); +/** + * @see https://playwright.dev/docs/test-configuration + * @type {import('@playwright/test').PlaywrightTestConfig} + */ +const config = { + testDir: "./tests/", + + /* Maximum time one test can run for. */ + timeout: 120 * 1000, + + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 20000, + }, + + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + + /* Retry on CI only */ + retries: 0, + + /* Opt out of parallel tests on CI. */ + workers: 3, + + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: "html", + + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 20000, + + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: process.env.MAGENTO_BASE_URL, + ignoreHTTPSErrors: true, + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + }, + + /* Configure projects for major browsers */ + projects: [ + + { + name: "userHyva", + testDir: "./tests/", + testIgnore: [], + use: { + browserName: "chromium", + trace: "retain-on-failure", + viewport: { + width: VIEWPORT_WIDTH, + height: VIEWPORT_HEIGHT, + }, + } + }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + outputDir: "test-results/", + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // port: 3000, + // }, +}; +module.exports = config; diff --git a/projects/hyva-default/pageObjects/checkout/CreditCardComponentsMagento.js b/projects/hyva-default/pageObjects/checkout/CreditCardComponentsMagento.js new file mode 100644 index 00000000..6aedeb12 --- /dev/null +++ b/projects/hyva-default/pageObjects/checkout/CreditCardComponentsMagento.js @@ -0,0 +1,22 @@ +import { expect } from "@playwright/test"; +import { CreditCardComponents } from "../../../common/checkoutComponents/CreditCardComponents.js"; +export class CreditCardComponentsMagento extends CreditCardComponents { + constructor(page) { + super(page); + this.page = page; + + this.errorMessage = page.locator("#CreditCardActionContainerMessageContainer"); + } + + async verifyPaymentRefusal() { + expect(await this.errorMessage.innerText()).toContain( + "The Payment is Refused" + ); + } + + async verifyPaymentCancellation() { + expect(await this.errorMessage.innerText()).toContain( + "Payment has been cancelled" + ); + } +} diff --git a/projects/hyva-default/pageObjects/checkout/SuccessfulCheckout.page.js b/projects/hyva-default/pageObjects/checkout/SuccessfulCheckout.page.js new file mode 100644 index 00000000..c614155d --- /dev/null +++ b/projects/hyva-default/pageObjects/checkout/SuccessfulCheckout.page.js @@ -0,0 +1,13 @@ +export class SuccessfulCheckoutPage { + constructor(page) { + this.page = page; + this.pageTitle = page.locator("span[data-ui-id='page-title-wrapper']"); + } + + async waitForRedirection(timeout = 15000) { + await this.page.waitForNavigation({ + url: / *\/onepage\/success/, + timeout: timeout, + }); + } +} diff --git a/projects/hyva-default/pageObjects/plugin/Base.page.js b/projects/hyva-default/pageObjects/plugin/Base.page.js new file mode 100644 index 00000000..248ac160 --- /dev/null +++ b/projects/hyva-default/pageObjects/plugin/Base.page.js @@ -0,0 +1,38 @@ +import { TopBar } from "./TopBar.page.js"; + +export class BasePage extends TopBar { + constructor(page) { + super(page); + this.page = page; + this.storeLogo = page.locator(".logo img"); + this.searchInput = page.locator(".control #search"); + this.cartIcon = page.locator(".showcart"); + this.cartItemCount = page.locator(".qty .counter-number"); + this.shoppingCartLoaderMask = page.locator(".showcart .loading-mask"); + + this.miniCartWrapper = page.locator(".block-minicart"); + this.buyWithGoogleViaCartButton = this.miniCartWrapper.locator(".adyen-checkout__paywithgoogle"); + this.buyWithGoogleViaCartButtonAnimation = this.miniCartWrapper.locator(".gpay-card-info-animated-progress-bar"); + } + + async currentCartItemCount() { + await this.shoppingCartLoaderMask.waitFor({ + state: "detached", + timeout: 10000, + }); + return this.cartItemCount.innerText(); + } + + async clickbuyWithGPayViaMiniCart(){ + await this.shoppingCartLoaderMask.waitFor({ + state: "detached", + timeout: 10000, + }); + await this.cartIcon.click(); + + await (this.buyWithGoogleViaCartButtonAnimation).waitFor({state: "visible"}); + await (this.buyWithGoogleViaCartButton).waitFor({state: "visible"}); + await this.page.waitForLoadState("networkidle", { timeout: 10000 }); + await this.buyWithGoogleViaCartButton.click(); + } +} diff --git a/projects/hyva-default/pageObjects/plugin/Login.page.js b/projects/hyva-default/pageObjects/plugin/Login.page.js new file mode 100644 index 00000000..5953ba7a --- /dev/null +++ b/projects/hyva-default/pageObjects/plugin/Login.page.js @@ -0,0 +1,23 @@ +import { TopBar } from "./TopBar.page.js"; + +export class LoginPage extends TopBar { + constructor(page) { + super(page); + this.page = page; + this.loginFormContainer = page.locator(".login-container"); + this.loginForm = this.loginFormContainer.locator(".fieldset.login"); + this.emailInput = this.loginForm.locator("#email"); + this.passwordInput = this.loginForm.locator("#pass"); + this.loginButton = this.loginForm.locator("button[type='submit']"); + } + + async goTo() { + await this.page.goto("/customer/account/login"); + } + + async login(user) { + await this.emailInput.fill(user.email); + await this.passwordInput.fill(user.password); + await this.loginButton.click(); + } +} diff --git a/projects/hyva-default/pageObjects/plugin/PaymentDetails.page.js b/projects/hyva-default/pageObjects/plugin/PaymentDetails.page.js new file mode 100644 index 00000000..15b777b5 --- /dev/null +++ b/projects/hyva-default/pageObjects/plugin/PaymentDetails.page.js @@ -0,0 +1,64 @@ +import { CreditCardComponentsMagento } from "../checkout/CreditCardComponentsMagento.js"; +import { PayPalComponents } from "../../../common/checkoutComponents/PayPalComponents.js" +import PaymentResources from "../../../data/PaymentResources.js"; + +const paymentResources = new PaymentResources(); + +export class PaymentDetailsPage { + constructor(page) { + this.page = page; + + this.emailField = page.locator("#customer-email"); + + this.creditCardRadioButton = page.locator("input[id='payment-method-adyen_cc']"); + this.without3dsVaultButton = page.locator("input#payment-method-adyen_vault_1"); + this.with3dsVaultButton = page.locator("input#payment-method-adyen_vault_2"); + this.cvcInput = page + .frameLocator(".adyen-checkout__card__cvc__input iframe") + .locator(".input-field"); + this.payPalRadioButton = page.locator("input#payment-method-adyen_paypal"); + + this.paymentSummaryLoadingSpinner = page.locator( + ".opc-sidebar .loading-mask" + ); + this.activePaymentMethod = page.locator("div[id='CreditCardActionContainer']"); + this.paymentMethodSaveCheckBox = this.activePaymentMethod.locator( + ".adyen-checkout__checkbox__label" + ); + } + + async fillEmailAddress(user){ + this.emailField.fill(user.email); + } + + async savePaymentMethod() { + await this.paymentMethodSaveCheckBox.click(); + } + + async selectVaultAndInsertCVC(creditCardNumber, cvc) { + if (creditCardNumber == paymentResources.masterCard3DS2) { + await this.with3dsVaultButton.click(); + } else { + await this.without3dsVaultButton.click(); + } + + await this.cvcInput.type(cvc, { delay: 50 }); + } + + async selectCreditCard() { + await this.creditCardRadioButton.click(); + await this.waitForPaymentMethodReady(); + return new CreditCardComponentsMagento(this.page.locator("div[id='CreditCardActionContainer']")); + } + + async selectPayPal() { + await this.payPalRadioButton.click(); + this.activePaymentMethod = this.page.locator("div[id='PaypalActionContainer']"); + await this.waitForPaymentMethodReady(); + return new PayPalComponents(this.page); + } + + async waitForPaymentMethodReady() { + await this.activePaymentMethod.scrollIntoViewIfNeeded(); + } +} diff --git a/projects/hyva-default/pageObjects/plugin/ProductDetail.page.js b/projects/hyva-default/pageObjects/plugin/ProductDetail.page.js new file mode 100644 index 00000000..46bc573b --- /dev/null +++ b/projects/hyva-default/pageObjects/plugin/ProductDetail.page.js @@ -0,0 +1,52 @@ +import { BasePage } from "../plugin/Base.page.js"; + +export class ProductDetailsPage extends BasePage { + constructor(page) { + super(page); + this.page = page; + + this.firstColorOption = page + .locator("//*[contains(@class,'swatch-option color')]") + .first(); + this.quantityField = page.locator("input[name='qty']"); + this.addToCartButton = page.locator("#product-addtocart-button"); + + this.productDetailActionsWrapper = page.locator(".box-tocart"); + this.buyWithGoogleViaProductPageButton = this.productDetailActionsWrapper + .locator(".adyen-checkout__paywithgoogle"); + this.buyWithGoogleViaProductPageButtonAnimation = this.productDetailActionsWrapper + .locator(".gpay-card-info-animated-progress-bar"); + } + + async navigateToItemPage(itemURL){ + await this.page.goto(`/${itemURL}`); + } + + async addToCart(){ + await this.addToCartButton.click(); + } + + async addItemToCart(itemURL) { + await this.navigateToItemPage(itemURL); + await this.addToCart(); + } + + async addItemWithOptionsToCart(itemURL, itemSize = "S", howMany = 1) { + await this.navigateToItemPage(itemURL); + await this.page.locator(`[aria-label='${itemSize.toUpperCase()}']`).click(); + await this.firstColorOption.click(); + await this.quantityField.fill(howMany.toString()); + + await this.addToCartButton.click(); + /* 100ms additional ugly wait to prevent race condition between + the animation and the item number update */ + await new Promise(resolve => setTimeout(resolve, 100)); + } + + async clickBuyWithGPay(){ + await (this.buyWithGoogleViaProductPageButtonAnimation).waitFor({state: "visible"}); + await (this.buyWithGoogleViaProductPageButton).waitFor({state: "visible"}); + await this.page.waitForLoadState("networkidle", { timeout: 10000 }); + await this.buyWithGoogleViaProductPageButton.click(); + } +} diff --git a/projects/hyva-default/pageObjects/plugin/ShippingDetails.page.js b/projects/hyva-default/pageObjects/plugin/ShippingDetails.page.js new file mode 100644 index 00000000..be2ce66b --- /dev/null +++ b/projects/hyva-default/pageObjects/plugin/ShippingDetails.page.js @@ -0,0 +1,78 @@ + +export class ShippingDetails { + constructor(page, formWrapper = page.locator("#guest_details")) { + this.page = page; + + this.guestEmailSection = page.locator("#guest-details"); + this.shippingForm = page.locator("#shipping-details"); + this.shippingMethodForm = page.locator("#shipping"); + this.emailInput = this.guestEmailSection.locator("#guest_details-email_address"); + this.firstNameInput = this.shippingForm.locator("input[name='firstname']"); + this.lastNameInput = this.shippingForm.locator("input[name='lastname']"); + this.addressInput = this.shippingForm.locator("input[name='street[0]']"); + this.countrySelector = this.shippingForm.locator("select[name='country_id']"); + this.stateProvinceDropdown = this.shippingForm.locator("select[name='region']"); + this.stateProvinceField = this.shippingForm.locator("input[name='region']"); + this.cityInput = this.shippingForm.locator("input[name='city']"); + this.postCodeInput = this.shippingForm.locator("input[name='postcode']"); + this.phoneNumberInput = this.shippingForm.locator("input[name='telephone']"); + this.shippingMethodRadioButton = this.shippingMethodForm + .locator("#shipping-method-option-flatrate input[type='radio']"); + this.nextButton = page.locator("button[rel=next]"); + } + + async goTo(waitForAnimation = true) { + // Only works with a non-empty shopping cart + await this.page.goto("/checkout"); + if (waitForAnimation != false) { + } + } + + async fillShippingDetails(user, fillEmail = true) { + const typeDelay = 50; + + if (fillEmail === true) { + await this.emailInput.fill(user.email); + } + + await this.firstNameInput.fill(user.firstName, { delay: typeDelay }); + await this.lastNameInput.fill(user.lastName, { delay: typeDelay }); + await this.addressInput.fill(user.street, { delay: typeDelay }); + await this.postCodeInput.type(user.postCode, { delay: typeDelay }); + await this.cityInput.type(user.city, { delay: typeDelay }); + await this.phoneNumberInput.type(user.phoneNumber, { delay: typeDelay }); + + await this.countrySelector.selectOption(user.countryCode, { timeout: 10000 }); + + if (user.stateOrProvince != undefined) { + const dropdownValue = await this.stateProvinceDropdown + .locator(`option`).last().getAttribute("value"); + await this.stateProvinceDropdown.selectOption(dropdownValue); + } else if (user.stateCode != undefined) { + await this.stateProvinceField.fill(user.stateCode); + } + + if (fillEmail === true) { + await this.shippingMethodRadioButton.check(); + } + } + + async clickNextButton() { + await this.nextButton.click(); + } + + async fillShippingDetailsAndProceedToPayment(user) { + await this.fillShippingDetails(user); + await this.page.waitForLoadState("domcontentloaded", { timeout: 10000 }); + await this.page.waitForLoadState("networkidle", { timeout: 10000 }); + + await this.clickNextButton(); + } + + async proceedToPaymentWithSavedAddress() { + await this.shippingMethodRadioButton.check(); + await this.page.waitForLoadState("load", { timeout: 10000 }); + + await this.clickNextButton(); + } +} diff --git a/projects/hyva-default/pageObjects/plugin/ShoppingCart.page.js b/projects/hyva-default/pageObjects/plugin/ShoppingCart.page.js new file mode 100644 index 00000000..0efc6890 --- /dev/null +++ b/projects/hyva-default/pageObjects/plugin/ShoppingCart.page.js @@ -0,0 +1,24 @@ +import { expect } from "@playwright/test"; +import { BasePage } from "./Base.page.js"; + +export class ShoppingCartPage extends BasePage { + constructor(page) { + super(page); + this.page = page; + this.errorMessage = page.locator(".message-error"); + } + + async goTo() { + await this.page.goto("/checkout/cart"); + } + + async verifyPaymentFailure() { + await this.page.waitForNavigation({ + url: /.*checkout\/cart/, + timeout: 10000, + }); + await expect(await this.errorMessage.innerText()).toContain( + "Your payment failed, Please try again later" + ); + } +} diff --git a/projects/hyva-default/pageObjects/plugin/TopBar.page.js b/projects/hyva-default/pageObjects/plugin/TopBar.page.js new file mode 100644 index 00000000..2dcaa0c2 --- /dev/null +++ b/projects/hyva-default/pageObjects/plugin/TopBar.page.js @@ -0,0 +1,7 @@ +export class TopBar { + constructor(page) { + this.page = page; + this.signInLink = page.locator(".link.authorization-link > a"); + this.createAccountLink = page.locator("text=Create an Account"); + } +} diff --git a/projects/hyva-default/tests/CreditCardPayment.spec.js b/projects/hyva-default/tests/CreditCardPayment.spec.js new file mode 100644 index 00000000..488e827b --- /dev/null +++ b/projects/hyva-default/tests/CreditCardPayment.spec.js @@ -0,0 +1,84 @@ +import { test } from "@playwright/test"; +import PaymentResources from "../../data/PaymentResources.js"; +import { makeCreditCardPayment } from "../helpers/PaymentHelper.js"; +import { ThreeDSPaymentPage } from "../../common/redirect/ThreeDSPaymentPage.js"; +import { ThreeDS2PaymentPage } from "../../common/redirect/ThreeDS2PaymentPage.js"; +import { CreditCardComponentsMagento } from "../pageObjects/checkout/CreditCardComponentsMagento.js"; +import { + goToShippingWithFullCart, + proceedToPaymentAs, + verifyFailedPayment, + verifySuccessfulPayment, +} from "../helpers/ScenarioHelper.js"; + +const paymentResources = new PaymentResources(); +const users= paymentResources.guestUser; + +test.describe.parallel("Payment via credit card", () => { + test.beforeEach(async ({ page }) => { + await goToShippingWithFullCart(page); + await proceedToPaymentAs(page, users.regular); + }); + + test("without 3Ds should succeed", async ({ page }) => { + + await makeCreditCardPayment( + page, + users.regular, + paymentResources.masterCardWithout3D, + paymentResources.expDate, + paymentResources.cvc + ); + + await verifySuccessfulPayment(page); + }); + + test("with 3Ds2 should succeed", async ({ page }) => { + + await makeCreditCardPayment( + page, + users.regular, + paymentResources.masterCard3DS2, + paymentResources.expDate, + paymentResources.cvc + ); + + await new ThreeDS2PaymentPage(page).validate3DS2( + paymentResources.threeDSCorrectPassword + ); + + await verifySuccessfulPayment(page); + }); + + test("with wrong 3Ds2 credentials should fail", async ({ page }) => { + + await makeCreditCardPayment( + page, + users.regular, + paymentResources.masterCard3DS2, + paymentResources.expDate, + paymentResources.cvc + ); + + await new ThreeDS2PaymentPage(page).validate3DS2( + paymentResources.threeDSWrongPassword + ); + + await new CreditCardComponentsMagento(page).verifyPaymentRefusal(); + }); + + test("with 3Ds2 should abort the payment with correct message when cancelled", async ({ page }) => { + + await makeCreditCardPayment( + page, + users.regular, + paymentResources.masterCard3DS2, + paymentResources.expDate, + paymentResources.cvc + ); + + await new ThreeDS2PaymentPage(page).clickCancel(); + + await new CreditCardComponentsMagento(page).verifyPaymentRefusal(); + }); +}); diff --git a/projects/hyva-default/tests/PayPalPayment.spec.js b/projects/hyva-default/tests/PayPalPayment.spec.js new file mode 100644 index 00000000..7cfd3451 --- /dev/null +++ b/projects/hyva-default/tests/PayPalPayment.spec.js @@ -0,0 +1,44 @@ +import { test } from "@playwright/test"; +import PaymentResources from "../../data/PaymentResources.js"; +import { + goToShippingWithFullCart, + verifySuccessfulPayment, +} from "../helpers/ScenarioHelper.js"; +import { proceedToPaymentAs } from "../helpers/ScenarioHelper.js"; +import { PaymentDetailsPage } from "../pageObjects/plugin/PaymentDetails.page.js"; +import { PayPalPaymentPage } from "../../common/redirect/PayPalPaymentPage.js"; + +const paymentResources = new PaymentResources(); +const users = paymentResources.guestUser; + +test.describe("Payment via PayPal", () => { + test.beforeEach(async ({ page }) => { + await goToShippingWithFullCart(page); + }); + + test("should succeed", async ({ page }) => { + await proceedToPaymentAs(page, users.dutch); + + await payViaPayPal( + page, + paymentResources.payPalUserName, + paymentResources.payPalPassword + ); + + await verifySuccessfulPayment(page); + }); + + async function payViaPayPal(page, username, password) { + const paymentDetailPage = new PaymentDetailsPage(page); + const payPalSection = await paymentDetailPage.selectPayPal(); + + await page.waitForLoadState("load", { timeout: 15000 }); + + const [popup] = await Promise.all([ + page.waitForEvent("popup"), + payPalSection.proceedToPayPal(), + ]); + + await new PayPalPaymentPage(popup).makePayPalPayment(username, password); + } +}); diff --git a/projects/hyva-default/tests/loggedIn/LoggedInUser.spec.js b/projects/hyva-default/tests/loggedIn/LoggedInUser.spec.js new file mode 100644 index 00000000..a7ec135c --- /dev/null +++ b/projects/hyva-default/tests/loggedIn/LoggedInUser.spec.js @@ -0,0 +1,2 @@ + +import "./StoredCardPayment.js" diff --git a/projects/hyva-default/tests/loggedIn/StoredCardPayment.js b/projects/hyva-default/tests/loggedIn/StoredCardPayment.js new file mode 100644 index 00000000..dfdccf91 --- /dev/null +++ b/projects/hyva-default/tests/loggedIn/StoredCardPayment.js @@ -0,0 +1,102 @@ +import { expect, test } from "@playwright/test"; +import PaymentResources from "../../../data/PaymentResources.js"; +import { PaymentDetailsPage } from "../../pageObjects/plugin/PaymentDetails.page.js"; +import { + goToShippingWithFullCart, + proceedToPaymentAs, + verifySuccessfulPayment, + loginAs, + placeOrder, +} from "../../helpers/ScenarioHelper.js"; +import { ThreeDS2PaymentPage } from "../../../common/redirect/ThreeDS2PaymentPage.js"; + +const paymentResources = new PaymentResources(); +const magentoSampleUser = paymentResources.sampleRegisteredUser; +const users = paymentResources.guestUser; + +/* No parallelism due to usage of same user account +since it will cause the cart to reset */ +test.describe("Payment via stored credit card", () => { + + test.beforeEach(async ({ page }) => { + await loginAs(page, magentoSampleUser); + await goToShippingWithFullCart(page); + await proceedToPaymentAs(page, undefined, false); + }); + + test("should succeed with no 3Ds", async ({ page }) => { + await makeCreditCardPayment( + page, + users.regular, + paymentResources.masterCardWithout3D, + paymentResources.expDate, + paymentResources.cvc, + true + ); + await verifySuccessfulPayment(page); + + await goToShippingWithFullCart(page); + await proceedToPaymentAs(page, undefined, false); + + await makeVaultPayment(page, paymentResources.masterCardWithout3D, paymentResources.cvc); + await verifySuccessfulPayment(page); + }); + + test("should succeed with 3Ds2", async ({ page }) => { + await makeCreditCardPayment( + page, + users.regular, + paymentResources.masterCard3DS2, + paymentResources.expDate, + paymentResources.cvc, + true + ); + await new ThreeDS2PaymentPage(page).validate3DS2( + paymentResources.threeDSCorrectPassword + ); + await verifySuccessfulPayment(page); + + await goToShippingWithFullCart(page); + await proceedToPaymentAs(page, undefined, false); + + await makeVaultPayment(page, paymentResources.masterCard3DS2, paymentResources.cvc); + await new ThreeDS2PaymentPage(page).validate3DS2( + paymentResources.threeDSCorrectPassword + ); + await verifySuccessfulPayment(page); + }); +}); + + + +async function makeCreditCardPayment( + page, + user, + creditCardNumber, + expDate, + cvc, + saveCard = false +) { + const paymentDetailPage = new PaymentDetailsPage(page); + const creditCardSection = await paymentDetailPage.selectCreditCard(); + + await creditCardSection.fillCreditCardInfo( + user.firstName, + user.lastName, + creditCardNumber, + expDate, + cvc + ); + + if (saveCard == true){ + await paymentDetailPage.savePaymentMethod(); + } + + await placeOrder(page); +} + +async function makeVaultPayment(page, creditCardNumber, cvc) { + await new PaymentDetailsPage(page).selectVaultAndInsertCVC(creditCardNumber, cvc); + + await placeOrder(page); +}