diff --git a/playwright-tests/.env b/playwright-tests/.env index 2b8d27d3e..179424e5e 100644 --- a/playwright-tests/.env +++ b/playwright-tests/.env @@ -10,6 +10,7 @@ SKIP_CONTENT_SITE="false" # Experiments Flag IS_MOBILEFIRST="false" IS_ADYENPAYMENTS="false" +IS_MULTILOCATIONPOPUP="false" # Base URLs by environment # qa diff --git a/playwright-tests/.env.dev b/playwright-tests/.env.dev index 8d2786918..de2c3c0f2 100644 --- a/playwright-tests/.env.dev +++ b/playwright-tests/.env.dev @@ -10,6 +10,7 @@ SKIP_CONTENT_SITE="false" # Experiments Flag IS_MOBILEFIRST="false" IS_ADYENPAYMENTS="false" +IS_MULTILOCATIONPOPUP="false" # Base URLs by environment # qa diff --git a/playwright-tests/framework/TestData.ts b/playwright-tests/framework/TestData.ts index f863a74fd..6a87dfd8f 100644 --- a/playwright-tests/framework/TestData.ts +++ b/playwright-tests/framework/TestData.ts @@ -18,5 +18,6 @@ export function getDefaultExperimentsData(): IExperiments { return { 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, + isMultiLocationPopup: !!process.env.IS_MULTILOCATIONPOPUP && process.env.IS_MULTILOCATIONPOPUP !== "" ? process.env.IS_MULTILOCATIONPOPUP === "true" : false, } } diff --git a/playwright-tests/framework/localTypes/IExperiments.ts b/playwright-tests/framework/localTypes/IExperiments.ts index 78935f480..bfe13c864 100644 --- a/playwright-tests/framework/localTypes/IExperiments.ts +++ b/playwright-tests/framework/localTypes/IExperiments.ts @@ -1,4 +1,5 @@ export interface IExperiments { isMobileFirst: boolean, - isAdyenPayments: boolean + isAdyenPayments: boolean, + isMultiLocationPopup: boolean } \ No newline at end of file diff --git a/playwright-tests/pages/BasePage.ts b/playwright-tests/pages/BasePage.ts index 07d254391..e9fe835dc 100644 --- a/playwright-tests/pages/BasePage.ts +++ b/playwright-tests/pages/BasePage.ts @@ -257,8 +257,12 @@ export class BasePage { : "MobileFirstAppointment=MobileFirstAppt_V1=MobileFirstAppt_CONTROL=true"; experimentsURLExtension += experiments?.isAdyenPayments - ? ",Adyen%20Payments=Adyen%20Payment%20Test=Adyen%20Payment%20(Test)" - : ",Adyen%20Payments=Adyen%20Payment%20Test=CyberSource%20(Control)"; + ? ",Adyen%20Payments=Adyen%20Payment%20Test=Adyen%20Payment%20(Test)=true" + : ",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 { console.log("Url extension without query string"); } diff --git a/playwright-tests/pages/PaymentAdyenPage.ts b/playwright-tests/pages/PaymentAdyenPage.ts new file mode 100644 index 000000000..82c876af0 --- /dev/null +++ b/playwright-tests/pages/PaymentAdyenPage.ts @@ -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){ + 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 { + await this.payPalButton.click(); + const paypalPage = this.page.waitForEvent('popup'); + await this.navigateToPaypalButton.click(); + return new PaypalPage(await paypalPage); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/PaymentMethodPage.ts b/playwright-tests/pages/PaymentMethodPage.ts index 248ce845f..215ccff30 100644 --- a/playwright-tests/pages/PaymentMethodPage.ts +++ b/playwright-tests/pages/PaymentMethodPage.ts @@ -8,6 +8,8 @@ import { AfterpayPage } from './AfterpayPage'; import { PaypalPage } from './PaypalPage'; import { ITestData } from 'framework/TestData'; import { step } from 'framework/localTypes/Step'; +import { IExperiments } from 'framework/localTypes/IExperiments'; +import { PaymentAdyenPage } from './PaymentAdyenPage'; export class PaymentMethodPage extends BasePage { readonly page: Page; @@ -25,7 +27,9 @@ export class PaymentMethodPage extends BasePage { readonly submitButton: Locator; readonly recalibrationCheckbox: Locator; readonly paymentPage: PaymentPage; + readonly paymentAdyenPage: PaymentAdyenPage; readonly paypalPage: PaypalPage; + readonly afterpayPage: AfterpayPage readonly afterPayBreakoutSection: Locator; readonly afterPayToggle: Locator; @@ -59,7 +63,9 @@ export class PaymentMethodPage extends BasePage { this.recalibrationCheckbox = this.page.locator('label:has(>input[name=\'recalAckOptIn\'])'); // this.creditCardButton = page.locator('div').filter({ hasText: /^Credit or Debit$/ }).nth(1); this.paymentPage = new PaymentPage(page); + this.paymentAdyenPage = new PaymentAdyenPage(page); this.paypalPage = new PaypalPage(page); + this.afterpayPage = new AfterpayPage(page); // Payment details validation locators this.reviewTable = this.page.locator('div.review-table'); @@ -191,35 +197,57 @@ export class PaymentMethodPage extends BasePage { return text; } - async executePayment(paymentDetails: IPaymentDetails, isRecalVehicle: boolean) { + async executePayment(testData: Partial) { const browserContext = this.page.context(); + const { paymentDetails, isRecalVehicle, experiments } = testData; - switch (paymentDetails.paymentType) { + switch (paymentDetails!.paymentType) { case PaymentType.Credit: await this.selectCreditCard(); await this.nextPage(); - await this.paymentPage.populateCreditCardDetails(paymentDetails); + experiments!.isAdyenPayments + ? await this.paymentAdyenPage.populateAdyenCreditCardDetails(testData) + : await this.paymentPage.populateCreditCardDetails(paymentDetails!); break; case PaymentType.AfterPay: await this.payInFourButton.click(); await this.continueButton.click(); - // Capture popup - const afterpayPopup = await browserContext.waitForEvent('page'); - const afterpayPage = new AfterpayPage(afterpayPopup); + if (experiments?.isAdyenPayments) { + await this.paymentAdyenPage.afterPayButton.click(); + 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; case PaymentType.Paypal: await this.selectPaypal(); // TODO: Click paypal button await this.nextPage(); - await this.paymentPage.navigateToPaypalCheckout(); - await this.paypalPage.completePaypalPurchase(paymentDetails); + const paypalPage = experiments?.isAdyenPayments + ? 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; case PaymentType.PayAtService: - await this.selectPayAtService(isRecalVehicle); + await this.selectPayAtService(isRecalVehicle!); await this.nextPage(); break; case PaymentType.PayWithInsurance: @@ -527,7 +555,7 @@ export class PaymentMethodPage extends BasePage { @step("PaymentMethodPage >> Select Payment Method: ") async handlePaymentMethodPage(testData: Partial) { - const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM } = testData; + const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM, experiments } = testData; await this.validateProgressBar(ProgressBarPercentages.PaymentMethodPage); await this.validatePaymentDetailsPage(testData); @@ -542,7 +570,8 @@ export class PaymentMethodPage extends BasePage { await this.verifyVAPS(); } if (paymentDetails?.paymentType) { - await this.executePayment(paymentDetails!, isRecalVehicle!); + // await this.executePayment(paymentDetails!, isRecalVehicle!, experiments!); + await this.executePayment(testData); } else { await this.nextPage(); } diff --git a/playwright-tests/pages/PaymentPage.ts b/playwright-tests/pages/PaymentPage.ts index 9bc95fb66..ca51450df 100644 --- a/playwright-tests/pages/PaymentPage.ts +++ b/playwright-tests/pages/PaymentPage.ts @@ -1,6 +1,7 @@ import { type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; import { IPaymentDetails } from 'safelite-playwright-core'; +import { PaypalPage } from './PaypalPage'; export class PaymentPage extends BasePage { readonly page: Page; @@ -43,8 +44,8 @@ export class PaymentPage extends BasePage { await this.submitPaymentButton.click(); } - async navigateToPaypalCheckout() { + async navigateToPaypalCheckout(): Promise { await this.payPalButton.click(); + return new PaypalPage(this.page); } - } \ No newline at end of file diff --git a/playwright-tests/pages/PaypalPage.ts b/playwright-tests/pages/PaypalPage.ts index d0601ffcb..650acc27a 100644 --- a/playwright-tests/pages/PaypalPage.ts +++ b/playwright-tests/pages/PaypalPage.ts @@ -1,6 +1,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; -import { IPaymentDetails } from 'safelite-playwright-core'; +import { ITestData } from 'framework/TestData'; export class PaypalPage extends BasePage { readonly page: Page; @@ -30,24 +30,28 @@ export class PaypalPage extends BasePage { this.tryAnotherWayButton = page.getByRole('button', { name: 'Try another way' }); } - async completePaypalPurchase(paymentDetails: IPaymentDetails){ + async completePaypalPurchase(testData: Partial){ + + const {paymentDetails, experiments } = testData; // Wait for the PayPal login page to load 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 if (await this.loginWithPasswordButton.isVisible()) { await this.loginWithPasswordButton.click(); - await this.passwordTextBox.fill(paymentDetails.password!); + await this.passwordTextBox.fill(paymentDetails!.password!); await this.paypalLoginButton.click(); await this.completePurchaseButton.click(); } else { await this.usernameTextBox.waitFor({ state: 'visible' }); 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.tryAnotherWayButton.click(); - await this.usePasswordInsteadButton.click(); - await this.passwordTextBox.fill(paymentDetails.password!); + if (!experiments!.isAdyenPayments){ + await this.tryAnotherWayButton.click(); + await this.usePasswordInsteadButton.click(); + } + await this.passwordTextBox.fill(paymentDetails!.password!); await this.paypalLoginButton.click(); await this.payWithRadioButton.click(); await this.page.waitForTimeout(2000);