adding adyen payment flow for playwright tests
This commit is contained in:
parent
e1bbcc7948
commit
1edd257d06
9 changed files with 148 additions and 25 deletions
|
|
@ -10,6 +10,7 @@ SKIP_CONTENT_SITE="false"
|
||||||
# Experiments Flag
|
# Experiments Flag
|
||||||
IS_MOBILEFIRST="false"
|
IS_MOBILEFIRST="false"
|
||||||
IS_ADYENPAYMENTS="false"
|
IS_ADYENPAYMENTS="false"
|
||||||
|
IS_MULTILOCATIONPOPUP="false"
|
||||||
|
|
||||||
# Base URLs by environment
|
# Base URLs by environment
|
||||||
# qa
|
# qa
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ SKIP_CONTENT_SITE="false"
|
||||||
# Experiments Flag
|
# Experiments Flag
|
||||||
IS_MOBILEFIRST="false"
|
IS_MOBILEFIRST="false"
|
||||||
IS_ADYENPAYMENTS="false"
|
IS_ADYENPAYMENTS="false"
|
||||||
|
IS_MULTILOCATIONPOPUP="false"
|
||||||
|
|
||||||
# Base URLs by environment
|
# Base URLs by environment
|
||||||
# qa
|
# qa
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,6 @@ export function getDefaultExperimentsData(): IExperiments {
|
||||||
return {
|
return {
|
||||||
isAdyenPayments: !!process.env.IS_ADYENPAYMENTS && process.env.IS_ADYENPAYMENTS !== "" ? process.env.IS_ADYENPAYMENTS === "true" : false,
|
isAdyenPayments: !!process.env.IS_ADYENPAYMENTS && process.env.IS_ADYENPAYMENTS !== "" ? process.env.IS_ADYENPAYMENTS === "true" : false,
|
||||||
isMobileFirst: !!process.env.IS_MOBILEFIRST && process.env.IS_MOBILEFIRST !== "" ? process.env.IS_MOBILEFIRST === "true" : false,
|
isMobileFirst: !!process.env.IS_MOBILEFIRST && process.env.IS_MOBILEFIRST !== "" ? process.env.IS_MOBILEFIRST === "true" : false,
|
||||||
|
isMultiLocationPopup: !!process.env.IS_MULTILOCATIONPOPUP && process.env.IS_MULTILOCATIONPOPUP !== "" ? process.env.IS_MULTILOCATIONPOPUP === "true" : false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export interface IExperiments {
|
export interface IExperiments {
|
||||||
isMobileFirst: boolean,
|
isMobileFirst: boolean,
|
||||||
isAdyenPayments: boolean
|
isAdyenPayments: boolean,
|
||||||
|
isMultiLocationPopup: boolean
|
||||||
}
|
}
|
||||||
|
|
@ -257,8 +257,12 @@ export class BasePage {
|
||||||
: "MobileFirstAppointment=MobileFirstAppt_V1=MobileFirstAppt_CONTROL=true";
|
: "MobileFirstAppointment=MobileFirstAppt_V1=MobileFirstAppt_CONTROL=true";
|
||||||
|
|
||||||
experimentsURLExtension += experiments?.isAdyenPayments
|
experimentsURLExtension += experiments?.isAdyenPayments
|
||||||
? ",Adyen%20Payments=Adyen%20Payment%20Test=Adyen%20Payment%20(Test)"
|
? ",Adyen%20Payments=Adyen%20Payment%20Test=Adyen%20Payment%20(Test)=true"
|
||||||
: ",Adyen%20Payments=Adyen%20Payment%20Test=CyberSource%20(Control)";
|
: ",Adyen%20Payments=Adyen%20Payment%20Test=CyberSource%20(Control)=true";
|
||||||
|
|
||||||
|
experimentsURLExtension += experiments?.isMultiLocationPopup
|
||||||
|
? ",MultiLocationPopup=MultiLocationPopup_V1=MultiLocationPopup_TEST=true"
|
||||||
|
: ",MultiLocationPopup=MultiLocationPopup_V1=MultiLocationPopup_CONTROL=true";
|
||||||
} else {
|
} else {
|
||||||
console.log("Url extension without query string");
|
console.log("Url extension without query string");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
81
playwright-tests/pages/PaymentAdyenPage.ts
Normal file
81
playwright-tests/pages/PaymentAdyenPage.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { type Locator, type Page } from '@playwright/test';
|
||||||
|
import { BasePage } from './BasePage';
|
||||||
|
import { IPaymentDetails } from 'safelite-playwright-core';
|
||||||
|
import { ITestData } from 'framework/TestData';
|
||||||
|
import { PaypalPage } from './PaypalPage';
|
||||||
|
|
||||||
|
export class PaymentAdyenPage extends BasePage {
|
||||||
|
readonly page: Page;
|
||||||
|
readonly creditOrDebitCardButton: Locator;
|
||||||
|
readonly nameOnCardTextField: Locator;
|
||||||
|
readonly cardNumberTextField: Locator;
|
||||||
|
readonly expiryDateTextField: Locator;
|
||||||
|
readonly cvvTextField: Locator;
|
||||||
|
readonly countryDropDown: Locator;
|
||||||
|
readonly billingAddressTextField: Locator;
|
||||||
|
readonly cityTextField: Locator;
|
||||||
|
readonly stateDropDown: Locator;
|
||||||
|
readonly billingZipTextField: Locator;
|
||||||
|
readonly submitPaymentButton: Locator;
|
||||||
|
readonly payPalButton: Locator;
|
||||||
|
readonly navigateToPaypalButton: Locator;
|
||||||
|
readonly afterPayButton: Locator;
|
||||||
|
readonly navigateToAfterPayButton: Locator;
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
super(page);
|
||||||
|
this.page = page;
|
||||||
|
this.creditOrDebitCardButton = page.locator('.adyen-checkout__payment-method--credit button');
|
||||||
|
this.cardNumberTextField = page.frameLocator('iframe[title="Iframe for card number"]').locator('input[id*="adyen-checkout-encryptedCardNumber"]');
|
||||||
|
this.expiryDateTextField = page.frameLocator('iframe[title="Iframe for expiry date"]').locator('input[id*="adyen-checkout-encryptedExpiryDate"]');
|
||||||
|
this.cvvTextField = page.frameLocator('iframe[title="Iframe for security code"]').locator('input[id*="adyen-checkout-encryptedSecurityCode"]');
|
||||||
|
this.nameOnCardTextField = page .locator('input[id*="adyen-checkout-holderName"]');
|
||||||
|
this.countryDropDown = page.locator('input[id*="adyen-checkout-country"]');
|
||||||
|
this.billingAddressTextField = page.locator('input[id*="adyen-checkout-street"]');
|
||||||
|
this.cityTextField = page.locator('input[id*="adyen-checkout-city"]');
|
||||||
|
this.stateDropDown = page.locator('input[id*="adyen-checkout-stateOrProvince"]');
|
||||||
|
this.billingZipTextField = page.locator('input[id*="adyen-checkout-postalCode"]');
|
||||||
|
this.submitPaymentButton = page.locator('button.adyen-checkout__button');
|
||||||
|
this.payPalButton = page.locator('button[id*="button-paypal"]');
|
||||||
|
this.navigateToPaypalButton = page.frameLocator('iframe[title="PayPal-paypal"]:first-of-type').locator('div[role="link"][class*="paypal-button"]');
|
||||||
|
this.afterPayButton = page.locator('button[id*="button-redirect"]');
|
||||||
|
this.navigateToAfterPayButton = page.locator('div.adyen-checkout__payment-method--afterpaytouch_US button.adyen-checkout__button');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async populateAdyenCreditCardDetails(testData: Partial<ITestData>){
|
||||||
|
const { paymentDetails, customerDetails } = testData;
|
||||||
|
|
||||||
|
paymentDetails!.cardNumber = '5100 0600 0000 0002';
|
||||||
|
paymentDetails!.expirationMonth = "12 - December";
|
||||||
|
paymentDetails!.expirationYear = "2029";
|
||||||
|
paymentDetails!.cvv = "737";
|
||||||
|
|
||||||
|
await this.creditOrDebitCardButton.click();
|
||||||
|
await this.cardNumberTextField.fill(paymentDetails!.cardNumber || '');
|
||||||
|
|
||||||
|
const expirationDate = `${(paymentDetails!.expirationMonth!.split(' ')[0] || '').padStart(2, '0')} + ${paymentDetails!.expirationYear!.toString().slice(-2)}`;
|
||||||
|
await this.expiryDateTextField.pressSequentially(expirationDate);
|
||||||
|
|
||||||
|
await this.cvvTextField.fill(paymentDetails!.cvv!);
|
||||||
|
await this.nameOnCardTextField.fill(customerDetails!.firstName! + ' ' + customerDetails!.lastName!);
|
||||||
|
await this.billingAddressTextField.fill(paymentDetails!.billingAddress!.street);
|
||||||
|
await this.countryDropDown.pressSequentially("United States");
|
||||||
|
await this.countryDropDown.click();
|
||||||
|
await this.page.getByRole('option', { name: 'United States', exact: true }).click();
|
||||||
|
|
||||||
|
await this.cityTextField.fill(paymentDetails!.billingAddress!.city);
|
||||||
|
await this.stateDropDown.pressSequentially(paymentDetails!.billingAddress!.state);
|
||||||
|
await this.stateDropDown.click();
|
||||||
|
await this.page.locator(".adyen-checkout__field--stateOrProvince li").nth(0).click();
|
||||||
|
await this.billingZipTextField.fill(paymentDetails!.billingAddress!.postalCode);
|
||||||
|
await this.submitPaymentButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async navigateToAdyenPaypalCheckout(): Promise<PaypalPage> {
|
||||||
|
await this.payPalButton.click();
|
||||||
|
const paypalPage = this.page.waitForEvent('popup');
|
||||||
|
await this.navigateToPaypalButton.click();
|
||||||
|
return new PaypalPage(await paypalPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ import { AfterpayPage } from './AfterpayPage';
|
||||||
import { PaypalPage } from './PaypalPage';
|
import { PaypalPage } from './PaypalPage';
|
||||||
import { ITestData } from 'framework/TestData';
|
import { ITestData } from 'framework/TestData';
|
||||||
import { step } from 'framework/localTypes/Step';
|
import { step } from 'framework/localTypes/Step';
|
||||||
|
import { IExperiments } from 'framework/localTypes/IExperiments';
|
||||||
|
import { PaymentAdyenPage } from './PaymentAdyenPage';
|
||||||
|
|
||||||
export class PaymentMethodPage extends BasePage {
|
export class PaymentMethodPage extends BasePage {
|
||||||
readonly page: Page;
|
readonly page: Page;
|
||||||
|
|
@ -25,7 +27,9 @@ export class PaymentMethodPage extends BasePage {
|
||||||
readonly submitButton: Locator;
|
readonly submitButton: Locator;
|
||||||
readonly recalibrationCheckbox: Locator;
|
readonly recalibrationCheckbox: Locator;
|
||||||
readonly paymentPage: PaymentPage;
|
readonly paymentPage: PaymentPage;
|
||||||
|
readonly paymentAdyenPage: PaymentAdyenPage;
|
||||||
readonly paypalPage: PaypalPage;
|
readonly paypalPage: PaypalPage;
|
||||||
|
readonly afterpayPage: AfterpayPage
|
||||||
readonly afterPayBreakoutSection: Locator;
|
readonly afterPayBreakoutSection: Locator;
|
||||||
readonly afterPayToggle: Locator;
|
readonly afterPayToggle: Locator;
|
||||||
|
|
||||||
|
|
@ -59,7 +63,9 @@ export class PaymentMethodPage extends BasePage {
|
||||||
this.recalibrationCheckbox = this.page.locator('label:has(>input[name=\'recalAckOptIn\'])');
|
this.recalibrationCheckbox = this.page.locator('label:has(>input[name=\'recalAckOptIn\'])');
|
||||||
// this.creditCardButton = page.locator('div').filter({ hasText: /^Credit or Debit$/ }).nth(1);
|
// this.creditCardButton = page.locator('div').filter({ hasText: /^Credit or Debit$/ }).nth(1);
|
||||||
this.paymentPage = new PaymentPage(page);
|
this.paymentPage = new PaymentPage(page);
|
||||||
|
this.paymentAdyenPage = new PaymentAdyenPage(page);
|
||||||
this.paypalPage = new PaypalPage(page);
|
this.paypalPage = new PaypalPage(page);
|
||||||
|
this.afterpayPage = new AfterpayPage(page);
|
||||||
|
|
||||||
// Payment details validation locators
|
// Payment details validation locators
|
||||||
this.reviewTable = this.page.locator('div.review-table');
|
this.reviewTable = this.page.locator('div.review-table');
|
||||||
|
|
@ -191,35 +197,57 @@ export class PaymentMethodPage extends BasePage {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
async executePayment(paymentDetails: IPaymentDetails, isRecalVehicle: boolean) {
|
async executePayment(testData: Partial<ITestData>) {
|
||||||
const browserContext = this.page.context();
|
const browserContext = this.page.context();
|
||||||
|
const { paymentDetails, isRecalVehicle, experiments } = testData;
|
||||||
|
|
||||||
switch (paymentDetails.paymentType) {
|
switch (paymentDetails!.paymentType) {
|
||||||
case PaymentType.Credit:
|
case PaymentType.Credit:
|
||||||
await this.selectCreditCard();
|
await this.selectCreditCard();
|
||||||
await this.nextPage();
|
await this.nextPage();
|
||||||
await this.paymentPage.populateCreditCardDetails(paymentDetails);
|
experiments!.isAdyenPayments
|
||||||
|
? await this.paymentAdyenPage.populateAdyenCreditCardDetails(testData)
|
||||||
|
: await this.paymentPage.populateCreditCardDetails(paymentDetails!);
|
||||||
break;
|
break;
|
||||||
case PaymentType.AfterPay:
|
case PaymentType.AfterPay:
|
||||||
await this.payInFourButton.click();
|
await this.payInFourButton.click();
|
||||||
await this.continueButton.click();
|
await this.continueButton.click();
|
||||||
|
|
||||||
// Capture popup
|
if (experiments?.isAdyenPayments) {
|
||||||
const afterpayPopup = await browserContext.waitForEvent('page');
|
await this.paymentAdyenPage.afterPayButton.click();
|
||||||
const afterpayPage = new AfterpayPage(afterpayPopup);
|
await this.paymentAdyenPage.navigateToAfterPayButton.click();
|
||||||
|
await this.afterpayPage.executeAfterpayPayment(paymentDetails!);
|
||||||
|
} else {
|
||||||
|
// Capture popup
|
||||||
|
const afterpayPopup = await browserContext.waitForEvent('page');
|
||||||
|
const afterpayPage = new AfterpayPage(afterpayPopup);
|
||||||
|
|
||||||
|
// Execute payment
|
||||||
|
await afterpayPage.executeAfterpayPayment(paymentDetails!);
|
||||||
|
}
|
||||||
|
|
||||||
// Execute payment
|
|
||||||
await afterpayPage.executeAfterpayPayment(paymentDetails);
|
|
||||||
break;
|
break;
|
||||||
case PaymentType.Paypal:
|
case PaymentType.Paypal:
|
||||||
await this.selectPaypal();
|
await this.selectPaypal();
|
||||||
// TODO: Click paypal button
|
// TODO: Click paypal button
|
||||||
await this.nextPage();
|
await this.nextPage();
|
||||||
await this.paymentPage.navigateToPaypalCheckout();
|
const paypalPage = experiments?.isAdyenPayments
|
||||||
await this.paypalPage.completePaypalPurchase(paymentDetails);
|
? await this.paymentAdyenPage.navigateToAdyenPaypalCheckout()
|
||||||
|
: await this.paymentPage.navigateToPaypalCheckout();
|
||||||
|
|
||||||
|
/*if (experiments?.isAdyenPayments) {
|
||||||
|
// Capture popup
|
||||||
|
const payPalPopup = await this.page.waitForEvent('popup');
|
||||||
|
const paypalPage = new PaypalPage(payPalPopup);
|
||||||
|
paypalPage.completePaypalPurchase(testData);
|
||||||
|
} else {
|
||||||
|
await this.paypalPage.completePaypalPurchase(testData);
|
||||||
|
}*/
|
||||||
|
await paypalPage.completePaypalPurchase(testData);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PaymentType.PayAtService:
|
case PaymentType.PayAtService:
|
||||||
await this.selectPayAtService(isRecalVehicle);
|
await this.selectPayAtService(isRecalVehicle!);
|
||||||
await this.nextPage();
|
await this.nextPage();
|
||||||
break;
|
break;
|
||||||
case PaymentType.PayWithInsurance:
|
case PaymentType.PayWithInsurance:
|
||||||
|
|
@ -527,7 +555,7 @@ export class PaymentMethodPage extends BasePage {
|
||||||
|
|
||||||
@step("PaymentMethodPage >> Select Payment Method: ")
|
@step("PaymentMethodPage >> Select Payment Method: ")
|
||||||
async handlePaymentMethodPage(testData: Partial<ITestData>) {
|
async handlePaymentMethodPage(testData: Partial<ITestData>) {
|
||||||
const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM } = testData;
|
const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM, experiments } = testData;
|
||||||
|
|
||||||
await this.validateProgressBar(ProgressBarPercentages.PaymentMethodPage);
|
await this.validateProgressBar(ProgressBarPercentages.PaymentMethodPage);
|
||||||
await this.validatePaymentDetailsPage(testData);
|
await this.validatePaymentDetailsPage(testData);
|
||||||
|
|
@ -542,7 +570,8 @@ export class PaymentMethodPage extends BasePage {
|
||||||
await this.verifyVAPS();
|
await this.verifyVAPS();
|
||||||
}
|
}
|
||||||
if (paymentDetails?.paymentType) {
|
if (paymentDetails?.paymentType) {
|
||||||
await this.executePayment(paymentDetails!, isRecalVehicle!);
|
// await this.executePayment(paymentDetails!, isRecalVehicle!, experiments!);
|
||||||
|
await this.executePayment(testData);
|
||||||
} else {
|
} else {
|
||||||
await this.nextPage();
|
await this.nextPage();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { type Locator, type Page } from '@playwright/test';
|
import { type Locator, type Page } from '@playwright/test';
|
||||||
import { BasePage } from './BasePage';
|
import { BasePage } from './BasePage';
|
||||||
import { IPaymentDetails } from 'safelite-playwright-core';
|
import { IPaymentDetails } from 'safelite-playwright-core';
|
||||||
|
import { PaypalPage } from './PaypalPage';
|
||||||
|
|
||||||
export class PaymentPage extends BasePage {
|
export class PaymentPage extends BasePage {
|
||||||
readonly page: Page;
|
readonly page: Page;
|
||||||
|
|
@ -43,8 +44,8 @@ export class PaymentPage extends BasePage {
|
||||||
await this.submitPaymentButton.click();
|
await this.submitPaymentButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
async navigateToPaypalCheckout() {
|
async navigateToPaypalCheckout(): Promise<PaypalPage> {
|
||||||
await this.payPalButton.click();
|
await this.payPalButton.click();
|
||||||
|
return new PaypalPage(this.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { expect, type Locator, type Page } from '@playwright/test';
|
import { expect, type Locator, type Page } from '@playwright/test';
|
||||||
import { BasePage } from './BasePage';
|
import { BasePage } from './BasePage';
|
||||||
import { IPaymentDetails } from 'safelite-playwright-core';
|
import { ITestData } from 'framework/TestData';
|
||||||
|
|
||||||
export class PaypalPage extends BasePage {
|
export class PaypalPage extends BasePage {
|
||||||
readonly page: Page;
|
readonly page: Page;
|
||||||
|
|
@ -30,24 +30,28 @@ export class PaypalPage extends BasePage {
|
||||||
this.tryAnotherWayButton = page.getByRole('button', { name: 'Try another way' });
|
this.tryAnotherWayButton = page.getByRole('button', { name: 'Try another way' });
|
||||||
}
|
}
|
||||||
|
|
||||||
async completePaypalPurchase(paymentDetails: IPaymentDetails){
|
async completePaypalPurchase(testData: Partial<ITestData>){
|
||||||
|
|
||||||
|
const {paymentDetails, experiments } = testData;
|
||||||
// Wait for the PayPal login page to load
|
// Wait for the PayPal login page to load
|
||||||
await this.page.waitForFunction(() => window.location.href.includes('sandbox.paypal.com'), null, { timeout: 10000 });
|
await this.page.waitForFunction(() => window.location.href.includes('sandbox.paypal.com'), null, { timeout: 10000 });
|
||||||
|
|
||||||
// handle flow with and without the "Log in with a password instead" link
|
// handle flow with and without the "Log in with a password instead" link
|
||||||
if (await this.loginWithPasswordButton.isVisible()) {
|
if (await this.loginWithPasswordButton.isVisible()) {
|
||||||
await this.loginWithPasswordButton.click();
|
await this.loginWithPasswordButton.click();
|
||||||
await this.passwordTextBox.fill(paymentDetails.password!);
|
await this.passwordTextBox.fill(paymentDetails!.password!);
|
||||||
await this.paypalLoginButton.click();
|
await this.paypalLoginButton.click();
|
||||||
await this.completePurchaseButton.click();
|
await this.completePurchaseButton.click();
|
||||||
} else {
|
} else {
|
||||||
await this.usernameTextBox.waitFor({ state: 'visible' });
|
await this.usernameTextBox.waitFor({ state: 'visible' });
|
||||||
await this.page.screenshot({ path: `test-results\\ortoni-data\\paypal-username-${Date.now()}.png`, fullPage: true });
|
await this.page.screenshot({ path: `test-results\\ortoni-data\\paypal-username-${Date.now()}.png`, fullPage: true });
|
||||||
await this.usernameTextBox.fill(paymentDetails.username!);
|
await this.usernameTextBox.fill(paymentDetails!.username!);
|
||||||
await this.nextButton.click();
|
await this.nextButton.click();
|
||||||
await this.tryAnotherWayButton.click();
|
if (!experiments!.isAdyenPayments){
|
||||||
await this.usePasswordInsteadButton.click();
|
await this.tryAnotherWayButton.click();
|
||||||
await this.passwordTextBox.fill(paymentDetails.password!);
|
await this.usePasswordInsteadButton.click();
|
||||||
|
}
|
||||||
|
await this.passwordTextBox.fill(paymentDetails!.password!);
|
||||||
await this.paypalLoginButton.click();
|
await this.paypalLoginButton.click();
|
||||||
await this.payWithRadioButton.click();
|
await this.payWithRadioButton.click();
|
||||||
await this.page.waitForTimeout(2000);
|
await this.page.waitForTimeout(2000);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue