From de2f5017738de3b181e3c16f8c027457fa269912 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Wed, 30 Apr 2025 10:01:27 -0400 Subject: [PATCH 1/2] Revert "Merge pull request #2458 from Safelite/feature/CASH-530" This reverts commit dafc79cd64473f12570d1af50f7e251a92217bbd, reversing changes made to 8ae73d4b6b8919bd0946351be1e935c31da8b4db. --- playwright-tests/.env.dev | 4 +- .../business-logic/types/CustomerDetails.ts | 2 - .../business-logic/types/ITestPages.ts | 4 +- playwright-tests/business-logic/types/Step.ts | 27 - .../business-logic/types/TestCase.ts | 4 +- playwright-tests/pages/CCPolicyInfoPage.ts | 10 - .../pages/CapabilityQuestionsPage.ts | 16 - playwright-tests/pages/ContactDetailsPage.ts | 9 - .../pages/CoverageStatementPage.ts | 11 +- playwright-tests/pages/DuplicateCheckPage.ts | 7 - playwright-tests/pages/EndorsementsPage.ts | 10 - playwright-tests/pages/EstimatePage.ts | 10 +- .../pages/InsuranceCompanyPage.ts | 11 - .../pages/MoldingQuestionsPage.ts | 10 - .../pages/OrderConfirmationPage.ts | 70 +-- playwright-tests/pages/PartQuestionPage.ts | 10 - playwright-tests/pages/PaymentMethodPage.ts | 341 ++++--------- playwright-tests/pages/PolicyDriverPage.ts | 10 +- .../pages/PolicyInfoSubmittedPage.ts | 8 +- playwright-tests/pages/PolicyVehiclesPage.ts | 27 - .../pages/RecalibrationInfoPage.ts | 6 - playwright-tests/pages/SchedulePage.ts | 62 +-- playwright-tests/pages/ServiceLocationPage.ts | 15 +- playwright-tests/pages/ServicePackagesPage.ts | 53 -- playwright-tests/pages/ServiceZipPage.ts | 20 - playwright-tests/pages/VehicleDamagePage.ts | 20 - .../pages/VehicleLookupAddressPage.ts | 9 - .../pages/VehicleLookupLicensePage.ts | 10 - playwright-tests/pages/VehiclePartsPage.ts | 10 - .../pages/VehicleSelectionPage.ts | 18 - playwright-tests/pages/VerifyDetailsPage.ts | 9 - playwright-tests/pages/VinLookupPage.ts | 10 - playwright-tests/tests/0000__M.test.ts | 472 ++++++++++++++---- .../tests/CashRepairMobileCreditCard.ts | 2 +- .../tests/CashReplaceDynamicRecalMobile.ts | 2 +- .../tests/CashReplaceMultiGlassMobile.ts | 2 +- .../CashReplaceSafeliteCanNotRecalMobile.ts | 2 +- .../tests/CashReplaceVinMobile.ts | 2 +- 38 files changed, 508 insertions(+), 817 deletions(-) delete mode 100644 playwright-tests/business-logic/types/Step.ts delete mode 100644 playwright-tests/pages/ServiceZipPage.ts diff --git a/playwright-tests/.env.dev b/playwright-tests/.env.dev index c89292674..8c064ba62 100644 --- a/playwright-tests/.env.dev +++ b/playwright-tests/.env.dev @@ -23,6 +23,4 @@ CCIS_API_URL="https://api.test.belronus.io" ADMIN_SERVICE_API_URL="https://issadminapi.dev.sagaws.net/iss-admin/api/v1/" # API authentication (replace with actual value when running tests) -CCIS_API_AUTH="undefined" - -SKIP_CONTENT_SITE=false \ No newline at end of file +CCIS_API_AUTH="undefined" \ No newline at end of file diff --git a/playwright-tests/business-logic/types/CustomerDetails.ts b/playwright-tests/business-logic/types/CustomerDetails.ts index 0ea0e9605..bb1f08b96 100644 --- a/playwright-tests/business-logic/types/CustomerDetails.ts +++ b/playwright-tests/business-logic/types/CustomerDetails.ts @@ -9,8 +9,6 @@ export interface ICustomerDetails { notes: string, address: IAddress, apptDate?: string, - apptTime?: string, - apptDuration?: string, packagePrice?: string } diff --git a/playwright-tests/business-logic/types/ITestPages.ts b/playwright-tests/business-logic/types/ITestPages.ts index d8ba7e5bd..77b9689e7 100644 --- a/playwright-tests/business-logic/types/ITestPages.ts +++ b/playwright-tests/business-logic/types/ITestPages.ts @@ -27,7 +27,6 @@ import { CoverageStatementPage } from "../../pages/CoverageStatementPage" import { VerifyDetailsPage } from "../../pages/VerifyDetailsPage" import { EndorsementsPage } from "../../pages/EndorsementsPage" import { PolicyDriverPage } from "../../pages/PolicyDriverPage" -import { ServiceZipPage } from "../../pages/ServiceZipPage" // File containing interface for all Page Object Models export default interface ITestPages { @@ -49,8 +48,7 @@ export default interface ITestPages { recalibrationInfoPage: RecalibrationInfoPage, schedulePage: SchedulePage, serviceLocationPage: ServiceLocationPage, - servicePackagesPage: ServicePackagesPage, - serviceZipPage: ServiceZipPage, + servicePackagePage: ServicePackagesPage, vehicleDamagePage: VehicleDamagePage, vehicleLookupAddressPage: VehicleLookupAddressPage, vehicleLookupLicensePage: VehicleLookupLicensePage, diff --git a/playwright-tests/business-logic/types/Step.ts b/playwright-tests/business-logic/types/Step.ts deleted file mode 100644 index 693e34da2..000000000 --- a/playwright-tests/business-logic/types/Step.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { test } from '@business-logic/types/Test'; - -export type Context = { - kind: string; - name: string | symbol; - access: { - get?(): unknown; - set?(value: unknown): void; - has?(value: unknown): boolean; - }; - private?: boolean; - static?: boolean; - addInitializer?(initializer: () => void): void; -}; - -export type Method = (...args: any[]) => any; - -export function step(label: string): Method { - return (value: T, context: Context): Method => { - return function (this: any, ...args: any[]): Promise { - // console.log(`Method ${value.name} has been decorated`, context, args, label); - return test.step(label, async () => { - return value.call(this, ...args); - }); - }; - }; -} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/TestCase.ts b/playwright-tests/business-logic/types/TestCase.ts index ab45a6078..5fe3ae857 100644 --- a/playwright-tests/business-logic/types/TestCase.ts +++ b/playwright-tests/business-logic/types/TestCase.ts @@ -41,7 +41,6 @@ import { CoverageStatementPage } from "../../pages/CoverageStatementPage"; import { VerifyDetailsPage } from "../../pages/VerifyDetailsPage"; import { EndorsementsPage } from "../../pages/EndorsementsPage"; import { PolicyDriverPage } from "../../pages/PolicyDriverPage"; -import { ServiceZipPage } from "../../pages/ServiceZipPage"; // File for Test Case Class @@ -165,8 +164,7 @@ export default class TestCase extends DisposableBase implements ITestCase { recalibrationInfoPage: new RecalibrationInfoPage(page), schedulePage: new SchedulePage(page), serviceLocationPage: new ServiceLocationPage(page), - servicePackagesPage: new ServicePackagesPage(page), - serviceZipPage: new ServiceZipPage(page), + servicePackagePage: new ServicePackagesPage(page), vehicleDamagePage: new VehicleDamagePage(page), vehicleLookupAddressPage: new VehicleLookupAddressPage(page), vehicleLookupLicensePage: new VehicleLookupLicensePage(page), diff --git a/playwright-tests/pages/CCPolicyInfoPage.ts b/playwright-tests/pages/CCPolicyInfoPage.ts index 7151e3c06..42ee3632b 100644 --- a/playwright-tests/pages/CCPolicyInfoPage.ts +++ b/playwright-tests/pages/CCPolicyInfoPage.ts @@ -2,8 +2,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { IClaimDetails, ICustomerDetails } from '@business-logic/types/CustomerDetails'; import { InsuranceBasePage } from './InsuranceBasePage'; import { STATE_ABBREVIATIONS } from '@business-logic/constants/StateAbbreviation'; -import { ITestData } from '@business-logic/types/ITestData'; -import { step } from '@business-logic/types/Step'; export class CCPolicyInfoPage extends InsuranceBasePage { readonly policyNumber: Locator; @@ -72,12 +70,4 @@ export class CCPolicyInfoPage extends InsuranceBasePage { throw error; } } - - @step("CCPolicyInfoPage >> Fill out claim information") - async handleCCPolicyInfoPage(testData: Partial) { - const { customerDetails, claimDetails } = testData; - - await this.populatePage(customerDetails!, claimDetails!, await this.hasCityInfo()); - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/CapabilityQuestionsPage.ts b/playwright-tests/pages/CapabilityQuestionsPage.ts index 092a424f3..e2b6260a3 100644 --- a/playwright-tests/pages/CapabilityQuestionsPage.ts +++ b/playwright-tests/pages/CapabilityQuestionsPage.ts @@ -1,7 +1,5 @@ import { Page } from "@playwright/test"; import { PartQuestionsPage } from "./PartQuestionPage"; -import { step } from "@business-logic/types/Step"; -import { ITestData } from "@business-logic/types/ITestData"; export default class CapabilityQuestionsPage extends PartQuestionsPage { url = process.env['BASE_URL']! + '/fmg/?fmgPage=capability-questions'; @@ -9,18 +7,4 @@ export default class CapabilityQuestionsPage extends PartQuestionsPage { constructor(page: Page) { super(page); } - - @step("CapabilityQuestionsPage >> Select Capability Questions: ") - async handleCapabilityQuestionsPage(testCase: Partial) { - const { capabilityQuestions } = testCase; - - // Validate the capability questions are on the page - await this.validatePartQuestions(capabilityQuestions!); - - // Select the capability question responses - await this.selectPartQuestionResponses(capabilityQuestions!); - - // Proceed to the next page - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/ContactDetailsPage.ts b/playwright-tests/pages/ContactDetailsPage.ts index 2641f6436..0b10be42a 100644 --- a/playwright-tests/pages/ContactDetailsPage.ts +++ b/playwright-tests/pages/ContactDetailsPage.ts @@ -1,8 +1,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; import { ICustomerDetails } from '@business-logic/types/CustomerDetails'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class ContactDetailsPage extends BasePage { readonly page: Page; @@ -41,11 +39,4 @@ export class ContactDetailsPage extends BasePage { await this.notesTextBox.fill(notes); } } - - @step("ContactDetailsPage >> Enter contact details: ") - async handleContactDetailsPage(testData: Partial) { - const { customerDetails } = testData; - await this.enterContactDetails(customerDetails!); - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/CoverageStatementPage.ts b/playwright-tests/pages/CoverageStatementPage.ts index 7085c6794..a140604f7 100644 --- a/playwright-tests/pages/CoverageStatementPage.ts +++ b/playwright-tests/pages/CoverageStatementPage.ts @@ -1,8 +1,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { InsuranceBasePage } from './InsuranceBasePage'; import { IClaimDetails } from '@business-logic/types/CustomerDetails'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class CoverageStatementPage extends InsuranceBasePage { readonly page: Page; @@ -56,12 +54,5 @@ export class CoverageStatementPage extends InsuranceBasePage { async validateUnverifiedText(){ await expect(this.verfiyingCoverageText).toBeEnabled(); } - - @step("CoverageStatementPage >> Next page: ") - async handleCoverageStatementPage(testData: Partial) { - const { claimDetails } = testData; - - await this.validateDeductibleAmount(claimDetails!); - await this.nextPage(); - } + } \ No newline at end of file diff --git a/playwright-tests/pages/DuplicateCheckPage.ts b/playwright-tests/pages/DuplicateCheckPage.ts index e5f44492c..58d5463dd 100644 --- a/playwright-tests/pages/DuplicateCheckPage.ts +++ b/playwright-tests/pages/DuplicateCheckPage.ts @@ -1,7 +1,5 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { InsuranceBasePage } from './InsuranceBasePage'; -import { ITestData } from '@business-logic/types/ITestData'; -import { step } from '@business-logic/types/Step'; export class DuplicateCheckPage extends InsuranceBasePage { readonly page: Page; @@ -20,9 +18,4 @@ export class DuplicateCheckPage extends InsuranceBasePage { await this.page.waitForLoadState(); } - @step("DuplicateCheckPage >> Start a new claim: ") - async handleDuplicateCheckPage(testCase: Partial) { - await this.startNewClaim(); - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/EndorsementsPage.ts b/playwright-tests/pages/EndorsementsPage.ts index 3b63dcac0..08d69e4e3 100644 --- a/playwright-tests/pages/EndorsementsPage.ts +++ b/playwright-tests/pages/EndorsementsPage.ts @@ -3,8 +3,6 @@ import { BasePage } from './BasePage'; import { IEndorsementDetails } from '@business-logic/types/CustomerDetails'; import { EndorsementType } from '@business-logic/types/Enums'; import { InsuranceBasePage } from './InsuranceBasePage'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class EndorsementsPage extends InsuranceBasePage { readonly page: Page; @@ -54,12 +52,4 @@ export class EndorsementsPage extends InsuranceBasePage { } } } - - @step("EndorsementsPage >> Select endorsements: ") - async handleEndorsementsPage(testData: Partial) { - const { endorsements } = testData; - await this.verifyEndorsements(endorsements!); - await this.selectEndorsements(endorsements!); - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/EstimatePage.ts b/playwright-tests/pages/EstimatePage.ts index 2beb507b3..141fa73a6 100644 --- a/playwright-tests/pages/EstimatePage.ts +++ b/playwright-tests/pages/EstimatePage.ts @@ -5,8 +5,6 @@ import { IVehicleDetails } from '@business-logic/types/CustomerDetails'; import { VinLookupPage } from './VinLookupPage'; import { VehicleLookupAddressPage } from './VehicleLookupAddressPage'; import { VehicleLookupLicensePage } from './VehicleLookupLicensePage'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class EstimatePage extends BasePage { readonly page: Page; @@ -22,7 +20,7 @@ export class EstimatePage extends BasePage { constructor(page: Page) { super(page); this.page = page; - this.vinLookupButton = page.locator('label').filter({ hasText: 'Provide my VIN' }).locator('div'); + this.vinLookupButton = page.getByLabel('Provide my VIN', { exact: true }); this.zipLookupButton = page.locator('label').filter({ hasText: 'I\'d rather not share my VIN' }).locator('div'); this.addressLookupButton = page.getByLabel('Provide my home address', { exact: true }); this.licenseLookupButton = page.getByLabel('Provide my license plate #', { exact: true }); @@ -70,10 +68,4 @@ export class EstimatePage extends BasePage { async selectZipLookup(){ await this.zipLookupButton.click(); } - - @step("EstimatePage >> Select Lookup Type") - async handleEstimatePage(testData: Partial) { - const { vehicleDetails } = testData; - await this.vehicleLookup(vehicleDetails!); - } } diff --git a/playwright-tests/pages/InsuranceCompanyPage.ts b/playwright-tests/pages/InsuranceCompanyPage.ts index e2d63ec2d..6bc125225 100644 --- a/playwright-tests/pages/InsuranceCompanyPage.ts +++ b/playwright-tests/pages/InsuranceCompanyPage.ts @@ -1,9 +1,6 @@ import { type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; import { IClaimDetails } from '@business-logic/types/CustomerDetails'; -import { step } from '@business-logic/types/Step'; -import TestCase from '@business-logic/types/TestCase'; -import { ITestData } from '@business-logic/types/ITestData'; export class InsuranceCompanyPage extends BasePage { @@ -47,12 +44,4 @@ export class InsuranceCompanyPage extends BasePage { } } } - - @step("InsuranceCompanyPage >> Enter insurance company: ") - async handleInsuranceCompanyPage(testData: Partial) { - const { claimDetails } = testData; - - await this.enterInsuranceCompany(claimDetails!.client!); - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/MoldingQuestionsPage.ts b/playwright-tests/pages/MoldingQuestionsPage.ts index 5671900ff..3221a35e2 100644 --- a/playwright-tests/pages/MoldingQuestionsPage.ts +++ b/playwright-tests/pages/MoldingQuestionsPage.ts @@ -1,7 +1,5 @@ import { Page } from "@playwright/test"; import { PartQuestionsPage } from "./PartQuestionPage"; -import { step } from "@business-logic/types/Step"; -import { ITestData } from "@business-logic/types/ITestData"; export default class MoldingQuestionsPage extends PartQuestionsPage { url = process.env['BASE_URL']! + '/fmg/?fmgPage=molding-questions'; @@ -9,12 +7,4 @@ export default class MoldingQuestionsPage extends PartQuestionsPage { constructor(page: Page) { super(page); } - - @step("MoldingQuestionsPage >> Select Molding Questions: ") - async handleMoldingQuestionsPage(testCase: Partial) { - const { moldingQuestions } = testCase; - await this.validatePartQuestions(moldingQuestions!); - await this.selectPartQuestionResponses(moldingQuestions!); - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/OrderConfirmationPage.ts b/playwright-tests/pages/OrderConfirmationPage.ts index 1c6158c68..c8b5f63f6 100644 --- a/playwright-tests/pages/OrderConfirmationPage.ts +++ b/playwright-tests/pages/OrderConfirmationPage.ts @@ -1,10 +1,8 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; -import { test } from '@business-logic/types/Test'; import { ICustomerDetails, IVehicleDetails } from '@business-logic/types/CustomerDetails'; -import { ServicePackage, PaymentType, PaymentMethod, AppointmentType } from '@business-logic/types/Enums'; +import { ServicePackage, PaymentType, PaymentMethod } from '@business-logic/types/Enums'; import { ITestData } from '@business-logic/types/ITestData'; -import { step } from '@business-logic/types/Step'; export class OrderConfirmationPage extends BasePage { readonly page: Page; @@ -19,7 +17,6 @@ export class OrderConfirmationPage extends BasePage { readonly cartServicePackageText: Locator; readonly winshieldWiper: Locator; readonly rainDefense: Locator; - readonly appointmentSummarySection: Locator; url = process.env['BASE_URL']! + '/fmg/?fmgPage=confirmation'; @@ -37,7 +34,6 @@ export class OrderConfirmationPage extends BasePage { this.subtotalText = this.page.locator('.sub-total'); this.finalAmountDue = this.page.locator('div.amount-due'); this.cartServicePackageText = this.cartServicePackageText = this.page.locator('.cart-panel'); - this.appointmentSummarySection = this.page.locator('.main .scheduleText, .main .add-to-calendar-text, .main .appointment-text, .main .duration-text-block'); // this.validateURL(this.url); } @@ -46,10 +42,10 @@ export class OrderConfirmationPage extends BasePage { const { vehicleDetails, customerDetails, servicePackage, promoCode, isPolicyFound, claimDetails, paymentDetails, isUseVehicleOnPolicy, paymentMethod } = testData; await this.serviceText.waitFor({ state: "visible" }); - - expect.soft((await this.getActualAppointmentSummary()).map(item => item.toLowerCase())).toEqual((await this.getExpectedAppointmentSummary(testData)).map(item => item.toLowerCase())); // Grab text + const serviceTextValue = await this.serviceText.textContent(); + const apptDateValue = await this.apptDateText.textContent(); const emailTextValue = await this.emailText.textContent(); const servicePackageValue = await this.cartServicePackageText.textContent(); const amountDueValue = await this.amountDueText.textContent(); @@ -61,6 +57,8 @@ export class OrderConfirmationPage extends BasePage { const servicePackageAmt = Number.parseFloat(servicePackageValue!.split('$')[1].replaceAll(',', '')); // General Validations + expect.soft(serviceTextValue).toContain(`${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`); + expect.soft(apptDateValue).toContain(customerDetails!.apptDate); expect.soft(emailTextValue).toContain(customerDetails!.email); // Service package validations @@ -125,63 +123,9 @@ export class OrderConfirmationPage extends BasePage { } } - async getFormattedAppointmentDate(appointmentDate: string) { - - // Parse the original date - let parsedAppointmentDate = new Date(`${appointmentDate}` + 'T00:00:00'); - - // Format the new date as a string - let updatedAppointmentDate = parsedAppointmentDate.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' }); - return updatedAppointmentDate; - } - async logOrderNumber() { const sessionStorage = JSON.parse(await this.page.evaluate('sessionStorage.getItem(\'submittedState\')')); - return sessionStorage.order.workOrderNumber; + return sessionStorage.workOrderNumber; } - - async getActualAppointmentSummary(): Promise { - let appointmentSummary: string[] = []; - for (let element of await this.appointmentSummarySection.all()){ - let text = (await element.textContent() || "").replaceAll(/\u00A0| /g, ' '); - appointmentSummary.push(text.trim()); - } - return appointmentSummary; - } - - async getExpectedAppointmentSummary(testData: Partial): Promise { - const { appointmentDetails, customerDetails, vehicleDetails } = testData; - let appointmentSummary: string[] = []; - - // Format the expected appointment date - const formattedExpectedAppointmentDate = await this.getFormattedAppointmentDate(customerDetails!.apptDate!); - appointmentSummary.push( - appointmentDetails?.serviceLocation == AppointmentType.Mobile - ? formattedExpectedAppointmentDate + `${customerDetails!.apptTime?.replace("arriving between", "Between").replaceAll(":00", "")}` - : appointmentDetails?.serviceLocation == AppointmentType.InShop - ? formattedExpectedAppointmentDate + `${customerDetails!.apptTime}` - : formattedExpectedAppointmentDate + "Drop off before 9:30 AM" - ); - appointmentSummary.push("Add to calendar"); - appointmentSummary.push( - appointmentDetails?.serviceLocation == AppointmentType.Mobile - ? appointmentDetails?.serviceAddress - ? ("We're coming to you at" + appointmentDetails.serviceAddress.street + ", " + appointmentDetails.serviceAddress.city + ", " + appointmentDetails.serviceAddress.state + " " + appointmentDetails.serviceAddress.postalCode + "to service your " + `${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`) - : "" - : appointmentDetails?.shopAddress - ? ("You're going to a Safelite shop at" + appointmentDetails.shopAddress + "to service your " + `${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`) - : ""); - appointmentSummary.push("Duration: " + customerDetails!.apptDuration!); - - return appointmentSummary; - } - - @step("OrderConfirmationPage >> Validate order") - async verifyOrderConfirmationPage(testData: Partial) { - await this.validateOrderConfirmationPage(testData); - const workOrderNumber = await this.logOrderNumber(); - await test.step(`Session Storage Work Order Number: ${workOrderNumber}`, async () => { - console.log(`Session Storage Work Order Number: ${workOrderNumber}`); - }); - } + } \ No newline at end of file diff --git a/playwright-tests/pages/PartQuestionPage.ts b/playwright-tests/pages/PartQuestionPage.ts index 831a27dc1..02c26490f 100644 --- a/playwright-tests/pages/PartQuestionPage.ts +++ b/playwright-tests/pages/PartQuestionPage.ts @@ -1,8 +1,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; import { IPartQuestion } from '@business-logic/types/CustomerDetails'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class PartQuestionsPage extends BasePage { readonly page: Page; @@ -58,12 +56,4 @@ export class PartQuestionsPage extends BasePage { } } } - - @step("PartQuestionsPage >> Select Vehicle Part Question Responses") - async handlePartQuestionsPage(testData: Partial) { - const { partQuestions } = testData; - await this.validatePartQuestions(partQuestions!); - await this.selectPartQuestionResponses(partQuestions!); - await this.nextPage(); - } } diff --git a/playwright-tests/pages/PaymentMethodPage.ts b/playwright-tests/pages/PaymentMethodPage.ts index 92d0da651..c859aa916 100644 --- a/playwright-tests/pages/PaymentMethodPage.ts +++ b/playwright-tests/pages/PaymentMethodPage.ts @@ -1,12 +1,11 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; import { IPaymentDetails } from '@business-logic/types/CustomerDetails'; -import { AppointmentType, PaymentMethod, PaymentType, ServicePackage, VehicleDamage } from '@business-logic/types/Enums'; +import { PaymentMethod, PaymentType, ServicePackage, VehicleDamage } from '@business-logic/types/Enums'; import { PaymentPage } from './PaymentPage'; import { AfterpayPage } from './AfterpayPage'; import { PaypalPage } from './PaypalPage'; import { ITestData } from '@business-logic/types/ITestData'; -import { step } from '@business-logic/types/Step'; export class PaymentMethodPage extends BasePage { readonly page: Page; @@ -18,7 +17,6 @@ export class PaymentMethodPage extends BasePage { readonly payInFourButton: Locator; readonly amountDueTextField: Locator; readonly amountDueDropDown: Locator; - readonly appointmentDetailsSection: Locator; readonly appointmentDetailsDropdown: Locator; readonly subtotalAmountTextField: Locator; readonly submitButton: Locator; @@ -50,7 +48,6 @@ export class PaymentMethodPage extends BasePage { this.payAtServiceButton = this.page.locator('[buttonlabel="Pay at my appointment"]'); //this.page.getByText('Pay at time of service'); this.amountDueTextField = this.page.getByLabel('expand cart').locator('.amount-due'); this.amountDueDropDown = this.page.getByLabel('expand cart'); - this.appointmentDetailsSection = this.page.locator('div .appt-details-snapshot'); this.appointmentDetailsDropdown = this.page.getByLabel('expand appointment details'); this.payWithInsuranceButton = this.page.locator('div').filter({ hasText: /^Pay with insurance$/ }).nth(1); this.subtotalAmountTextField = this.page.locator('.sub-total span').nth(1); @@ -91,27 +88,110 @@ export class PaymentMethodPage extends BasePage { // Wait for review table to be visible to ensure page is loaded await this.appointmentDetailsDropdown.waitFor({state: "visible"}); - await this.appointmentDetailsDropdown.click(); - - let expectedAppointmentDetails = new Map(); - expectedAppointmentDetails = await this.getExpectedVehicleDetails(testData, expectedAppointmentDetails); - expectedAppointmentDetails = await this.getExpectedVehicleDamage(testData, expectedAppointmentDetails); - expectedAppointmentDetails = await this.expectedServicePackageDetails(testData, expectedAppointmentDetails); - expectedAppointmentDetails = await this.getExpectedServiceLocation(testData, expectedAppointmentDetails); - expectedAppointmentDetails = await this.getExpectedAppointmentDate(testData, expectedAppointmentDetails); - expectedAppointmentDetails = await this.getExpectedCustomerDetails(testData, expectedAppointmentDetails); - - let actualAppointmentDetails = await this.getActualAppointmentDetails(); - - for (const key in expectedAppointmentDetails) { - expect.soft(actualAppointmentDetails[key]?.map(item => item.toLowerCase())) - .toEqual(expectedAppointmentDetails[key].map(item => item.toLowerCase())); - } - - // Expand cart to see all details + await this.appointmentDetailsDropdown.click(); // Expand cart to see all details await this.amountDueDropDown.click(); await this.reviewTable.waitFor({ state: "visible" }); + // Helper method to get content lines from a section + const getSectionContent = async (section: Locator) => { + // First make sure section exists + if (await section.count() === 0) return []; + + // Find all content lines within this section + const contentLines = await section.locator('div.small.review-block-content').allInnerTexts(); + return contentLines; + }; + + // Validate vehicle information + const vehicleContent = await getSectionContent(this.vehicleSection); + if (vehicleContent.length > 0) { + const vehicleText = vehicleContent[0]; + expect.soft(vehicleText).toContain(`${vehicleDetails?.year} ${vehicleDetails?.make} ${vehicleDetails?.model}`); + } + + // Validate damage type + + // TODO: Work out logic on how to verify vehicle Damage in payment details with vehicleDamage + + // const damageContent = await getSectionContent(this.damageSection); + // if (vehicleDamage && damageContent.length > 0) { + // const damageText = damageContent[0]; + + // // For each damage type in the array, check if its display text is in the damage content + // for (const damage of vehicleDamage) { + // const expectedDamageText = this.getDamageDisplayText(damage); + + // // If this is a single damage item, it should match exactly + // if (vehicleDamage.length === 1) { + // expect.soft(damageText).toContain(expectedDamageText); + // } else { + // // For multiple damages, check if any of the damage content lines contain this damage type + // const damageFound = damageContent.some(content => + // content.includes(expectedDamageText) + // ); + // expect.soft(damageFound).toBeTruthy(); + // } + // } + // } + + // Validate service details based on package + const serviceContent = await getSectionContent(this.serviceDetailsSection); + if (servicePackage && serviceContent.length > 0) { + //TODO: Add service package validation + // move over logic from Kishan's code + + // Additional validations for Standard and Premium packages + if (servicePackage === ServicePackage.Premium || servicePackage === ServicePackage.Standard) { + await expect.soft(this.wiperBladesText).toBeVisible(); + } + + if (servicePackage === ServicePackage.Premium) { + await expect.soft(this.rainDefenseText).toBeVisible(); + } + } + + // Validate service location + const locationContent = await getSectionContent(this.serviceLocationSection); + if (appointmentDetails?.serviceLocation && locationContent.length > 0) { + // Find the title element of the service location section + const serviceLocationTitle = this.serviceLocationSection.locator('span').first(); + const serviceLocationValue = await serviceLocationTitle.innerText(); + + // Check for mobile/inshop service wording + if (appointmentDetails.serviceLocation.toString().includes('Mobile')) { + expect.soft(serviceLocationValue).toContain("We're coming to you"); + } else if (appointmentDetails.serviceLocation.toString().includes('InShop')) { + expect.soft(serviceLocationValue).toContain("Bring to shop"); + } + + // Validate address if available + if (appointmentDetails.serviceAddress && locationContent.length > 0) { + const addressText = locationContent[0].toLowerCase(); + expect.soft(addressText).toContain(appointmentDetails.serviceAddress.street.toLowerCase()); + } + } + + // Validate appointment date/time + const appointmentContent = await getSectionContent(this.appointmentDateSection); + if (customerDetails?.apptDate && appointmentContent.length > 0) { + const appointmentText = appointmentContent[0]; + expect.soft(appointmentText).toContain(customerDetails.apptDate); + } + + // Validate contact details + const contactContent = await getSectionContent(this.contactDetailsSection); + if (customerDetails && contactContent.length > 0) { + const fullName = `${customerDetails.firstName} ${customerDetails.lastName}`; + const email = customerDetails.email; + const phone = customerDetails.phoneNumber; + + // Check if contact details are present + const contactTextJoined = contactContent.join(' '); + expect.soft(contactTextJoined).toContain(fullName); + expect.soft(contactTextJoined).toContain(email); + expect.soft(contactTextJoined).toContain(phone); + } + // Cart Validation // Get pricing information from cart panel if (await this.subtotalText.isVisible()) { @@ -259,221 +339,4 @@ l } } - async getActualAppointmentDetails(): Promise { - let actualAppointmentDetails = new Map(); - let appointmentDetailsSubSections = await this.appointmentDetailsSection.locator('div .py-3').all(); - - for (let element of appointmentDetailsSubSections) { - let label = await element.locator('div .text-block').innerText(); - //added to trim the text to remove any leading or trailing spaces (example: "expert installation " to "expert installation") - let value = (await element.locator('.review-block-content').allInnerTexts() as string[]).map(item => item.trim()); - actualAppointmentDetails[label] = value; - } - return actualAppointmentDetails; - } - - async getExpectedVehicleDetails(testdata: Partial, expectedServicePackageDetails: Map): Promise { - const { vehicleDetails } = testdata; - expectedServicePackageDetails["Vehicle"] = [vehicleDetails?.year + " " + vehicleDetails?.make + " " + vehicleDetails?.model]; - return expectedServicePackageDetails; - } - - async getExpectedVehicleDamage(testdata: Partial, expectedServicePackageDetails: Map): Promise { - - const {vehicleDamage} = testdata; - - let vehicleDamageText: string[] = []; - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldOneChip)) { - vehicleDamageText.push("Windshield repair - 1 chip"); - } - - else if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldTwoChips)) { - vehicleDamageText.push("Windshield repair - 2 chips"); - } - - else if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldThreeChips)) { - vehicleDamageText.push("Windshield repair - 3 chips"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldCrack)) { - vehicleDamageText.push("Windshield crack"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverVentGlass)) { - vehicleDamageText.includes("Side door - Driver side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nVent glass" : vehicleDamageText.push("Side door - Driver side", "Vent glass"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverFrontDoor)) { - vehicleDamageText.includes("Side door - Driver side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nFront door" : vehicleDamageText.push("Side door - Driver side", "Front door"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverRearDoor)) { - vehicleDamageText.includes("Side door - Driver side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nBack door" : vehicleDamageText.push("Side door - Driver side", "Back door"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverQuarterPanel)) { - vehicleDamageText.includes("Side door - Driver side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nQuarter panel" : vehicleDamageText.push("Side door - Driver side", "Quarter panel"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerVentGlass)) { - vehicleDamageText.includes("Side door - Passenger side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nVent glass" : vehicleDamageText.push("Side door - Passenger side", "Vent glass"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerFrontDoor)) { - vehicleDamageText.includes("Side door - Passenger side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nFront door" : vehicleDamageText.push("Side door - Passenger side", "Front door"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerRearDoor)) { - vehicleDamageText.includes("Side door - Passenger side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nBack door" : vehicleDamageText.push("Side door - Passenger side", "Back door"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerQuarterPanel)) { - vehicleDamageText.includes("Side door - Passenger side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nQuarter panel" : vehicleDamageText.push("Side door - Passenger side", "Quarter panel"); - } - - if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.RearWindow || item == VehicleDamage.RearSliding)) { - vehicleDamageText.push("Rear window"); - } - - expectedServicePackageDetails["Damage"] = vehicleDamageText; - return expectedServicePackageDetails; - } - - async expectedServicePackageDetails(testData: Partial, expectedServicePackageDetails: Map): Promise { - const {servicePackage} = testData; - - let localStorage= JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')')); - let isRepair = localStorage.order.damage.isRepair as boolean; - let isInsurance = localStorage.order.payment.isInsurance as boolean; - let hasNonWindshieldGlass: boolean = false; - if (!isRepair) { - hasNonWindshieldGlass = localStorage.order.lineItems.glassParts.find((item: any) => item.partType !== "WINDSHIELD") ? true : false; - } - let isCaliforniaState = localStorage.order.serviceLocation.state as string == 'CA' ? true : false; - let hasRecalPart: boolean = false; - if (!isRepair) { - hasRecalPart = localStorage.order.lineItems.glassParts.find((item: any) => item.requiresRecalibration === true) ? true : false; - } - let recalRequired = !isRepair && (isInsurance || isCaliforniaState) && hasRecalPart - let stringForRepair: string[] = ["Expert windshield repair", "Exclusive resin sealant", "Nationwide lifetime guarantee"]; - let stringIfRecal = isRepair - ? "" - : recalRequired - ? " and recalibration" - : ""; - let stringForReplace: string[] = [hasNonWindshieldGlass ? "New replacement glass" : "New replacement windshield", "Expert installation" + `${stringIfRecal}`, "Nationwide lifetime warranty"]; - switch (servicePackage) - { - case ServicePackage.GlassOnly: - expectedServicePackageDetails["Glass service only"] = isRepair - ? stringForRepair - : stringForReplace; - break; - case ServicePackage.Standard: - stringForRepair.push("New wiper blades"); - stringForReplace.push("New wiper blades"); - expectedServicePackageDetails["Standard service"] = isRepair - ? stringForRepair - : stringForReplace; - break; - case ServicePackage.Premium: - stringForRepair.push("New wiper blades", "Rain Defense™"); - stringForReplace.push("New wiper blades", "Rain Defense™"); - expectedServicePackageDetails["Premium service"] = isRepair - ? stringForRepair - : stringForReplace; - break; - } - return expectedServicePackageDetails; - } - - async getExpectedServiceLocation(testData: Partial, expectedServicePackageDetails: Map): Promise { - const { appointmentDetails } = testData; - let serviceLocationTitle = appointmentDetails?.serviceLocation == AppointmentType.Mobile - ? "We're coming to you" - : "You're going to a Safelite shop"; - let serviceLocation: string[] = []; - - serviceLocation.push( - appointmentDetails?.serviceLocation == AppointmentType.Mobile - ? appointmentDetails?.serviceAddress - ? appointmentDetails.serviceAddress.street + ", " + appointmentDetails.serviceAddress.city + ", " + appointmentDetails.serviceAddress.state + " " + appointmentDetails.serviceAddress.postalCode - : "" - : appointmentDetails?.shopAddress - ? appointmentDetails.shopAddress - : "" - ); - - expectedServicePackageDetails[serviceLocationTitle] = serviceLocation; - return expectedServicePackageDetails; - } - - async getExpectedAppointmentDate(testData: Partial, expectedServicePackageDetails: Map): Promise { - const { customerDetails, appointmentDetails } = testData; - let appointmentDateText: string[] = []; - const isMobileAppointment = appointmentDetails?.serviceLocation == AppointmentType.Mobile - if (isMobileAppointment) { - let localStorage= JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')')); - let jobMinMinutes = localStorage.order.schedule.jobMinMinutes as number; - let jobMaxMinutes = localStorage.order.schedule.jobMaxMinutes as number; - - if (jobMinMinutes < 60) { - customerDetails!.apptDuration = `${jobMinMinutes} - ${jobMaxMinutes} minutes`; - } else { - const minHours = Math.floor(jobMinMinutes / 60); - const maxHours = Math.floor(jobMaxMinutes / 60); - customerDetails!.apptDuration = `${minHours} - ${maxHours} hours`; - } - } - - appointmentDateText.push( - customerDetails?.apptDate ? await this.getFormattedAppointmentDate(customerDetails.apptDate) + " " + customerDetails?.apptTime?.replace("-", "—") : "", - customerDetails?.apptDuration ? "Estimated appointment length: " + customerDetails.apptDuration : "" - ); - - expectedServicePackageDetails["Appointment date + time"] = appointmentDateText; - return expectedServicePackageDetails; - } - - async getExpectedCustomerDetails(testData: Partial, expectedServicePackageDetails: Map): Promise { - const { customerDetails } = testData; - let customerDetailsText: string[] = []; - - customerDetailsText.push( - customerDetails?.firstName.toUpperCase() + " " + customerDetails?.lastName.toUpperCase(), - customerDetails?.email ? customerDetails?.email.toUpperCase() : "", - customerDetails?.phoneNumber ? customerDetails?.phoneNumber : "", - "Opted out of text message updates" - ); - - expectedServicePackageDetails["Contact details"] = customerDetailsText; - return expectedServicePackageDetails; - } - - async getFormattedAppointmentDate(appointmentDate: string) { - - // Parse the original date - let parsedAppointmentDate = new Date(`${appointmentDate}` + 'T00:00:00'); - - // Format the new date as a string - let updatedAppointmentDate = parsedAppointmentDate.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); - return updatedAppointmentDate; - } - - @step("PaymentMethodPage >> Select Payment Method: ") - async handlePaymentMethodPage(testData: Partial) { - const { servicePackage, isRecalVehicle, paymentDetails } = testData; - - await this.validatePaymentDetailsPage(testData); - - // Verify VAPS wipers on backend for standard and premium packages - if (servicePackage === ServicePackage.Standard || servicePackage === ServicePackage.Premium) { - await this.verifyVAPS(); - } - if (paymentDetails?.paymentType) { - await this.executePayment(paymentDetails!, isRecalVehicle!); - } else { - await this.nextPage(); - } - } } \ No newline at end of file diff --git a/playwright-tests/pages/PolicyDriverPage.ts b/playwright-tests/pages/PolicyDriverPage.ts index 1bb140088..f6e56ca5f 100644 --- a/playwright-tests/pages/PolicyDriverPage.ts +++ b/playwright-tests/pages/PolicyDriverPage.ts @@ -1,8 +1,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { InsuranceBasePage } from './InsuranceBasePage'; import { ICustomerDetails } from '@business-logic/types/CustomerDetails'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class PolicyDriverPage extends InsuranceBasePage { readonly page: Page; @@ -49,11 +47,5 @@ export class PolicyDriverPage extends InsuranceBasePage { await this.driverNotListedOption.click(); } } - - @step("PolicyDriverPage >> Select policy driver: ") - async handlePolicyDriverPage(testData: Partial) { - const { customerDetails } = testData; - await this.selectPolicyDriver(customerDetails!); - await this.nextPage(); - } + } \ No newline at end of file diff --git a/playwright-tests/pages/PolicyInfoSubmittedPage.ts b/playwright-tests/pages/PolicyInfoSubmittedPage.ts index 0a4bf79be..6d5c96c5c 100644 --- a/playwright-tests/pages/PolicyInfoSubmittedPage.ts +++ b/playwright-tests/pages/PolicyInfoSubmittedPage.ts @@ -1,6 +1,5 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { InsuranceBasePage } from './InsuranceBasePage'; -import { step } from '@business-logic/types/Step'; export class PolicyInfoSubmittedPage extends InsuranceBasePage { readonly page: Page; @@ -21,10 +20,5 @@ export class PolicyInfoSubmittedPage extends InsuranceBasePage { const policyInfoMessage = await this.policyInfoMessage.textContent(); expect(policyInfoMessage).toBe("Your policy and vehicle information has been submitted for coverage verification") } - - @step("PolicyInfoSubmittedPage >> Verify policy info submitted: ") - async handlePolicyInfoSubmittedPage() { - await this.verifyPolicyInfoSubmitted(); - await this.nextPage(); - } + } \ No newline at end of file diff --git a/playwright-tests/pages/PolicyVehiclesPage.ts b/playwright-tests/pages/PolicyVehiclesPage.ts index 67776adf6..90882062d 100644 --- a/playwright-tests/pages/PolicyVehiclesPage.ts +++ b/playwright-tests/pages/PolicyVehiclesPage.ts @@ -1,8 +1,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { IVehicleDetails } from '@business-logic/types/CustomerDetails'; import { InsuranceBasePage } from './InsuranceBasePage'; -import { ITestData } from '@business-logic/types/ITestData'; -import { step } from '@business-logic/types/Step'; export class PolicyVehiclesPage extends InsuranceBasePage { readonly page: Page; @@ -28,29 +26,4 @@ export class PolicyVehiclesPage extends InsuranceBasePage { async selectVehicleNotListed(){ await this.page.getByText('Vehicle not listed').click(); } - - @step("PolicyVehiclesPage >> Select Vehicle: ") - async handlePolicyVehiclesPage(testCase: Partial) { - - const { vehicleDetails, otherVehiclesOnPolicy, isUseVehicleOnPolicy } = testCase; - - // Validate other vehicles on policy - if (otherVehiclesOnPolicy && otherVehiclesOnPolicy.length > 0) { - for (const vehicle of otherVehiclesOnPolicy) { - await this.validateVehicleIsOnPolicy(vehicle); - } - } - - // If vehicle is not on the policy, select new vehicle - if (!(isUseVehicleOnPolicy ?? true)) { - await this.selectVehicleNotListed(); - await this.nextPage(); - await this.selectVehicle(vehicleDetails!); - await this.nextPage(); - } else { - // Otherwise, select the vehicle entered in Safelite.com - await this.selectVehicle(vehicleDetails!); - await this.nextPage(); - } - } } \ No newline at end of file diff --git a/playwright-tests/pages/RecalibrationInfoPage.ts b/playwright-tests/pages/RecalibrationInfoPage.ts index 7310d6c16..0af4b0a57 100644 --- a/playwright-tests/pages/RecalibrationInfoPage.ts +++ b/playwright-tests/pages/RecalibrationInfoPage.ts @@ -1,6 +1,5 @@ import { Locator, Page } from "@playwright/test"; import { InsuranceBasePage } from "./InsuranceBasePage"; -import { step } from "@business-logic/types/Step"; export default class RecalibrationInfoPage extends InsuranceBasePage { url = process.env['BASE_URL']! + '/FixMyGlass/RecalibrationInfo.aspx'; @@ -12,9 +11,4 @@ export default class RecalibrationInfoPage extends InsuranceBasePage { this.continueButton = page.getByRole('button', { name: 'Continue' }); } - - @step("RecalibrationInfoPage >> Click continue button: ") - async handleRecalibrationInfoPage() { - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/SchedulePage.ts b/playwright-tests/pages/SchedulePage.ts index 5daa3060e..bea9e82bd 100644 --- a/playwright-tests/pages/SchedulePage.ts +++ b/playwright-tests/pages/SchedulePage.ts @@ -1,11 +1,8 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; -import { IAppointmentDetails, ICustomerDetails } from '@business-logic/types/CustomerDetails'; +import { IAppointmentDetails } from '@business-logic/types/CustomerDetails'; import { formatDate, formatTime } from '@impl/utils/DateUtils'; import { AppointmentType, ServiceLocation } from '@business-logic/types/Enums'; -import { time } from 'console'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class SchedulePage extends BasePage { readonly page: Page; @@ -16,8 +13,6 @@ export class SchedulePage extends BasePage { readonly dropOffButton: Locator; readonly dateText: Locator; readonly viewMoreDatesLink: Locator; - readonly appointmentDuration: Locator; - readonly timeSlots: Locator; constructor(page: Page) { super(page); @@ -28,8 +23,6 @@ export class SchedulePage extends BasePage { this.dropOffButton = this.page.getByText('Drop off your vehicle', { exact: true }); this.dateText = this.page.locator('label.modal-title'); this.viewMoreDatesLink = this.page.getByText(/View more dates/).first(); - this.appointmentDuration = this.page.locator('.duration-text-block'); - this.timeSlots = this.page.locator('fieldset[aria-labelledby=\'chooseTimeSlot\'] label'); } async scheduleAppointment(appointmentDetails: IAppointmentDetails) { @@ -47,47 +40,20 @@ export class SchedulePage extends BasePage { await this.modalContinueButton.click(); } - async scheduleFirstAppointment(customerDetails: ICustomerDetails) { - while (!(await this.firstAvailableDate.isVisible())) { + async scheduleFirstAppointment(serviceLocation: AppointmentType) { + if (await this.firstAvailableDate.isVisible()) { + await this.firstAvailableDate.click(); + } else { await this.viewMoreDatesLink.click(); + while(await this.firstAvailableDate.isHidden()){ + await this.viewMoreDatesLink.click(); + } + await this.firstAvailableDate.click(); } - await this.firstAvailableDate.click().then(async () => { - customerDetails.apptDate = `${await this.firstAvailableDate.getAttribute("id")}` - }); - // const timeSlots = this.timeSlots; - const timeSlotCount = await this.timeSlots.count(); - const randomIndex = Math.floor(Math.random() * timeSlotCount); - const timeSlot = this.timeSlots.nth(randomIndex); - await timeSlot.click().then(async () => { - customerDetails.apptTime = await this.getFormattedTimeSlot(timeSlot); - }); - // appointmentmentDetails.serviceLocation === AppointmentType.DropOff ? await this.dropOffButton.click() : await this.firstAvailableTime.click(); - customerDetails.apptDuration = (await this.appointmentDuration.innerText()).replace("Duration: ", ""); - await this.nextPage(); + serviceLocation === AppointmentType.DropOff ? await this.dropOffButton.click() : await this.firstAvailableTime.click(); + const apptDate = `${await this.dateText.allInnerTexts()}` + await this.modalContinueButton.click(); + return (apptDate); } - - async getFormattedTimeSlot(timeSlot: Locator) { - const selectedTimeSlot = await timeSlot.innerText(); - let formattedTimeSlot: string = ""; - if (selectedTimeSlot.toLowerCase().includes("drop off")) - { - formattedTimeSlot = selectedTimeSlot.includes("overnight") ? "drop off by 5:30 pm on the night of your scheduled appointment. Pick-up time dependent on shop schedule" : "drop off before 9:30 AM"; - } - else if (selectedTimeSlot.includes("-") || selectedTimeSlot.toLowerCase().includes("Earlybird")) - { - formattedTimeSlot = selectedTimeSlot.includes("Earlybird") ? "arriving between 8:00 AM - 12:00 PM" : `arriving between ${selectedTimeSlot}`; - } - else - { - formattedTimeSlot = `at ${selectedTimeSlot}`; - } - return formattedTimeSlot; - } - - @step("SchedulePage >> Schedule appointment: ") - async handleSchedulePage(testData: Partial) { - const { customerDetails } = testData; - await this.scheduleFirstAppointment(customerDetails!); - } -} \ No newline at end of file +} \ No newline at end of file diff --git a/playwright-tests/pages/ServiceLocationPage.ts b/playwright-tests/pages/ServiceLocationPage.ts index a31f38eb5..609f2d1db 100644 --- a/playwright-tests/pages/ServiceLocationPage.ts +++ b/playwright-tests/pages/ServiceLocationPage.ts @@ -4,8 +4,6 @@ import { IAppointmentDetails } from '@business-logic/types/CustomerDetails'; import { AppointmentType } from '@business-logic/types/Enums'; import { AddressForm } from './forms/AddressForm'; import { faker } from '@faker-js/faker'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class ServiceLocationPage extends BasePage { readonly page: Page; @@ -121,9 +119,6 @@ export class ServiceLocationPage extends BasePage { await this.mobileButton.click(); await this.enterServiceAddressButton.click(); await this.addressForm.populateAddress({ address: appointmentDetails.serviceAddress! }); - if (await this.repeatedClicksModalCloseButton.isVisible()) { - await this.repeatedClicksModalCloseButton.click(); - } if (faker.datatype.boolean()) { await this.vehicleProtectedYesButton.check(); } else { @@ -140,8 +135,7 @@ export class ServiceLocationPage extends BasePage { if (appointmentDetails && appointmentDetails.shopAddress) { await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).check(); } else { - // await this.firstAppointmentButton.click(); - // await this.clickWithRetry(this.firstAppointmentButton, this.page); + /// await this.clickWithRetry(this.firstAppointmentButton, this.page); await this.firstAppointmentButton.scrollIntoViewIfNeeded().then(async () => { await this.firstAppointmentButton.click(); if (appointmentDetails) { @@ -156,11 +150,4 @@ export class ServiceLocationPage extends BasePage { await expect(this.RecalWarningMessage2).toBeVisible(); } - - @step("ServiceLocationPage >> Select service location: ") - async handleServiceLocationPage(testData: Partial) { - const { appointmentDetails } = testData; - await this.selectLocation(appointmentDetails!); - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/ServicePackagesPage.ts b/playwright-tests/pages/ServicePackagesPage.ts index 35872828b..6e09c003e 100644 --- a/playwright-tests/pages/ServicePackagesPage.ts +++ b/playwright-tests/pages/ServicePackagesPage.ts @@ -2,8 +2,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; import { ServicePackage, VehicleDamage } from '@business-logic/types/Enums'; import { PaymentMethod } from '@business-logic/types/Enums'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class ServicePackagesPage extends BasePage { readonly page: Page; @@ -180,55 +178,4 @@ export class ServicePackagesPage extends BasePage { throw new Error("No glass parts found in the order"); } } - - @step("ServicePackagePage >> Select Payment Method and Service Type: ") - async handleServicePackagePage(testData: Partial) { - const { customerDetails, paymentMethod, servicePackage, promoCode, canNotRecal, dynamicRecal, hasOemEndorsement, vehicleDamage } = testData; - - // Define repair damage types (vs. replacement types) - const repairTypes: VehicleDamage[] = [ - VehicleDamage.WindshieldOneChip, - VehicleDamage.WindshieldTwoChips, - VehicleDamage.WindshieldThreeChips - ]; - - // Determine if we're replacing or repairing - const isReplace = !repairTypes.some(damageType => { - return vehicleDamage!.includes(damageType); - }); - - await this.handleQuotePopup(customerDetails!.email!); - await this.selectPaymentMethod(paymentMethod!); - await this.selectServicePackage(servicePackage!); - - // Enter promo code - if (promoCode) { - await this.enterPromo(promoCode); - } - - // Backend Validations - // Validate backend for can not recal if applicable - if (canNotRecal) { - await this.verifyCanNotRecal(); - } - - // Validate backend for dynamic recal if applicable - if (dynamicRecal) { - await this.verifyDynamicRecal(); - } - - // Validate backend for repair info (including chip verification) - await this.verifyIsRepair(!isReplace, vehicleDamage!); - if (isReplace) { - // Validate backend for parts info - await this.verifyVehicleParts(vehicleDamage!); - } - - // Validate backend for OEM endorsement - if (hasOemEndorsement) { - await this.verifyOEMPart(); - } - - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/ServiceZipPage.ts b/playwright-tests/pages/ServiceZipPage.ts deleted file mode 100644 index d11ffa139..000000000 --- a/playwright-tests/pages/ServiceZipPage.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Page } from "@playwright/test"; -import { LookupPage } from "./LookupPage"; -import { step } from "@business-logic/types/Step"; -import { ITestData } from "@business-logic/types/ITestData"; - -export class ServiceZipPage extends LookupPage { - - url = process.env['BASE_URL']! + '/fmg/?fmgPage=service-zip'; - - constructor(page: Page) { - super(page); - } - - @step("ZipLookupPage >> Lookup by service ZIP: ") - async handleServiceZipPage(testData: Partial) { - const { customerDetails, vehicleDetails, alertFlags } = testData; - await this.enterZip(customerDetails!.address.postalCode!); - await this.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); - } -} \ No newline at end of file diff --git a/playwright-tests/pages/VehicleDamagePage.ts b/playwright-tests/pages/VehicleDamagePage.ts index 1b48a1d4d..33d778a79 100644 --- a/playwright-tests/pages/VehicleDamagePage.ts +++ b/playwright-tests/pages/VehicleDamagePage.ts @@ -2,8 +2,6 @@ import { type Locator, type Page, expect, test } from '@playwright/test'; import { BasePage } from './BasePage'; import { SideDoorDamage, VehicleDamage, WindshieldDamage } from '@business-logic/types/Enums'; import TestSuccessAlert from '@business-logic/types/TestSuccessAlert'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class VehicleDamagePage extends BasePage { readonly page: Page; @@ -163,22 +161,4 @@ export class VehicleDamagePage extends BasePage { console.log(`Alert encountered: ${alertMessage}`); expect(alertMessage).toContain("Service not availableWe're sorry, but we currently offer only repair service for your vehicle type. Need help with next steps? Call us at800-394-0288.") } - - @step('VehicleDamagePage >> Select Damage') - async handleVehicleDamagePage(testData: Partial): Promise { - const {vehicleDamage} = testData; - const {isRepairReplace, isRepairOnly} = testData.alertFlags || {}; - - await this.selectDamage(vehicleDamage!); - // Handle alert conditions for vehicle damage - if (isRepairReplace) { - await this.checkForBothRepairReplaceAlertMessage(); - throw new TestSuccessAlert('Both assertions are met successfully.'); - } - if (isRepairOnly) { - await this.checkForRepairOnlyAlertMessage(); - throw new TestSuccessAlert('Both assertions are met successfully.'); - } - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/VehicleLookupAddressPage.ts b/playwright-tests/pages/VehicleLookupAddressPage.ts index ab0ad86f8..23ba8521a 100644 --- a/playwright-tests/pages/VehicleLookupAddressPage.ts +++ b/playwright-tests/pages/VehicleLookupAddressPage.ts @@ -3,8 +3,6 @@ import { LookupPage } from './LookupPage'; import { AddressForm } from './forms/AddressForm'; import { VehicleSelectionForm } from './forms/VehicleSelectionForm'; import { ICustomerDetails, IVehicleDetails } from '@business-logic/types/CustomerDetails'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class VehicleLookupAddressPage extends LookupPage { readonly addressForm: AddressForm; @@ -27,11 +25,4 @@ export class VehicleLookupAddressPage extends LookupPage { //await this.nextPage(); //await this.vehicleSelectionForm.selectVehicle(vehicleDetails); } - - @step("VehicleLookupAddressPage >> Lookup by address: ") - async handleVehicleLookupAddressPage(testData: Partial) { - const { customerDetails, vehicleDetails, alertFlags } = testData; - await this.lookupVehicleByAddress(customerDetails!, vehicleDetails!); - await this.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); - } } \ No newline at end of file diff --git a/playwright-tests/pages/VehicleLookupLicensePage.ts b/playwright-tests/pages/VehicleLookupLicensePage.ts index ef7f51f85..492dfeffe 100644 --- a/playwright-tests/pages/VehicleLookupLicensePage.ts +++ b/playwright-tests/pages/VehicleLookupLicensePage.ts @@ -1,8 +1,6 @@ import { type Locator, type Page } from '@playwright/test'; import { LookupPage } from './LookupPage'; import { IVehicleDetails } from '@business-logic/types/CustomerDetails'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class VehicleLookupLicensePage extends LookupPage { readonly licensePlateNumTextBox: Locator; @@ -26,12 +24,4 @@ export class VehicleLookupLicensePage extends LookupPage { async enterPlateDetails(vehicleDetails: IVehicleDetails) { await this.licensePlateNumTextBox.fill(vehicleDetails.licensePlateNumber || ''); } - - @step("VehicleLookupLicensePage >> Lookup by license plate: ") - async handleVehicleLookupLicensePage(testData: Partial) { - const { customerDetails, vehicleDetails, alertFlags } = testData; - await this.enterPlateDetails(vehicleDetails!); - await this.enterZip(customerDetails!.address.postalCode!); - await this.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); - } } \ No newline at end of file diff --git a/playwright-tests/pages/VehiclePartsPage.ts b/playwright-tests/pages/VehiclePartsPage.ts index aabaa1f4e..1dc6b84f5 100644 --- a/playwright-tests/pages/VehiclePartsPage.ts +++ b/playwright-tests/pages/VehiclePartsPage.ts @@ -1,7 +1,5 @@ import { Page } from "@playwright/test"; import { PartQuestionsPage } from "./PartQuestionPage"; -import { step } from "@business-logic/types/Step"; -import { ITestData } from "@business-logic/types/ITestData"; export default class VehiclePartQuestionsPage extends PartQuestionsPage{ url = process.env['BASE_URL']! + '/fmg/?fmgPage=vehicle-parts'; @@ -9,12 +7,4 @@ export default class VehiclePartQuestionsPage extends PartQuestionsPage{ constructor(page: Page) { super(page); } - - @step("VehiclePartsPage >> Select Vehicle Part Questions: ") - async handleVehiclePartsPage(testCase: Partial) { - const { vehiclePartQuestions } = testCase; - await this.validatePartQuestions(vehiclePartQuestions!); - await this.selectPartQuestionResponses(vehiclePartQuestions!); - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/VehicleSelectionPage.ts b/playwright-tests/pages/VehicleSelectionPage.ts index 049f9fb8d..b2d370ef8 100644 --- a/playwright-tests/pages/VehicleSelectionPage.ts +++ b/playwright-tests/pages/VehicleSelectionPage.ts @@ -2,8 +2,6 @@ import { type Locator, type Page, expect, test } from '@playwright/test'; import { BasePage } from './BasePage'; import { IVehicleDetails } from '@business-logic/types/CustomerDetails'; import TestSuccessAlert from '@business-logic/types/TestSuccessAlert'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class VehicleSelectionPage extends BasePage { readonly page: Page; @@ -44,20 +42,4 @@ export class VehicleSelectionPage extends BasePage { await console.log(`Alert encountered: ${alertMessage}`); await expect(alertMessage).toContain('Service not available in your areaWe do not offer glass service for your vehicle in your ZIP code. We apologize for the inconvenience.'); } - - @step("VehicleSelectionPage >> Select Vehicle: ") - async handleVehicleSelectionPage(testData: Partial) { - const { vehicleDetails } = testData; - const { isHeavyTruckVehicle, isSplitWindshield } = testData.alertFlags || {}; - - await this.selectVehicle(vehicleDetails!); - - // Handle alert conditions for vehicle selection - if (isHeavyTruckVehicle || isSplitWindshield) { - await this.checkForAlertMessages(); - throw new TestSuccessAlert('Both assertions are met successfully.'); - } - - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/VerifyDetailsPage.ts b/playwright-tests/pages/VerifyDetailsPage.ts index 212ed7968..b74eb251e 100644 --- a/playwright-tests/pages/VerifyDetailsPage.ts +++ b/playwright-tests/pages/VerifyDetailsPage.ts @@ -1,8 +1,6 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { IClaimDetails, ICustomerDetails } from '@business-logic/types/CustomerDetails'; import { InsuranceBasePage } from './InsuranceBasePage'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class VerifyDetailsPage extends InsuranceBasePage { readonly page: Page; @@ -76,11 +74,4 @@ export class VerifyDetailsPage extends InsuranceBasePage { // Return the original string if format is unknown return date; } - - @step("VerifyDetailsPage >> Verify policy details: ") - async handleVerifyDetailsPage(testData: Partial) { - const { customerDetails, claimDetails } = testData; - await this.verifyPolicyDetails(customerDetails!, claimDetails!); - await this.nextPage(); - } } \ No newline at end of file diff --git a/playwright-tests/pages/VinLookupPage.ts b/playwright-tests/pages/VinLookupPage.ts index 1e96c14c5..4ba2de260 100644 --- a/playwright-tests/pages/VinLookupPage.ts +++ b/playwright-tests/pages/VinLookupPage.ts @@ -1,7 +1,5 @@ import { type Locator, type Page } from '@playwright/test'; import { LookupPage } from './LookupPage'; -import { step } from '@business-logic/types/Step'; -import { ITestData } from '@business-logic/types/ITestData'; export class VinLookupPage extends LookupPage { readonly vinLookupTextBox: Locator; @@ -22,12 +20,4 @@ export class VinLookupPage extends LookupPage { async enterVin(vin: string) { await this.vinLookupTextBox.fill(vin); } - - @step("VinLookupPage >> Lookup by VIN: ") - async handleVehicleLookupVinPage(testData: Partial) { - const { customerDetails, vehicleDetails, alertFlags } = testData; - await this.enterVin(vehicleDetails!.vin!); - await this.enterZip(customerDetails!.address.postalCode!); - await this.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); - } } \ No newline at end of file diff --git a/playwright-tests/tests/0000__M.test.ts b/playwright-tests/tests/0000__M.test.ts index b51c8dd5e..524e93664 100644 --- a/playwright-tests/tests/0000__M.test.ts +++ b/playwright-tests/tests/0000__M.test.ts @@ -156,11 +156,35 @@ async function runWorkflow(page: Page, testCase: TestCase) { // Destructure test data for easier access const { - paymentMethod, customerDetails, vehicleDetails, vehicleDamage, - paymentDetails, partQuestions, enterFunnelWithZip, capabilityQuestions, - vehiclePartQuestions, moldingQuestions, skipEstimatePage + servicePackage, paymentMethod, customerDetails, vehicleDetails, vehicleDamage, + appointmentDetails, paymentDetails, claimDetails, partQuestions, enterFunnelWithZip, + capabilityQuestions, vehiclePartQuestions, moldingQuestions, isPolicyFound, + otherVehiclesOnPolicy, isUseVehicleOnPolicy, isDuplicateClaim, isRecalNotification, + alertFlags, endorsements, isPolicyDriver, skipEstimatePage, isRecalVehicle, canNotRecal, + dynamicRecal, hasOemEndorsement, promoCode } = testCase.testData; + // Destructure alert flag data + const { + isHeavyTruckVehicle, isRepairReplace, isSplitWindshield, isRepairOnly, + isUnserviceableZip, isInvalidZip, isVinNotFound + } = testCase.testData.alertFlags || {}; + + // Define repair damage types (vs. replacement types) + const repairTypes: VehicleDamage[] = [ + VehicleDamage.WindshieldOneChip, + VehicleDamage.WindshieldTwoChips, + VehicleDamage.WindshieldThreeChips + ]; + + // Determine if we're replacing or repairing + const isReplace = !repairTypes.some(damageType => { + return vehicleDamage!.includes(damageType); + }); + + // Check if the insurance policy has endorsements + const hasEndorsements = endorsements && endorsements.length > 0; + // Check if the vehicle damage includes a windshield crack const hasWindshieldCrack = vehicleDamage!.some(damage => damage === VehicleDamage.WindshieldCrack @@ -187,166 +211,414 @@ async function runWorkflow(page: Page, testCase: TestCase) { console.log(`Customer for this test: ${customerDetails?.firstName} ${customerDetails?.lastName}`); } - // Handle vehicle selection page - let vehicleSelectionPage = testCase.pages.vehicleSelectionPage; - await vehicleSelectionPage.handleVehicleSelectionPage(testCase.testData); - // Handle vehicle damage page - let vehicleDamagePage = testCase.pages.vehicleDamagePage; - await vehicleDamagePage.handleVehicleDamagePage(testCase.testData); + await test.step('VehicleSelectionPage >> Select Vehicle', async () => { + let vehicleSelectionPage = testCase.pages.vehicleSelectionPage; + await vehicleSelectionPage.selectVehicle(vehicleDetails!); + + // Handle alert conditions for vehicle selection + if (isHeavyTruckVehicle || isSplitWindshield) { + await vehicleSelectionPage.checkForAlertMessages(); + throw new TestSuccessAlert('Both assertions are met successfully.'); + } + + await vehicleSelectionPage.nextPage(); + }); + + await test.step('VehicleDamagePage >> Select Damage', async () => { + let vehicleDamagePage = testCase.pages.vehicleDamagePage; + await vehicleDamagePage.selectDamage(vehicleDamage!); + + // Handle alert conditions for vehicle damage + if (isRepairReplace) { + await vehicleDamagePage.checkForBothRepairReplaceAlertMessage(); + throw new TestSuccessAlert('Both assertions are met successfully.'); + } + if (isRepairOnly) { + await vehicleDamagePage.checkForRepairOnlyAlertMessage(); + throw new TestSuccessAlert('Both assertions are met successfully.'); + } + + await vehicleDamagePage.nextPage(); + }); // If the vehicle has a windshield crack as part of its damage, go to estimate page and select lookup type if (hasWindshieldCrack && !skipEstimatePage) { - let estimatePage = testCase.pages.estimatePage; - await estimatePage.handleEstimatePage(testCase.testData); + await test.step('EstimatePage >> Select Lookup Type', async () => { + let estimatePage = testCase.pages.estimatePage; + await estimatePage.vehicleLookup(vehicleDetails!); + }); // Handle different vehicle lookup methods switch (vehicleDetails!.vehicleLookupType!) { case VehicleLookupType.Address: - let vehicleLookupAddressPage = testCase.pages.vehicleLookupAddressPage; - await vehicleLookupAddressPage.handleVehicleLookupAddressPage(testCase.testData); + await test.step('VehicleLookupAddressPage >> Lookup by address: ' + customerDetails!.address.street, async () => { + let vehicleLookupAddressPage = testCase.pages.vehicleLookupAddressPage; + await vehicleLookupAddressPage.lookupVehicleByAddress(customerDetails!, vehicleDetails!); + await vehicleLookupAddressPage.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); + }); break; case VehicleLookupType.LicensePlateNumber: - let vehicleLookupLicensePage = testCase.pages.vehicleLookupLicensePage; - await vehicleLookupLicensePage.handleVehicleLookupLicensePage(testCase.testData); + await test.step('VehicleLookupLicensePage >> Lookup by license plate: ' + vehicleDetails!.licensePlateNumber, async () => { + let vehicleLookupLicensePage = testCase.pages.vehicleLookupLicensePage; + await vehicleLookupLicensePage.enterPlateDetails(vehicleDetails!); + await vehicleLookupLicensePage.enterZip(customerDetails!.address.postalCode!); + await vehicleLookupLicensePage.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); + }); break; case VehicleLookupType.Vin: - let vinLookupPage = testCase.pages.vinLookupPage; - await vinLookupPage.handleVehicleLookupVinPage(testCase.testData); + await test.step('VinLookupPage >> Lookup by VIN: ' + vehicleDetails!.vin!, async () => { + let vinLookupPage = testCase.pages.vinLookupPage; + await vinLookupPage.enterVin(vehicleDetails!.vin!); + await vinLookupPage.enterZip(customerDetails!.address.postalCode!); + await vinLookupPage.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); + }); break; case VehicleLookupType.Zip: - let serviceZipPage = testCase.pages.serviceZipPage; - await serviceZipPage.handleServiceZipPage(testCase.testData); - break; + await test.step('ZipLookupPage >> Lookup by service ZIP: ' + customerDetails!.address.postalCode!, async () => { + let zipLookupPage = testCase.pages.zipLookupPage; + let vinLookupPage = testCase.pages.vinLookupPage; + await vinLookupPage.enterZip(customerDetails!.address.postalCode!); + await zipLookupPage.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); + }); } } else { - // Otherwise just use zip lookup for service zip page - let serviceZipPage = testCase.pages.serviceZipPage; - await serviceZipPage.handleServiceZipPage(testCase.testData); + // Otherwise just use zip lookup + await test.step('ZipLookupPage >> Lookup by service ZIP: ' + customerDetails!.address.postalCode!, async () => { + let zipLookupPage = testCase.pages.zipLookupPage; + let vinLookupPage = testCase.pages.vinLookupPage; + await vinLookupPage.enterZip(customerDetails!.address.postalCode!); + await zipLookupPage.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); + }); } // Handle part questions if applicable if (partQuestions && partQuestions.length > 0) { - let partQuestionsPage = testCase.pages.partQuestionsPage; - await partQuestionsPage.handlePartQuestionsPage(testCase.testData); + await test.step('PartQuestionsPage >> Select Vehicle Part Question Responses', async () => { + let partQuestionsPage = testCase.pages.partQuestionsPage; + await partQuestionsPage.validatePartQuestions(partQuestions); + await partQuestionsPage.selectPartQuestionResponses(partQuestions); + await partQuestionsPage.nextPage(); + }); } // Handle molding questions if applicable if (moldingQuestions && moldingQuestions.length > 0) { - let moldingQuestionsPage = testCase.pages.moldingQuestionsPage; - await moldingQuestionsPage.handleMoldingQuestionsPage(testCase.testData); + await test.step('MoldingQuestionsPage >> Select Molding Question Responses', async () => { + let moldingQuestionsPage = testCase.pages.moldingQuestionsPage; + await moldingQuestionsPage.validatePartQuestions(moldingQuestions); + await moldingQuestionsPage.selectPartQuestionResponses(moldingQuestions); + await moldingQuestionsPage.nextPage(); + }); } // Handle vehicle part questions if applicable if (vehiclePartQuestions && vehiclePartQuestions.length > 0) { - let vehiclePartsPage = testCase.pages.vehiclePartsPage; - await vehiclePartsPage.handleVehiclePartsPage(testCase.testData); + await test.step('VehiclePartsPage >> Select Vehicle Part Responses', async () => { + let vehiclePartsPage = testCase.pages.vehiclePartsPage; + await vehiclePartsPage.validatePartQuestions(vehiclePartQuestions); + await vehiclePartsPage.selectPartQuestionResponses(vehiclePartQuestions); + await vehiclePartsPage.nextPage(); + }); } // Handle capability questions if applicable if (capabilityQuestions && capabilityQuestions.length > 0) { - let capabilityQuestionsPage = testCase.pages.capabilityQuestionsPage; - await capabilityQuestionsPage.handleCapabilityQuestionsPage(testCase.testData); + await test.step('CapabilityQuestionsPage >> Select Capability Question Responses', async () => { + let capabilityQuestionsPage = testCase.pages.capabilityQuestionsPage; + await capabilityQuestionsPage.validatePartQuestions(capabilityQuestions); + await capabilityQuestionsPage.selectPartQuestionResponses(capabilityQuestions); + await capabilityQuestionsPage.nextPage(); + }); } // Select service package and payment method - let servicePackagesPage = testCase.pages.servicePackagesPage; - await servicePackagesPage.handleServicePackagePage(testCase.testData); + await test.step('ServicePackagePage >> Select Payment Method and Service Type', async() => { + let servicePackagePage = testCase.pages.servicePackagePage; + await servicePackagePage.handleQuotePopup(customerDetails!.email!); + await servicePackagePage.selectPaymentMethod(paymentMethod!); + await servicePackagePage.selectServicePackage(servicePackage!); + // Enter promo code + if (promoCode) { + await servicePackagePage.enterPromo(promoCode); + } + + // Backend Validations + // Validate backend for can not recal if applicable + if (canNotRecal) { + await servicePackagePage.verifyCanNotRecal(); + } + // Validate backend for dynamic recal if applicable + if (dynamicRecal) { + await servicePackagePage.verifyDynamicRecal(); + } + // Validate backend for repair info (including chip verification) + await servicePackagePage.verifyIsRepair(!isReplace, vehicleDamage!); + if (isReplace) { + // Validate backend for parts info + await servicePackagePage.verifyVehicleParts(vehicleDamage!); + } + // Validate backend for OEM endorsement + if (hasOemEndorsement) { + await servicePackagePage.verifyOEMPart(); + } + + await servicePackagePage.nextPage(); + }); //============================= INSURANCE FLOW =============================` // Insurance flow - if user selected Insurance as Payment Method if (paymentMethod == PaymentMethod.Insurance) { - await handleInsuranceFlow(testCase); + await test.step('InsuranceCoveragePage >> Select your insurance', async() => { + let insuranceCompanyPage = testCase.pages.insuranceCompanyPage; + await insuranceCompanyPage.enterInsuranceCompany(claimDetails!.client!); + await insuranceCompanyPage.nextPage(); + }); + + await test.step('CCPolicyInfoPage >> Fill out claim information', async() => { + let ccPolicyInfoPage = testCase.pages.ccPolicyInfoPage; + await ccPolicyInfoPage.populatePage(customerDetails!, claimDetails!, await ccPolicyInfoPage.hasCityInfo()); + await ccPolicyInfoPage.nextPage(); + }); + + // Handle duplicate claim case if applicable + if (isDuplicateClaim) { + await test.step('DuplicateCheckPage >> Start New Claim', async () => { + let duplicateCheckPage = testCase.pages.duplicateCheckPage; + await duplicateCheckPage.startNewClaim(); + await duplicateCheckPage.nextPage(); + }); + } + + // Handle policy found vs. not found flows + if (isPolicyFound) { + await test.step('PolicyVehiclesPage >> Select vehicle', async () => { + let policyVehiclesPage = testCase.pages.policyVehiclesPage; + let vehicleSelectionPage = testCase.pages.vehicleSelectionPage; + + // Validate other vehicles on policy + if (otherVehiclesOnPolicy && otherVehiclesOnPolicy.length > 0) { + for (const vehicle of otherVehiclesOnPolicy) { + await policyVehiclesPage.validateVehicleIsOnPolicy(vehicle); + } + } + + // If vehicle is not on the policy, select new vehicle + if (!(isUseVehicleOnPolicy ?? true)) { + await policyVehiclesPage.selectVehicleNotListed(); + await policyVehiclesPage.nextPage(); + await vehicleSelectionPage.selectVehicle(vehicleDetails!); + await policyVehiclesPage.nextPage(); + } else { + // Otherwise, select the vehicle entered in Safelite.com + await policyVehiclesPage.selectVehicle(vehicleDetails!); + await policyVehiclesPage.nextPage(); + } + }); + + // Handle policy driver selection if applicable + if (isPolicyDriver) { + await test.step('PolicyDriverPage >> Confirm Driver at time of damage', async () => { + let policyDriverPage = testCase.pages.policyDriverPage; + await policyDriverPage.selectPolicyDriver(customerDetails!); + await policyDriverPage.nextPage(); + }); + } + + // Handle endorsements if applicable + if (hasEndorsements) { + await test.step('EndorsementsPage >> Select Endorsements', async () => { + let endorsementsPage = testCase.pages.endorsementsPage; + await endorsementsPage.verifyEndorsements(endorsements); + await endorsementsPage.selectEndorsements(endorsements); + await endorsementsPage.nextPage(); + }); + } + } else { + // Policy not found flow + await test.step('VerifyDetailsPage >> Verify Details', async () => { + let verifyDetailsPage = testCase.pages.verifyDetailsPage; + await verifyDetailsPage.verifyPolicyDetails(customerDetails!, claimDetails!); + await verifyDetailsPage.nextPage(); + }); + } + + // Continue with the insurance flow after policy information + await test.step('PolicyInfoSubmittedPage >> Continue With Safelite Autoglass', async () => { + let policyInfoSubmittedPage = testCase.pages.policyInfoSubmittedPage; + await policyInfoSubmittedPage.verifyPolicyInfoSubmitted(); + await policyInfoSubmittedPage.nextPage(); + }); + + // Handle recalibration notification if applicable + if (isRecalNotification) { + await test.step('RecallibrationInfoPage >> Continue With Recalibration Information', async () => { + let recalibrationInfoPage = testCase.pages.recalibrationInfoPage; + await recalibrationInfoPage.nextPage(); + }); + } + + // Continue to coverage statement + await test.step('CoverageStatementPage >> Next page', async () => { + let coverageStatementPage = testCase.pages.coverageStatementPage; + await coverageStatementPage.validateDeductibleAmount(claimDetails!); + await coverageStatementPage.nextPage(); + }); } //============================= SERVICE SCHEDULING ============================= // Select service location - let serviceLocationPage = testCase.pages.serviceLocationPage; - await serviceLocationPage.handleServiceLocationPage(testCase.testData); + await test.step('ServiceLocationPage >> Select service location', async () => { + let serviceLocationPage = testCase.pages.serviceLocationPage; + await serviceLocationPage.selectLocation(appointmentDetails!); + await serviceLocationPage.nextPage(); + }); // Schedule appointment - let schedulePage = testCase.pages.schedulePage; - await schedulePage.handleSchedulePage(testCase.testData); + await test.step('SchedulePage >> Select day and time', async () => { + let schedulePage = testCase.pages.schedulePage; + customerDetails!.apptDate! = await schedulePage.scheduleFirstAppointment(appointmentDetails!.serviceLocation); + }); // Enter contact details - let contactDetailsPage = testCase.pages.contactDetailsPage; - await contactDetailsPage.handleContactDetailsPage(testCase.testData); + await test.step('ContactDetailsPage >> Enter contact details', async () => { + let contactDetailsPage = testCase.pages.contactDetailsPage; + await contactDetailsPage.enterContactDetails(customerDetails!); + await contactDetailsPage.nextPage(); + }); //============================= PAYMENT PROCESSING ============================= // Handle payment - let paymentMethodPage = testCase.pages.paymentMethodPage; - await paymentMethodPage.handlePaymentMethodPage(testCase.testData); + await test.step('PaymentMethodPage >> Execute Payment', async () => { + let paymentMethodPage = testCase.pages.paymentMethodPage; + await paymentMethodPage.validatePaymentDetailsPage(testCase.testData); + // Verify VAPS wipers on backend for standard and premium packages + if (servicePackage === ServicePackage.Standard || servicePackage === ServicePackage.Premium) { + await paymentMethodPage.verifyVAPS(); + } + if (paymentDetails?.paymentType) { + await paymentMethodPage.executePayment(paymentDetails!, isRecalVehicle!); + } else { + await paymentMethodPage.nextPage(); + } + }); // If user selected Pay with Insurance as Payment Method, Enter Insurance Flow if (paymentDetails?.paymentType === PaymentType.PayWithInsurance) { - await handleInsuranceFlow(testCase); + await test.step('InsuranceCoveragePage >> Select your insurance', async() => { + let insuranceCompanyPage = testCase.pages.insuranceCompanyPage; + await insuranceCompanyPage.enterInsuranceCompany(claimDetails!.client!); + await insuranceCompanyPage.nextPage(); + }); + + await test.step('CCPolicyInfoPage >> Fill out claim information', async() => { + const ccPolicyInfoPage = testCase.pages.ccPolicyInfoPage; + await ccPolicyInfoPage.populatePage(customerDetails!, claimDetails!, await ccPolicyInfoPage.hasCityInfo()); + await ccPolicyInfoPage.nextPage(); + }); + + // Handle duplicate claim case if applicable + if (isDuplicateClaim) { + await test.step('DuplicateCheckPage >> Start New Claim', async () => { + const duplicateCheckPage = testCase.pages.duplicateCheckPage; + await duplicateCheckPage.startNewClaim(); + await duplicateCheckPage.nextPage(); + }); + } + + // Handle policy found vs. not found flows + if (isPolicyFound) { + await test.step('PolicyVehiclesPage >> Select vehicle', async () => { + const policyVehiclesPage = testCase.pages.policyVehiclesPage; + const vehicleSelectionPage = testCase.pages.vehicleSelectionPage; + + // Validate other vehicles on policy + if (otherVehiclesOnPolicy && otherVehiclesOnPolicy.length > 0) { + for (const vehicle of otherVehiclesOnPolicy) { + await policyVehiclesPage.validateVehicleIsOnPolicy(vehicle); + } + } + + // If vehicle is not on the policy, select new vehicle + if (!(isUseVehicleOnPolicy ?? true)) { + await policyVehiclesPage.selectVehicleNotListed(); + await policyVehiclesPage.nextPage(); + await vehicleSelectionPage.selectVehicle(vehicleDetails!); + await policyVehiclesPage.nextPage(); + } else { + // Otherwise, select the vehicle entered in Safelite.com + await policyVehiclesPage.selectVehicle(vehicleDetails!); + await policyVehiclesPage.nextPage(); + } + }); + + // Handle policy driver selection if applicable + if (isPolicyDriver) { + await test.step('PolicyDriverPage >> Confirm Driver at time of damage', async () => { + const policyDriverPage = testCase.pages.policyDriverPage; + await policyDriverPage.selectPolicyDriver(customerDetails!); + await policyDriverPage.nextPage(); + }); + } + + // Handle endorsements if applicable + if (hasEndorsements) { + await test.step('EndorsementsPage >> Select Endorsements', async () => { + const endorsementsPage = testCase.pages.endorsementsPage; + await endorsementsPage.verifyEndorsements(endorsements); + await endorsementsPage.selectEndorsements(endorsements); + await endorsementsPage.nextPage(); + }); + } + } else { + // Policy not found flow + await test.step('VerifyDetailsPage >> Verify Details', async () => { + const verifyDetailsPage = testCase.pages.verifyDetailsPage; + await verifyDetailsPage.verifyPolicyDetails(customerDetails!, claimDetails!); + await verifyDetailsPage.nextPage(); + }); + } + + // Continue with the insurance flow after policy information + await test.step('PolicyInfoSubmittedPage >> Continue With Safelite Autoglass', async () => { + const policyInfoSubmittedPage = testCase.pages.policyInfoSubmittedPage; + await policyInfoSubmittedPage.verifyPolicyInfoSubmitted(); + await policyInfoSubmittedPage.nextPage(); + }); + + // Handle recalibration notification if applicable + if (isRecalNotification) { + await test.step('RecallibrationInfoPage >> Continue With Recalibration Information', async () => { + const recalibrationInfoPage = testCase.pages.recalibrationInfoPage; + await recalibrationInfoPage.nextPage(); + }); + } + + // Continue to coverage statement + await test.step('CoverageStatementPage >> Next page', async () => { + const coverageStatementPage = testCase.pages.coverageStatementPage; + await coverageStatementPage.validateDeductibleAmount(claimDetails!); + await coverageStatementPage.nextPage(); + }); } //============================= ORDER CONFIRMATION ============================= // Validate order confirmation - let orderConfirmationPage = testCase.pages.orderConfirmationPage; - await orderConfirmationPage.verifyOrderConfirmationPage(testCase.testData); -} + await test.step('OrderConfirmationPage >> Validate order', async () => { + let orderConfirmationPage = testCase.pages.orderConfirmationPage; + await orderConfirmationPage.validateOrderConfirmationPage(testCase.testData); + }); -export async function handleInsuranceFlow(testCase: TestCase) { - const { isPolicyFound, isPolicyDriver, endorsements, isRecalNotification } = testCase.testData; - - // Check if the insurance policy has endorsements - const hasEndorsements = endorsements && endorsements.length > 0; - - // Handle insurance company page - let insuranceCompanyPage = testCase.pages.insuranceCompanyPage; - await insuranceCompanyPage.handleInsuranceCompanyPage(testCase.testData); - - // Handle ccPolicyInfoPage - let ccPolicyInfoPage = testCase.pages.ccPolicyInfoPage; - await ccPolicyInfoPage.handleCCPolicyInfoPage(testCase.testData); - - let duplicateCheckPage = testCase.pages.duplicateCheckPage; - if (duplicateCheckPage.page.url().includes('DuplicateCheck.aspx')) { - await duplicateCheckPage.handleDuplicateCheckPage(testCase.testData); - } - - if (isPolicyFound) { - let policyVehiclesPage = testCase.pages.policyVehiclesPage; - await policyVehiclesPage.handlePolicyVehiclesPage(testCase.testData); - - // Handle policy driver selection if applicable - if (isPolicyDriver) { - let policyDriverPage = testCase.pages.policyDriverPage; - await policyDriverPage.handlePolicyDriverPage(testCase.testData); - } - - // Handle endorsements if applicable - if (hasEndorsements) { - let endorsementsPage = testCase.pages.endorsementsPage; - await endorsementsPage.handleEndorsementsPage(testCase.testData); - } - }else { - - //Handle verify details page - let verifyDetailsPage = testCase.pages.verifyDetailsPage; - await verifyDetailsPage.handleVerifyDetailsPage(testCase.testData); - } - - // Handle Policy info submitted page - let policyInfoSubmittedPage = testCase.pages.policyInfoSubmittedPage; - await policyInfoSubmittedPage.handlePolicyInfoSubmittedPage(); - - if(isRecalNotification) - { - let recalibrationInfoPage = testCase.pages.recalibrationInfoPage; - await recalibrationInfoPage.handleRecalibrationInfoPage(); - } - - //handle coveraage statement page - let coverageStatementPage = testCase.pages.coverageStatementPage; - await coverageStatementPage.handleCoverageStatementPage(testCase.testData); + // Get the order number and wrap it in a test step + const workOrderNumber = await testCase.pages.orderConfirmationPage.logOrderNumber(); + await test.step(`Session Storage Work Order Number: ${workOrderNumber}`, async () => { + console.log(`Session Storage Work Order Number: ${workOrderNumber}`); + }); } \ No newline at end of file diff --git a/playwright-tests/tests/CashRepairMobileCreditCard.ts b/playwright-tests/tests/CashRepairMobileCreditCard.ts index 892c8d71e..3674159aa 100644 --- a/playwright-tests/tests/CashRepairMobileCreditCard.ts +++ b/playwright-tests/tests/CashRepairMobileCreditCard.ts @@ -28,7 +28,7 @@ const cashRepairMobileCCData : Partial = { serviceAddress: { street: '13735 San Antonio Ave', city: 'Chino', - state: 'CA', + state: 'California', postalCode: '91710', country: 'United States' } diff --git a/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts b/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts index 25bb3bf50..50c1d72b5 100644 --- a/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts +++ b/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts @@ -39,7 +39,7 @@ const cashReplaceDynamicRecalMobileData: Partial = { // Use street address from current faker seed street: getDefaultTestData().customerDetails!.address.street, city: 'Rosedale', - state: 'MD', + state: 'Maryland', postalCode: '21237', country: 'United States' }, diff --git a/playwright-tests/tests/CashReplaceMultiGlassMobile.ts b/playwright-tests/tests/CashReplaceMultiGlassMobile.ts index eac5e2263..54da96bb6 100644 --- a/playwright-tests/tests/CashReplaceMultiGlassMobile.ts +++ b/playwright-tests/tests/CashReplaceMultiGlassMobile.ts @@ -35,7 +35,7 @@ const cashReplaceMultiGlassMobileData: Partial = { // Use street address from current faker seed street: getDefaultTestData().customerDetails!.address.street, city: 'Rosedale', - state: 'MD', + state: 'Maryland', postalCode: '21237', country: 'United States' }, diff --git a/playwright-tests/tests/CashReplaceSafeliteCanNotRecalMobile.ts b/playwright-tests/tests/CashReplaceSafeliteCanNotRecalMobile.ts index f7d301da1..9ad33f23d 100644 --- a/playwright-tests/tests/CashReplaceSafeliteCanNotRecalMobile.ts +++ b/playwright-tests/tests/CashReplaceSafeliteCanNotRecalMobile.ts @@ -48,7 +48,7 @@ const cashReplaceSafeliteCanNotRecalMobileData: Partial = { // Use street address from current faker seed street: getDefaultTestData().customerDetails!.address.street, city: 'Rosedale', - state: 'MD', + state: 'Maryland', postalCode: '21237', country: 'United States' }, diff --git a/playwright-tests/tests/CashReplaceVinMobile.ts b/playwright-tests/tests/CashReplaceVinMobile.ts index 37470200e..59930b1ac 100644 --- a/playwright-tests/tests/CashReplaceVinMobile.ts +++ b/playwright-tests/tests/CashReplaceVinMobile.ts @@ -45,7 +45,7 @@ const cashReplaceVinMobileData: Partial = { // Use street address from current faker seed street: getDefaultTestData().customerDetails!.address.street, city: 'Rosedale', - state: 'MD', + state: 'Maryland', postalCode: '21237', country: 'United States' } From b5e0a564c7ecf4f1faae700398b5118f57854758 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Wed, 30 Apr 2025 10:01:42 -0400 Subject: [PATCH 2/2] Revert "Merge pull request #2454 from Safelite/feature/CASH-488-fixed" This reverts commit 1a0ed339ac5c7df3aab5476aae9655a8cfc15d02, reversing changes made to fc2d0ceecd78df9999a0a0aec9c3bb8239e149b6. --- playwright-tests/.env.dev | 4 --- .../azure-pipelines-automated-testing.yml | 15 ++++++++--- .../constants/DefaultTestData.ts | 2 +- playwright-tests/pages/ServiceLocationPage.ts | 25 +++++++------------ playwright-tests/pages/ServicePackagesPage.ts | 4 +-- playwright-tests/tests/0000__M.test.ts | 4 +-- 6 files changed, 25 insertions(+), 29 deletions(-) diff --git a/playwright-tests/.env.dev b/playwright-tests/.env.dev index 8c064ba62..47ec8336d 100644 --- a/playwright-tests/.env.dev +++ b/playwright-tests/.env.dev @@ -4,13 +4,9 @@ # Environment type NODE_ENV="qa" -SKIP_CONTENT_SITE=false - # Base URLs by environment (uncomment the one you need) # qa BASE_URL="https://www-qa2.safelite.com/" -# qa with skipToInsurance Turned Off -# BASE_URL="https://fixmyglassqa.safelite.com/?cns=all&experiments=ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_VinAndEmailOptional=true,NextGen_IGQSkipToInsurance=NextGen_IGQSkipToInsurance_V1=NextGen_IGQSkipToInsurance_CONTROL=true" # sys # BASE_URL="https://www-test2.safelite.com/fmg/?fmgPage=vehicle" # dev diff --git a/playwright-tests/azure-pipelines-automated-testing.yml b/playwright-tests/azure-pipelines-automated-testing.yml index ac332ef44..b4d7da831 100644 --- a/playwright-tests/azure-pipelines-automated-testing.yml +++ b/playwright-tests/azure-pipelines-automated-testing.yml @@ -1,3 +1,11 @@ +schedules: +- cron: 0 9 * * MON-FRI + always: true + displayName: Daily Test Automation Run for FMG-NextGen + branches: + include: + - main + pool: 'AmazonLinuxPool' variables: @@ -42,10 +50,10 @@ stages: container_id=$(docker create \ --ipc=host \ -e CCIS_API_AUTH=$(CCIS_API_AUTH) \ - -e BASE_URL="$(BASE_URL)" \ + -e BASE_URL=$(BASE_URL) \ -e CCIS_API_URL=$(CCIS_API_URL) \ -e ADMIN_SERVICE_API_URL=$(ADMIN_SERVICE_API_URL) \ - -e SKIP_CONTENT_SITE=$(SKIP_CONTENT_SITE) \ + -e SHARD=$(shardNumber) \ -e CI=true \ -e NODE_ENV=$(NODE_ENV) \ $(dockerImageName):$(imageTag) \ @@ -123,7 +131,6 @@ stages: -e REPORT_NAME="$report_name" \ -e CURRENT_DAY="$current_day" \ -e NUMERIC_DATE="$numeric_date" \ - -e NODE_ENV=$(NODE_ENV) \ $(dockerImageName):$(imageTag) \ bash -c "chmod +x devops/scripts/jira_writeback.sh echo \"Moving Playwright reports out of subfolders...\" @@ -144,7 +151,7 @@ stages: fi echo \"Writing report to Jira card '$jira_card_number'...\" - /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/\${REPORT_NAME} \"AUTOMATED TEST RUN: $(date) - \${CURRENT_DAY} (\${NUMERIC_DATE}) - Environment: \${NODE_ENV}\" ") + /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/\${REPORT_NAME} \"AUTOMATED TEST RUN: $(date) - \${CURRENT_DAY} (\${NUMERIC_DATE})\" ") # Start container and stream logs echo "Starting merge" diff --git a/playwright-tests/business-logic/constants/DefaultTestData.ts b/playwright-tests/business-logic/constants/DefaultTestData.ts index 197297b4b..6efb2c1db 100644 --- a/playwright-tests/business-logic/constants/DefaultTestData.ts +++ b/playwright-tests/business-logic/constants/DefaultTestData.ts @@ -43,7 +43,7 @@ export function getCustomerDetails(): ICustomerDetails { street: faker.location.streetAddress(), city: 'Columbus', state: 'Ohio', - postalCode: '21237', + postalCode: '43215', country: 'United States' } }; diff --git a/playwright-tests/pages/ServiceLocationPage.ts b/playwright-tests/pages/ServiceLocationPage.ts index 609f2d1db..b8c8649b6 100644 --- a/playwright-tests/pages/ServiceLocationPage.ts +++ b/playwright-tests/pages/ServiceLocationPage.ts @@ -55,7 +55,7 @@ export class ServiceLocationPage extends BasePage { // For in-shop and drop off this.selectAShopOptions = this.page.locator('[class="shop-question"]'); - this.firstAppointmentButton = this.page.locator('fieldset:has(legend#chooseShop)').locator('label').first(); + this.firstAppointmentButton = this.page.locator('div').filter({ hasText: /Appts/}).first(); this.changeZipButton = this.page.locator('a:has(span.sr-only:has-text("edit zip code"))'); this.updateZipTextBox = this.page.locator('#serviceZipCode'); this.saveZipButton = this.page.getByText('Save ZIP code', { exact: true }); @@ -71,7 +71,7 @@ export class ServiceLocationPage extends BasePage { this.vehicleProtectedYesButton = this.page.locator('label').filter({ hasText: 'Yes' }).locator('div'); this.vehicleProtectedNoButton = this.page.locator('label').filter({ hasText: 'No' }).locator('div'); this.saveAddressButton = this.page.getByRole('button', { name: 'Continue' }) - this.repeatedClicksModalCloseButton = this.page.locator('.QSISlider').locator('img[src*=\'close\']'); + this.repeatedClicksModalCloseButton = this.page.getByRole('img').nth(1); } async selectLocation(appointmentDetails: IAppointmentDetails){ @@ -103,14 +103,9 @@ export class ServiceLocationPage extends BasePage { await this.saveZipButton.click(); } await this.inShopButton.click(); - await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).scrollIntoViewIfNeeded().then(() => this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).click()); + await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).check(); } else { - await this.firstAppointmentButton.scrollIntoViewIfNeeded().then(async () => { - await this.firstAppointmentButton.click(); - if (appointmentDetails) { - appointmentDetails.shopAddress = await this.firstAppointmentButton.locator('.row-two').innerText(); - } - }); + await this.firstAppointmentButton.click(); } } @@ -119,6 +114,9 @@ export class ServiceLocationPage extends BasePage { await this.mobileButton.click(); await this.enterServiceAddressButton.click(); await this.addressForm.populateAddress({ address: appointmentDetails.serviceAddress! }); + if (await this.repeatedClicksModalCloseButton.isVisible()) { + await this.repeatedClicksModalCloseButton.click(); + } if (faker.datatype.boolean()) { await this.vehicleProtectedYesButton.check(); } else { @@ -135,13 +133,8 @@ export class ServiceLocationPage extends BasePage { if (appointmentDetails && appointmentDetails.shopAddress) { await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).check(); } else { - /// await this.clickWithRetry(this.firstAppointmentButton, this.page); - await this.firstAppointmentButton.scrollIntoViewIfNeeded().then(async () => { - await this.firstAppointmentButton.click(); - if (appointmentDetails) { - appointmentDetails.shopAddress = await this.firstAppointmentButton.locator('.row-two').innerText(); - } - }); + // await this.firstAppointmentButton.click(); + await this.clickWithRetry(this.firstAppointmentButton, this.page); } } diff --git a/playwright-tests/pages/ServicePackagesPage.ts b/playwright-tests/pages/ServicePackagesPage.ts index 6e09c003e..9a3b87ae3 100644 --- a/playwright-tests/pages/ServicePackagesPage.ts +++ b/playwright-tests/pages/ServicePackagesPage.ts @@ -41,7 +41,7 @@ export class ServicePackagesPage extends BasePage { this.closeButton = this.page. getByRole('dialog').locator('button').filter({ hasText: 'Close' }); this.promoCodeTextbox = this.page.getByLabel('Enter a promo code'); this.applyPromoButton = this.page.getByRole('button', { name: 'Apply promo code' }); - this.repeatedClicksModalCloseButton = this.page.locator('.QSISlider').locator('img[src*=\'close\']'); + this.repeatedClicksModalCloseButton = this.page.getByRole('img').nth(1); // this.validateURL(this.url); } @@ -76,8 +76,8 @@ export class ServicePackagesPage extends BasePage { if (await this.repeatedClicksModalCloseButton.isVisible()) { await this.repeatedClicksModalCloseButton.click(); } - await this.page.waitForTimeout(3000); await this.closeButton.click(); + } else { await this.skipQuoteEmailButton.click(); } diff --git a/playwright-tests/tests/0000__M.test.ts b/playwright-tests/tests/0000__M.test.ts index 524e93664..6bef5a2f3 100644 --- a/playwright-tests/tests/0000__M.test.ts +++ b/playwright-tests/tests/0000__M.test.ts @@ -192,8 +192,8 @@ async function runWorkflow(page: Page, testCase: TestCase) { //============================= TEST WORKFLOW STEPS ============================= - // Use Environment Variable to decide whether or not we want to skip content site aka home page - if (process.env.SKIP_CONTENT_SITE == "false") { + // Execute home page for qa and dev environments (skip for sys) + if (process.env.NODE_ENV !== 'sys') { await test.step('HomePage >> Lets Get Started', async () => { let homePage = testCase.pages.homePage; console.log(`Customer for this test: ${customerDetails?.firstName} ${customerDetails?.lastName}`);