Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Card, Stored Card, Paypal E2E tests for huva-default checkout #113

Merged
merged 7 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading