Skip to content

Commit

Permalink
Card, Stored Card, Paypal E2E tests for huva-default checkout (#113)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
pmarjan-onestic authored Aug 9, 2024
1 parent 561f213 commit d1de7ca
Show file tree
Hide file tree
Showing 17 changed files with 747 additions and 0 deletions.
12 changes: 12 additions & 0 deletions projects/hyva-default/README.md
Original file line number Diff line number Diff line change
@@ -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.

22 changes: 22 additions & 0 deletions projects/hyva-default/helpers/PaymentHelper.js
Original file line number Diff line number Diff line change
@@ -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);
}
83 changes: 83 additions & 0 deletions projects/hyva-default/helpers/ScenarioHelper.js
Original file line number Diff line number Diff line change
@@ -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();
}
77 changes: 77 additions & 0 deletions projects/hyva-default/magento.hyva.config.cjs
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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"
);
}
}
Original file line number Diff line number Diff line change
@@ -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,
});
}
}
38 changes: 38 additions & 0 deletions projects/hyva-default/pageObjects/plugin/Base.page.js
Original file line number Diff line number Diff line change
@@ -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();
}
}
23 changes: 23 additions & 0 deletions projects/hyva-default/pageObjects/plugin/Login.page.js
Original file line number Diff line number Diff line change
@@ -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();
}
}
64 changes: 64 additions & 0 deletions projects/hyva-default/pageObjects/plugin/PaymentDetails.page.js
Original file line number Diff line number Diff line change
@@ -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();
}
}
Loading

0 comments on commit d1de7ca

Please sign in to comment.