From dc07703f6c6251e225e8f40658e72062057b1099 Mon Sep 17 00:00:00 2001 From: Katie Kroell Date: Fri, 13 Sep 2024 16:41:37 -0400 Subject: [PATCH 01/99] added headers for API calls --- src/constants/header-keys.js | 7 +++++- src/global-methods.js | 44 ++++++++++++++++++++---------------- src/helpers/logger.js | 38 ++++++++++++++++--------------- 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/constants/header-keys.js b/src/constants/header-keys.js index 20d0cc1b..52a0d617 100644 --- a/src/constants/header-keys.js +++ b/src/constants/header-keys.js @@ -1,5 +1,10 @@ const headerKeys = Object.freeze({ - EXPERIMENT: 'X-Experiment-Data' + EXPERIMENT: 'X-Experiment-Data', + TRANSACTION_ID: 'X-Transaction-ID', + EON: 'X-EON', + APP_NAME: 'X-App-Name', + REFERRAL_SEQUENCE_NUMBER: 'X-Referral-Sequence-Number', + SESSION_SEQUENCE_NUMBER: 'X-Session-Sequence-Number' }); export default headerKeys; diff --git a/src/global-methods.js b/src/global-methods.js index 4ff7a336..07a1aac8 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -1,24 +1,24 @@ import axios from 'axios'; import analyticsMixIn from '@/mixins/analytics-mixin.js'; import { useMainStore } from '@/store'; - import applicationConfig from '@/constants/application-config.js'; import { GaCategories, GaActions, GaLabels } from '@/constants/analytics'; import headerKeys from '@/constants/header-keys'; -import axiosResponseInterceptorMessages from "@/constants/axios-response-interceptor-messages.js"; +import axiosResponseInterceptorMessages from '@/constants/axios-response-interceptor-messages.js'; +import { getSessionKeyValue } from '@/helpers/cookie-helper'; // Add a response interceptor for global axios error handing. axios.interceptors.response.use( (response) => response, (error) => { - let rejectionError = ""; - if (typeof error.response === "undefined") { + let rejectionError = ''; + if (typeof error.response === 'undefined') { // The request was not made, could be a bad url, bad connection or a CORS error. rejectionError = { message: - "A network error occurred. " + - "This could be a CORS issue or a dropped internet connection. " + - "It is impossible for us to know.", + 'A network error occurred. ' + + 'This could be a CORS issue or a dropped internet connection. ' + + 'It is impossible for us to know.', cause: error, response: error, message: axiosResponseInterceptorMessages.NETWORK_ERROR @@ -28,7 +28,7 @@ axios.interceptors.response.use( // that falls out of the range of 2xx rejectionError = { response: error.response, - message: axiosResponseInterceptorMessages.STATUS_CODE_ERROR, + message: axiosResponseInterceptorMessages.STATUS_CODE_ERROR }; } else if (error.request) { // The request was made but no response was received @@ -36,13 +36,13 @@ axios.interceptors.response.use( // http.ClientRequest in node.js rejectionError = { response: error.request, - message: axiosResponseInterceptorMessages.NO_RESPONSE_ERROR, + message: axiosResponseInterceptorMessages.NO_RESPONSE_ERROR }; } else { // Something happened in setting up the request that triggered an Error rejectionError = { response: error.message, - message: axiosResponseInterceptorMessages.GENERIC_ERROR, + message: axiosResponseInterceptorMessages.GENERIC_ERROR }; } @@ -56,8 +56,14 @@ export default { const store = useMainStore(); const cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO; const payloadAndAnalyticsData = { ...payload, AppName: 'ISS' }; + const sessionKey = getSessionKeyValue(); const headers = { - [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings) + [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), + [headerKeys.APP_NAME]: JSON.stringify(applicationConfig.APPLICATION_NAME), + [headerKeys.TRANSACTION_ID]: JSON.stringify(store.order.payment.creditCardToken.transactionId), + [headerKeys.EON]: JSON.stringify(store.order.eon), + [headerKeys.REFERRAL_SEQUENCE_NUMBER]: JSON.stringify(store.order.referralSequenceNumber), + [headerKeys.SESSION_SEQUENCE_NUMBER]: JSON.stringify(sessionKey) }; axios({ @@ -89,15 +95,13 @@ export default { true ); } - - if (error.response.status != "404") { - - - global.$logger.logError( - `${method}: ${endpoint}: ${error.message}`, - error.response - ); - } + + if (error.response.status != '404') { + global.$logger.logError( + `${method}: ${endpoint}: ${error.message}`, + error.response + ); + } return reject(error.response); } ); diff --git a/src/helpers/logger.js b/src/helpers/logger.js index aad71f43..1557d2d0 100644 --- a/src/helpers/logger.js +++ b/src/helpers/logger.js @@ -1,9 +1,9 @@ -import applicationConfig from "@/constants/application-config.js"; -import axios from "axios"; -import { useMainStore } from "@/store"; -import loggingEndpointMethods from "@/constants/logging-endpoint-methods"; - -import headerKeys from "@/constants/header-keys"; +import applicationConfig from '@/constants/application-config.js'; +import axios from 'axios'; +import { useMainStore } from '@/store'; +import loggingEndpointMethods from '@/constants/logging-endpoint-methods'; +import headerKeys from '@/constants/header-keys'; +import { getSessionKeyValue } from '@/helpers/cookie-helper'; export class Logger { logInformation(message, details) { @@ -33,19 +33,20 @@ export class Logger { formatLogEntry(message, details) { return `Application: ${applicationConfig.APPLICATION_NAME}\n${message}\n${ - details ? JSON.stringify(details, undefined, 2) : "" + details ? JSON.stringify(details, undefined, 2) : '' }`; } writeLogEntry(endpoint, logEntry) { const store = useMainStore(); + const sessionKey = getSessionKeyValue(); return new Promise((resolve, reject) => { // If running locally or in the Dev environment show the log entries in the console. if ( - applicationConfig.CURRENT_ENVIRONMENT === "Localhost" || - applicationConfig.CURRENT_ENVIRONMENT === "Dev" || - applicationConfig.CURRENT_ENVIRONMENT === "SysTest" + applicationConfig.CURRENT_ENVIRONMENT === 'Localhost' + || applicationConfig.CURRENT_ENVIRONMENT === 'Dev' + || applicationConfig.CURRENT_ENVIRONMENT === 'SysTest' ) { switch (endpoint) { case loggingEndpointMethods.LOG_INFORMATION: @@ -69,22 +70,23 @@ export class Logger { applicationConfig.CONSUMER_CF_DISTRO + applicationConfig.FRONTEND_LOGGER_PATH; const headers = { [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), + [headerKeys.APP_NAME]: JSON.stringify(applicationConfig.APPLICATION_NAME), + [headerKeys.TRANSACTION_ID]: JSON.stringify(store.order.payment.creditCardToken.transactionId), + [headerKeys.EON]: JSON.stringify(store.order.eon), + [headerKeys.REFERRAL_SEQUENCE_NUMBER]: JSON.stringify(store.order.referralSequenceNumber), + [headerKeys.SESSION_SEQUENCE_NUMBER]: JSON.stringify(sessionKey) }; axios({ - method: "POST", + method: 'POST', url: `${url}/${endpoint}`, data: { entry: logEntry }, crossDomain: true, responseType: {}, - headers: headers, + headers }).then( - (response) => { - return resolve(response); - }, - (error) => { - return reject(error); - } + (response) => resolve(response), + (error) => reject(error) ); }); } From ffbc6c888f9a071e86d5435b8888e486ab27cfa3 Mon Sep 17 00:00:00 2001 From: Katie Kroell Date: Mon, 23 Sep 2024 14:31:42 -0400 Subject: [PATCH 02/99] naming changes --- src/constants/header-keys.js | 6 +++--- src/global-methods.js | 2 +- src/helpers/logger.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/constants/header-keys.js b/src/constants/header-keys.js index 52a0d617..16f54055 100644 --- a/src/constants/header-keys.js +++ b/src/constants/header-keys.js @@ -1,8 +1,8 @@ const headerKeys = Object.freeze({ EXPERIMENT: 'X-Experiment-Data', - TRANSACTION_ID: 'X-Transaction-ID', - EON: 'X-EON', - APP_NAME: 'X-App-Name', + TRANSACTION_ID: 'X-Transaction-Id', + EON: 'X-Enterprise-Order-Number', + APPLICATION_NAME: 'X-Application-Name', REFERRAL_SEQUENCE_NUMBER: 'X-Referral-Sequence-Number', SESSION_SEQUENCE_NUMBER: 'X-Session-Sequence-Number' }); diff --git a/src/global-methods.js b/src/global-methods.js index 07a1aac8..f60e63ba 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -59,7 +59,7 @@ export default { const sessionKey = getSessionKeyValue(); const headers = { [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), - [headerKeys.APP_NAME]: JSON.stringify(applicationConfig.APPLICATION_NAME), + [headerKeys.APPLICATION_NAME]: JSON.stringify(applicationConfig.APPLICATION_NAME), [headerKeys.TRANSACTION_ID]: JSON.stringify(store.order.payment.creditCardToken.transactionId), [headerKeys.EON]: JSON.stringify(store.order.eon), [headerKeys.REFERRAL_SEQUENCE_NUMBER]: JSON.stringify(store.order.referralSequenceNumber), diff --git a/src/helpers/logger.js b/src/helpers/logger.js index 1557d2d0..f21449b9 100644 --- a/src/helpers/logger.js +++ b/src/helpers/logger.js @@ -70,7 +70,7 @@ export class Logger { applicationConfig.CONSUMER_CF_DISTRO + applicationConfig.FRONTEND_LOGGER_PATH; const headers = { [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), - [headerKeys.APP_NAME]: JSON.stringify(applicationConfig.APPLICATION_NAME), + [headerKeys.APPLICATION_NAME]: JSON.stringify(applicationConfig.APPLICATION_NAME), [headerKeys.TRANSACTION_ID]: JSON.stringify(store.order.payment.creditCardToken.transactionId), [headerKeys.EON]: JSON.stringify(store.order.eon), [headerKeys.REFERRAL_SEQUENCE_NUMBER]: JSON.stringify(store.order.referralSequenceNumber), From 3b523ac7f119232bfcc2a6239fc0650e70a7defc Mon Sep 17 00:00:00 2001 From: Katie Kroell Date: Tue, 24 Sep 2024 13:40:43 -0400 Subject: [PATCH 03/99] updates --- src/global-methods.js | 10 +++++----- src/helpers/logger.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/global-methods.js b/src/global-methods.js index f60e63ba..b720328b 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -59,11 +59,11 @@ export default { const sessionKey = getSessionKeyValue(); const headers = { [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), - [headerKeys.APPLICATION_NAME]: JSON.stringify(applicationConfig.APPLICATION_NAME), - [headerKeys.TRANSACTION_ID]: JSON.stringify(store.order.payment.creditCardToken.transactionId), - [headerKeys.EON]: JSON.stringify(store.order.eon), - [headerKeys.REFERRAL_SEQUENCE_NUMBER]: JSON.stringify(store.order.referralSequenceNumber), - [headerKeys.SESSION_SEQUENCE_NUMBER]: JSON.stringify(sessionKey) + [headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME, + [headerKeys.TRANSACTION_ID]: store.order.payment.creditCardToken.transactionId, + [headerKeys.EON]: store.order.eon, + [headerKeys.REFERRAL_SEQUENCE_NUMBER]: store.order.referralSequenceNumber, + [headerKeys.SESSION_SEQUENCE_NUMBER]: sessionKey }; axios({ diff --git a/src/helpers/logger.js b/src/helpers/logger.js index f21449b9..c124b1c2 100644 --- a/src/helpers/logger.js +++ b/src/helpers/logger.js @@ -70,11 +70,11 @@ export class Logger { applicationConfig.CONSUMER_CF_DISTRO + applicationConfig.FRONTEND_LOGGER_PATH; const headers = { [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), - [headerKeys.APPLICATION_NAME]: JSON.stringify(applicationConfig.APPLICATION_NAME), - [headerKeys.TRANSACTION_ID]: JSON.stringify(store.order.payment.creditCardToken.transactionId), - [headerKeys.EON]: JSON.stringify(store.order.eon), - [headerKeys.REFERRAL_SEQUENCE_NUMBER]: JSON.stringify(store.order.referralSequenceNumber), - [headerKeys.SESSION_SEQUENCE_NUMBER]: JSON.stringify(sessionKey) + [headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME, + [headerKeys.TRANSACTION_ID]: store.order.payment.creditCardToken.transactionId, + [headerKeys.EON]: store.order.eon, + [headerKeys.REFERRAL_SEQUENCE_NUMBER]: store.order.referralSequenceNumber, + [headerKeys.SESSION_SEQUENCE_NUMBER]: sessionKey }; axios({ From 9e84ccfbd3e466ff8d963547243266c0f1fd6a16 Mon Sep 17 00:00:00 2001 From: Katie Kroell Date: Mon, 14 Oct 2024 14:19:39 -0400 Subject: [PATCH 04/99] naming change --- src/constants/header-keys.js | 2 +- src/global-methods.js | 2 +- src/helpers/logger.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/constants/header-keys.js b/src/constants/header-keys.js index 16f54055..b1847281 100644 --- a/src/constants/header-keys.js +++ b/src/constants/header-keys.js @@ -1,7 +1,7 @@ const headerKeys = Object.freeze({ EXPERIMENT: 'X-Experiment-Data', TRANSACTION_ID: 'X-Transaction-Id', - EON: 'X-Enterprise-Order-Number', + ENTERPRISE_ORDER_NUMBER: 'X-Enterprise-Order-Number', APPLICATION_NAME: 'X-Application-Name', REFERRAL_SEQUENCE_NUMBER: 'X-Referral-Sequence-Number', SESSION_SEQUENCE_NUMBER: 'X-Session-Sequence-Number' diff --git a/src/global-methods.js b/src/global-methods.js index b720328b..bfe2a7e4 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -61,7 +61,7 @@ export default { [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), [headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME, [headerKeys.TRANSACTION_ID]: store.order.payment.creditCardToken.transactionId, - [headerKeys.EON]: store.order.eon, + [headerKeys.ENTERPRISE_ORDER_NUMBER]: store.order.eon, [headerKeys.REFERRAL_SEQUENCE_NUMBER]: store.order.referralSequenceNumber, [headerKeys.SESSION_SEQUENCE_NUMBER]: sessionKey }; diff --git a/src/helpers/logger.js b/src/helpers/logger.js index c124b1c2..3b40b6f0 100644 --- a/src/helpers/logger.js +++ b/src/helpers/logger.js @@ -72,7 +72,7 @@ export class Logger { [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), [headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME, [headerKeys.TRANSACTION_ID]: store.order.payment.creditCardToken.transactionId, - [headerKeys.EON]: store.order.eon, + [headerKeys.ENTERPRISE_ORDER_NUMBER]: store.order.eon, [headerKeys.REFERRAL_SEQUENCE_NUMBER]: store.order.referralSequenceNumber, [headerKeys.SESSION_SEQUENCE_NUMBER]: sessionKey }; From 90c7c459a76f220f385d1d9727d0870124b60921 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Tue, 15 Oct 2024 15:24:52 -0400 Subject: [PATCH 05/99] Fix for no supporting items on repair --- src/layouts/coverage-statement/coverage-statement.vue | 5 +++-- src/layouts/vehicle-damage/vehicle-damage.vue | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index 42c5bb20..ac0751e8 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -395,9 +395,10 @@ export default { showIssLoadingModal(false); }, async getPricedParts() { - const { glassParts } = this.mainStore.order.lineItems; + const { glassParts, supportingItems } = this.mainStore.order.lineItems; const availableLineItems = [ - ...(glassParts ?? []) + ...(glassParts ?? []), + ...(supportingItems ?? []) ]; // We only call the ITAC pricing endpoint if we are not repair or we are NoComp diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index b2394b82..a86fe4ca 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -415,7 +415,7 @@ export default { if (this.isWindshieldRepair) { const results = await Promise.allSettled([useMainStore().getSupportingItems(), useMainStore().getRecalParts()]); - const supportingItems = results[0]; + const supportingItems = results[0].value; useMainStore().updateSupportingItems(supportingItems.data); } else { useMainStore().updateSupportingItems([]); From edd1dcc4f8a9d2dcc36826623cafe0d46aaf9d51 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Wed, 16 Oct 2024 11:54:09 -0500 Subject: [PATCH 06/99] SSR-1632 Update getServiceabilityDetails for API Changes --- src/helpers/querystring-helper.js | 21 +++++++++++++++++++++ src/store/index.js | 22 +++++++++++++++------- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/helpers/querystring-helper.js b/src/helpers/querystring-helper.js index b827441a..3572916c 100644 --- a/src/helpers/querystring-helper.js +++ b/src/helpers/querystring-helper.js @@ -43,3 +43,24 @@ export function getTaxLineItemQueryString(lineItems, parameterName) { + `&${parameterName}[${index}].kitPrice=${lineItem.kitPrice}`).join('&'); return queryString.length !== 0 ? `&${queryString}` : ''; } + +export function buildURLSearchParams(data) { + const params = new URLSearchParams(); + Object.entries(data).forEach(([key, value]) => { + if (Array.isArray(value)) { + value.forEach((arrayValue, index) => { + if (typeof value === 'object') { + Object.entries(arrayValue).forEach(([objectKey, objectValue]) => { + params.append(`${key}[${index}].${objectKey}`, `${objectValue}`); + }); + } else { + params.append(`${key}[${index}]`, `${value}`); + } + }); + } else { + params.append(key, `${value}`); + } + }); + + return params; +} diff --git a/src/store/index.js b/src/store/index.js index d0542ae7..826062f2 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -26,7 +26,7 @@ import bailoutCode from '@/constants/bailoutCode'; import partNumberStrings from '@/constants/part-number-strings'; import { findLineItemIndex, getLineItemsFlattened } from '@/helpers/line-items-helper'; import { - buildQueryStringParameterFromArrayOfComplexObjects, + buildQueryStringParameterFromArrayOfComplexObjects, buildURLSearchParams, getLineItemQueryString, getPartNumbersListForQueryString, getTaxLineItemQueryString @@ -1276,18 +1276,26 @@ export const useMainStore = defineStore({ }, getServiceabilityDetails({ serviceZipCode }) { - const lineItems = getLineItemQueryString(this.order.lineItems.supportingItems, 'lineItems'); - - const { vehicle } = this.order; + const { vehicle, damage, parentAccountNumber, referralSequenceNumber, lineItems } = this.order; const { carId } = vehicle; - const { damage } = this.order; const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace); + const lineItemParts = [...lineItems.glassParts, ...lineItems.supportingItems].map((part) => ({ + partNumber: part.partNumber, + recalibrationType: part.recalibrationType + })); - const glassPieces = buildQueryStringParameterFromArrayOfComplexObjects(glassArray, 'glassPieces'); + const params = buildURLSearchParams({ + parentAccountNumber, + referralSequenceNumber, + zip: serviceZipCode, + carId, + glassPieces: glassArray, + lineItems: lineItemParts + }); return globalMethods.callHttpClient({ method: endpoints.GetServiceabilityDetails.method, - endpoint: `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}${lineItems}&${glassPieces}` + endpoint: `${endpoints.GetServiceabilityDetails.url}?${params.toString()}` }); }, lookupVehicleByVin(vin) { From 5b72a3fddda5766f2857e9e7eecf3c38ca4bd378 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Wed, 16 Oct 2024 13:37:04 -0500 Subject: [PATCH 07/99] SSR-1632 Don't pass null values --- src/helpers/querystring-helper.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/helpers/querystring-helper.js b/src/helpers/querystring-helper.js index 3572916c..11dcdb48 100644 --- a/src/helpers/querystring-helper.js +++ b/src/helpers/querystring-helper.js @@ -47,11 +47,16 @@ export function getTaxLineItemQueryString(lineItems, parameterName) { export function buildURLSearchParams(data) { const params = new URLSearchParams(); Object.entries(data).forEach(([key, value]) => { + if (value == null) { + return; + } if (Array.isArray(value)) { value.forEach((arrayValue, index) => { if (typeof value === 'object') { Object.entries(arrayValue).forEach(([objectKey, objectValue]) => { - params.append(`${key}[${index}].${objectKey}`, `${objectValue}`); + if (objectValue != null) { + params.append(`${key}[${index}].${objectKey}`, `${objectValue}`); + } }); } else { params.append(`${key}[${index}]`, `${value}`); From 2f816c8ec1aca974cbd84a224882267bca9741ab Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Wed, 16 Oct 2024 15:06:55 -0500 Subject: [PATCH 08/99] SSR-1483 Don't show cart if we fail to settle PIA Implement create WO for PIA Implement settledTenderAmount & lockToken --- src/helpers/order-helper.js | 10 ++++---- .../order-confirmation.spec.js | 23 +++++++++++++++++++ .../order-confirmation/order-confirmation.vue | 14 +++++++++++ src/layouts/payment-method/payment-method.vue | 12 ++++++++-- src/store/index.js | 10 +++++++- 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/helpers/order-helper.js b/src/helpers/order-helper.js index 58d8bc38..3190e7f7 100644 --- a/src/helpers/order-helper.js +++ b/src/helpers/order-helper.js @@ -4,8 +4,8 @@ import submitType from '@/constants/submit-type'; /* Encapsulates asynchronous Save Session logic inside a promise to allow for Save Session queuing */ -async function saveSessionHelper(store, { submitAfterSave }) { - const savedSessionInfo = await store.saveSession({ submitAfterSave }); +async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }) { + const savedSessionInfo = await store.saveSession({ submitAfterSave, createWorkOrderNumberForPIA }); if (savedSessionInfo) { store.setSaveSessionInfo(savedSessionInfo.data); } @@ -16,11 +16,11 @@ async function saveSessionHelper(store, { submitAfterSave }) { This will also set Referral information in the store after saving, and then update the cookie. To force synchronous behavior pass in 'true' for shouldAwaitSaveSessionQueue */ -export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false }) { +export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createWorkOrderNumberForPIA = false }) { const store = useMainStore(); const saveSessionPromise = store.applicationUser.saveSessionPromise - ? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave })) - : saveSessionHelper(store, { submitAfterSave }); + ? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA })) + : saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }); store.setSaveSessionPromise(saveSessionPromise); diff --git a/src/layouts/order-confirmation/order-confirmation.spec.js b/src/layouts/order-confirmation/order-confirmation.spec.js index e0f651c6..dfb9a45a 100644 --- a/src/layouts/order-confirmation/order-confirmation.spec.js +++ b/src/layouts/order-confirmation/order-confirmation.spec.js @@ -920,6 +920,29 @@ describe('OrderConfirmation.vue', () => { // Act const testValue = wrapper.vm.isDropOffAppointment; + // Assert + expect(testValue).toBe(expected); + }); + }); + describe('showCart', () => { + test.each([ + [true, true, 100], + [true, false, 100], + [false, true, 0], + [false, true, null] + ])('showCart is %p when is PIA is %p and settledTenderAmount is %p', (expected, isPia, settledTenderAmount) => { + // Arrange + const order = deepClone(sessionStorage); + order.payment.isPayInAdvance = isPia; + order.settledTenderAmount = settledTenderAmount; + const mockStoreActions = () => { + useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order); + }; + const { wrapper } = getMountedComponent({}, {}, mockStoreActions); + + // Act + const testValue = wrapper.vm.showCart; + // Assert expect(testValue).toBe(expected); }); diff --git a/src/layouts/order-confirmation/order-confirmation.vue b/src/layouts/order-confirmation/order-confirmation.vue index e33a5e08..4f649c88 100644 --- a/src/layouts/order-confirmation/order-confirmation.vue +++ b/src/layouts/order-confirmation/order-confirmation.vue @@ -56,6 +56,7 @@
0) { + return true; + } + + // settleTenderAmount always shows 0 via localhost or dev. + // Temporarily set return true to see cart in localhost or dev environment + return false; } }, mounted() { diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 832e2d9a..cd16dac3 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -89,9 +89,10 @@ import issPageValues from '@/router/router-constants/issPage-values'; import bailoutMessage from '@/constants/bailoutMessage'; import { AppointmentTypeStrings } from '@/constants/schedule-constants'; import VehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; -import { submitWorkOrder } from '@/helpers/order-helper.js'; +import { saveSession, submitWorkOrder } from '@/helpers/order-helper.js'; import { experimentSettings } from '@/constants/experiments'; import submitType from '@/constants/submit-type'; +import routerParams from '@/router/router-constants/router-params'; export default { name: 'payment-method', @@ -284,9 +285,16 @@ export default { console.error(`error: response from submit work order:${error.message}`); } } else { + await saveSession({ + createWorkOrderNumberForPIA: true, + shouldAwaitSaveSessionQueue: true + }); + this.$router.navigate( this.navigationScenarios.CLICKED_PAY_NOW, - this.$route + this.$route, + {}, + { [routerParams.SKIP_SAVE_SESSION]: true } ); } } diff --git a/src/store/index.js b/src/store/index.js index 826062f2..1d831100 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -214,6 +214,8 @@ export const getDefaultState = () => ({ eon: null, workOrderId: null, workOrderNumber: null, + settledTenderAmount: null, + lockToken: null, customerPortalLoginToken: null, originalDeductible: null, currentDeductible: null, @@ -1330,12 +1332,14 @@ export const useMainStore = defineStore({ this.order.referralCorrelationId = response.referralCorrelationId; this.order.eon = response.eon; this.order.workOrderNumber = response.workOrderNumber; + this.order.settledTenderAmount = response.settledTenderAmount; + this.order.lockToken = response.lockToken; this.order.customerPortalLoginToken = response.customerPortalLoginToken; this.applicationUser.savedSessionId = response.savedSessionId; this.applicationUser.crmCustomerId = response.crmCustomerId.toString(); }, - saveSession({ submitAfterSave }) { + saveSession({ submitAfterSave, createWorkOrderNumberForPIA }) { const { vehicle, damage, policy, customer, contactInfo, payment, lineItems, serviceLocation, schedule, insuranceCoverage } = this.order; @@ -1475,6 +1479,8 @@ export const useMainStore = defineStore({ referralSequenceNumber: this.order.referralSequenceNumber, eon: this.order.eon, submitToMainframe: !!this.order.referralNumber, + createWorkOrderNumberForPIA: createWorkOrderNumberForPIA, + lockToken: this.order.lockToken, loadedFromDupeCheck, submitAfterSave: !!submitAfterSave }; @@ -1989,6 +1995,8 @@ export const useMainStore = defineStore({ this.order.referralSequenceNumber = null; this.order.workOrderId = null; this.order.workOrderNumber = null; + this.order.settledTenderAmount = null; + this.order.lockToken = null; this.order.eon = null; this.order.originalDeductible = null; this.order.currentDeductible = null; From 65be039010f837c9c27d2a52b9709ebeb25a327b Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Thu, 17 Oct 2024 09:16:03 -0500 Subject: [PATCH 09/99] SSR-1632 Handle null line items --- src/store/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/index.js b/src/store/index.js index 826062f2..f0f767db 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1279,7 +1279,7 @@ export const useMainStore = defineStore({ const { vehicle, damage, parentAccountNumber, referralSequenceNumber, lineItems } = this.order; const { carId } = vehicle; const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace); - const lineItemParts = [...lineItems.glassParts, ...lineItems.supportingItems].map((part) => ({ + const lineItemParts = [...(lineItems.glassParts || []), ...(lineItems.supportingItems || [])].map((part) => ({ partNumber: part.partNumber, recalibrationType: part.recalibrationType })); From 6792aa0d9adf52cb6f3e4cdf039bb5a49430076a Mon Sep 17 00:00:00 2001 From: Katie Kroell Date: Fri, 18 Oct 2024 12:23:45 -0400 Subject: [PATCH 10/99] change value of transaction id --- src/global-methods.js | 2 +- src/helpers/logger.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/global-methods.js b/src/global-methods.js index bfe2a7e4..deed5368 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -60,7 +60,7 @@ export default { const headers = { [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), [headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME, - [headerKeys.TRANSACTION_ID]: store.order.payment.creditCardToken.transactionId, + [headerKeys.TRANSACTION_ID]: store.order.referralCorrelationId, [headerKeys.ENTERPRISE_ORDER_NUMBER]: store.order.eon, [headerKeys.REFERRAL_SEQUENCE_NUMBER]: store.order.referralSequenceNumber, [headerKeys.SESSION_SEQUENCE_NUMBER]: sessionKey diff --git a/src/helpers/logger.js b/src/helpers/logger.js index 3b40b6f0..96eb0909 100644 --- a/src/helpers/logger.js +++ b/src/helpers/logger.js @@ -71,7 +71,7 @@ export class Logger { const headers = { [headerKeys.EXPERIMENT]: JSON.stringify(store.experimentSettings), [headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME, - [headerKeys.TRANSACTION_ID]: store.order.payment.creditCardToken.transactionId, + [headerKeys.TRANSACTION_ID]: store.order.referralCorrelationId, [headerKeys.ENTERPRISE_ORDER_NUMBER]: store.order.eon, [headerKeys.REFERRAL_SEQUENCE_NUMBER]: store.order.referralSequenceNumber, [headerKeys.SESSION_SEQUENCE_NUMBER]: sessionKey From a81562a63a2df843bd48a72c86bbc046651014c1 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Fri, 18 Oct 2024 14:29:32 -0500 Subject: [PATCH 11/99] Don't mark the coverage status as verified if we don't have a valid policy --- src/layouts/coverage-statement/coverage-statement.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index ac0751e8..8d141f20 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -379,7 +379,7 @@ export default { async initializeComponent() { if (this.shouldRegisterClaim) { await this.mainStore.registerClaim(); - } else if (!this.mainStore.isClaimRegistrationRequired) { + } else if (!this.mainStore.isClaimRegistrationRequired && this.mainStore.order.insuranceCoverage.coverageType !== coverageType.NONE) { this.mainStore.updateCoverageStatus(coverageStatuses.VERIFIED); } From 1e426da316c9a9a9a41dfb9db702f51305736a78 Mon Sep 17 00:00:00 2001 From: Katie Kroell Date: Fri, 18 Oct 2024 16:03:05 -0400 Subject: [PATCH 12/99] WIP --- .../shop-preference-modal.vue | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/layouts/provider-preference/shop-preference-modal/shop-preference-modal.vue b/src/layouts/provider-preference/shop-preference-modal/shop-preference-modal.vue index 96b239c5..8955b622 100644 --- a/src/layouts/provider-preference/shop-preference-modal/shop-preference-modal.vue +++ b/src/layouts/provider-preference/shop-preference-modal/shop-preference-modal.vue @@ -24,6 +24,8 @@ diff --git a/src/layouts/payment-return/payment-return.vue b/src/layouts/payment-return/payment-return.vue index eed08848..39dc8465 100644 --- a/src/layouts/payment-return/payment-return.vue +++ b/src/layouts/payment-return/payment-return.vue @@ -15,7 +15,6 @@ import getQueryStringParameter from '@/helpers/querystring-helper.js'; import { paymentMethods } from '@/constants/payment-method-constants.js'; import { getCartTotal } from '@/helpers/cart-helper'; import { submitWorkOrder } from '@/helpers/order-helper.js'; -import showIssLoadingModal from '@/helpers/loading-modal-helper'; import submitType from '@/constants/submit-type'; export default { @@ -44,7 +43,6 @@ export default { } }, async mounted() { - showIssLoadingModal(true); const payInAdvanceError = getQueryStringParameter(queryStrings.ERROR); const { paymentMethod } = useMainStore().order.payment; @@ -130,11 +128,9 @@ export default { } catch (error) { console.error(`error: response from submit work order:${error.message}`); this.navigateOnPayInAdvanceError(); - showIssLoadingModal(false); return; } - showIssLoadingModal(false); this.$router.navigate( this.navigationScenarios.PAY_IN_ADVANCE_SUCCESS, this.$route From 84d52c900f93a52fe944e47867ccae2bfb0bab29 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Mon, 18 Nov 2024 12:59:57 -0600 Subject: [PATCH 56/99] SSR-1473 toLowerCase key --- src/layouts/entry-page/entry-page.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/entry-page/entry-page.vue b/src/layouts/entry-page/entry-page.vue index 0fcf2c21..2abbb5a0 100644 --- a/src/layouts/entry-page/entry-page.vue +++ b/src/layouts/entry-page/entry-page.vue @@ -194,7 +194,7 @@ export default { populateStoreItemsFromParams(params) { // Populate store items from parameters. for (const [key, value] of Object.entries(params)) { - switch (key) { + switch (key.toLowerCase()) { case 'policynbr': case 'policynumber': this.mainStore.order.policy.policyNumber = value; From 04af205b1c2eaf0237abc8d740d3de79bde82c0d Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 18 Nov 2024 14:15:26 -0500 Subject: [PATCH 57/99] need loading modal to appear on return page --- src/layouts/payment-return/payment-return.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/layouts/payment-return/payment-return.vue b/src/layouts/payment-return/payment-return.vue index 39dc8465..08f844a8 100644 --- a/src/layouts/payment-return/payment-return.vue +++ b/src/layouts/payment-return/payment-return.vue @@ -16,6 +16,7 @@ import { paymentMethods } from '@/constants/payment-method-constants.js'; import { getCartTotal } from '@/helpers/cart-helper'; import { submitWorkOrder } from '@/helpers/order-helper.js'; import submitType from '@/constants/submit-type'; +import showIssLoadingModal from '@/helpers/loading-modal-helper'; export default { name: 'payment-return', @@ -43,6 +44,7 @@ export default { } }, async mounted() { + showIssLoadingModal(true); const payInAdvanceError = getQueryStringParameter(queryStrings.ERROR); const { paymentMethod } = useMainStore().order.payment; From 96693dbeccd2c47ff0f5d34d496d65a689cb8480 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Tue, 19 Nov 2024 15:26:10 -0600 Subject: [PATCH 58/99] SSR-1830 Fix Policy Vehicle race condition --- src/layouts/policy-vehicles/policy-vehicles.vue | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index 5251385e..cc35fd73 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -85,7 +85,6 @@ export default { return { policyVehicles, selectedVehicleVin: '', - selectedPolicyVehicle: null, displayGeneric: true, policyVinFound: true, rules: { @@ -112,6 +111,13 @@ export default { }) ?? []; return mappedData; }, + selectedPolicyVehicle() { + if (this.selectedVehicleVin !== vehicleSelectionOptions.VEHICLE_NOT_LISTED) { + return this.policyVehicles.find((p) => p.vin === this.selectedVehicleVin); + } + + return null; + }, noCoverageForSelectedVehicle() { return noCoverageForSelectedVehicle(this.selectedPolicyVehicle); }, @@ -135,7 +141,6 @@ export default { // clear previously selected vehicle and image this.mainStore.resetVehicleState(); this.displayGeneric = true; - this.selectedPolicyVehicle = null; } else { // get vehicle details from selected VIN const vehicle = await this.lookupVehicleByVin(value); @@ -144,13 +149,11 @@ export default { if (vehicle?.error === true) { this.mainStore.resetVehicleState(); this.displayGeneric = true; - this.selectedPolicyVehicle = null; return; } if (vehicle) { // save selected vehicle to the store this.displayGeneric = false; - this.selectedPolicyVehicle = this.policyVehicles.find((p) => p.vin === value); useMainStore().updateVehicle({ ...vehicle.data, policyVehicleId: this.selectedPolicyVehicle?.id, From ac09198ed64bada75bfe30ef19caa91844da2436 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Thu, 21 Nov 2024 16:26:10 -0500 Subject: [PATCH 59/99] Removing styling from footer --- src/styles/client-customizations.scss | 37 ++++++++++++--------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/styles/client-customizations.scss b/src/styles/client-customizations.scss index 07142425..1b7bdaef 100644 --- a/src/styles/client-customizations.scss +++ b/src/styles/client-customizations.scss @@ -30,29 +30,24 @@ a.new-window-link { color: $link; } - footer { - a { - color: $link; - } - .btn { - &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { - &.btn-primary { - background: $button-color; - color: $button-text-color; - } - - &:focus-visible { - background: $button-color; - color: $button-text-color; - box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; - } - + .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; } - } - .loader { - &:after { - background-color: $button-text-color; + + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; } + + } + } + .loader { + &:after { + background-color: $button-text-color; } } } From 143bb76047647980543e29b961eeb7b3ba6a074e Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Fri, 22 Nov 2024 15:36:29 -0500 Subject: [PATCH 60/99] Updates for Cancel claim Also added Michaela updates from SSR-1860 to all client sections. --- src/styles/client-customizations.scss | 252 +++++++++++++++++--------- 1 file changed, 171 insertions(+), 81 deletions(-) diff --git a/src/styles/client-customizations.scss b/src/styles/client-customizations.scss index 1b7bdaef..06af6ea6 100644 --- a/src/styles/client-customizations.scss +++ b/src/styles/client-customizations.scss @@ -26,10 +26,12 @@ background-color: $accent-fill; background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='" + $svg-fill-color + "'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='" + $svg-fill-color + "'/%3E%3C/svg%3E"); } + a, a.new-window-link { color: $link; } + .btn { &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { &.btn-primary { @@ -45,6 +47,7 @@ } } + .loader { &:after { background-color: $button-text-color; @@ -54,8 +57,26 @@ &.modal-open { .modal { - .modal-footer { + .modal-body { .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; + } + + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; + } + + } + } + } + + .modal-footer { + .btn:not(.navigation-link) { background: $button-color; color: $button-text-color; } @@ -63,6 +84,7 @@ } } } + // End Liberty Mutual, SafeCo @@ -93,42 +115,58 @@ background-color: $accent-fill; background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='" + $svg-fill-color + "'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='" + $svg-fill-color + "'/%3E%3C/svg%3E"); } + a, a.new-window-link, .btn-link { color: $link; } - footer { - a { - color: $link; - } - .btn { - &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { - &.btn-primary { - background: $button-color; - color: $button-text-color; - } - &:focus-visible { - background: $button-color; - color: $button-text-color; - box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; - } + .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; + } + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; } + } - .loader { - &:after { - background-color: $button-text-color; - } + } + + .loader { + &:after { + background-color: $button-text-color; } } } &.modal-open { .modal { - .modal-footer { + .modal-body { .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; + } + + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; + } + + } + } + } + + .modal-footer { + .btn:not(.navigation-link) { background: $button-color; color: $button-text-color; } @@ -136,6 +174,7 @@ } } } + // End Amica @@ -165,41 +204,57 @@ background-color: $accent-fill; background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='" + $svg-fill-color + "'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='" + $svg-fill-color + "'/%3E%3C/svg%3E"); } + a, a.new-window-link { color: $link; } - footer { - a { - color: $link; - } - .btn { - &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { - &.btn-primary { - background: $button-color; - color: $button-text-color; - } - &:focus-visible { - background: $button-color; - color: $button-text-color; - box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; - } + .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; + } + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; } + } - .loader { - &:after { - background-color: $button-text-color; - } + } + + .loader { + &:after { + background-color: $button-text-color; } } } &.modal-open { .modal { - .modal-footer { + .modal-body { .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; + } + + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; + } + + } + } + } + + .modal-footer { + .btn:not(.navigation-link) { background: $button-color; color: $button-text-color; } @@ -207,6 +262,7 @@ } } } + // End Nationwide @@ -236,41 +292,57 @@ background-color: $accent-fill; background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='" + $svg-fill-color + "'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='" + $svg-fill-color + "'/%3E%3C/svg%3E"); } + a, a.new-window-link { color: $link; } - footer { - a { - color: $link; - } - .btn { - &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { - &.btn-primary { - background: $button-color; - color: $button-text-color; - } - &:focus-visible { - background: $button-color; - color: $button-text-color; - box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; - } + .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; + } + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; } + } - .loader { - &:after { - background-color: $button-text-color; - } + } + + .loader { + &:after { + background-color: $button-text-color; } } } &.modal-open { .modal { - .modal-footer { + .modal-body { .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; + } + + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; + } + + } + } + } + + .modal-footer { + .btn:not(.navigation-link) { background: $button-color; color: $button-text-color; } @@ -278,6 +350,7 @@ } } } + // End Country Financial @@ -307,41 +380,57 @@ background-color: $accent-fill; background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='" + $svg-fill-color + "'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='" + $svg-fill-color + "'/%3E%3C/svg%3E"); } + a, a.new-window-link { color: $link; } - footer { - a { - color: $link; - } - .btn { - &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { - &.btn-primary { - background: $button-color; - color: $button-text-color; - } - &:focus-visible { - background: $button-color; - color: $button-text-color; - box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; - } + .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; + } + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; } + } - .loader { - &:after { - background-color: $button-text-color; - } + } + + .loader { + &:after { + background-color: $button-text-color; } } } &.modal-open { .modal { - .modal-footer { + .modal-body { .btn { + &.btn-override[aria-disabled="false"]:not(.form-test-invalid) { + &.btn-primary { + background: $button-color; + color: $button-text-color; + } + + &:focus-visible { + background: $button-color; + color: $button-text-color; + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $button-color; + } + + } + } + } + + .modal-footer { + .btn:not(.navigation-link) { background: $button-color; color: $button-text-color; } @@ -349,4 +438,5 @@ } } } + // End Travelers \ No newline at end of file From 550a1c836342e996087aed61572c0129875ec37b Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 25 Nov 2024 14:55:37 -0500 Subject: [PATCH 61/99] Replacing most issConfig parent account numbers with order ones --- src/store/index.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index c9192a98..fbb2b5fb 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -565,6 +565,10 @@ export const useMainStore = defineStore({ this.updateCoverageType(coverageType.Deductible); const insured = responsePolicy.insureds?.[0]; + // populate parent account number + // todo confirm that this is the right policy + order.parentAccountNumber = responsePolicy.parentAccountNumber; + // populate policy holder details from policy lookup order.customer.address.streetAddress = insured?.address; order.customer.address.city = insured?.city; @@ -694,7 +698,7 @@ export const useMainStore = defineStore({ endpoint: endpoints.FinalDeductible.url, payload: { referralCorrelationId: this.order.referralCorrelationId, - accountNumber: this.issConfig.parentAccountNumber?.toString() ?? '', + accountNumber: this.order.parentAccountNumber?.toString() ?? '', endorsements: endorsementAnswersForPayload, manualGlassNames: manualGlassNamesArray, policyState: this.order.customer.address.state, @@ -722,7 +726,7 @@ export const useMainStore = defineStore({ getDuplicateReferrals() { const params = new URLSearchParams({ - parentAccountNumber: this.issConfig.parentAccountNumber, + parentAccountNumber: this.order.parentAccountNumber, customerPhoneNumber: this.order.contactInfo.servicePhone, policyNumber: this.order.policy.policyNumber, emailAddress: this.order.customer.emailAddress, @@ -879,7 +883,7 @@ export const useMainStore = defineStore({ endDate, applicationName: applicationConfig.APPLICATION_NAME, billToAccountNumber: this.billToAccountNumber, - parentAccountNumber: this.issConfig.parentAccountNumber, // this.payment.parentAccountNumber, + parentAccountNumber: order.parentAccountNumber, // this.payment.parentAccountNumber, carId: vehicle.carId, lineItems, glassPieces, @@ -943,7 +947,7 @@ export const useMainStore = defineStore({ shopAppointmentType, applicationName: applicationConfig.APPLICATION_NAME, billToAccountNumber: this.billToAccountNumber, - parentAccountNumber: this.issConfig.parentAccountNumber, // this.payment.parentAccountNumber, + parentAccountNumber: this.order.parentAccountNumber, // this.payment.parentAccountNumber, carId: vehicle.carId, lineItems, glassPieces, @@ -1007,7 +1011,7 @@ export const useMainStore = defineStore({ getProviders(serviceZipCode) { const { carId } = this.order.vehicle; const damageType = this.damage.isRepair ? 'Repair' : 'Replace'; - const { parentAccountNumber } = this.issConfig; + const { parentAccountNumber } = this.order; const partsWithRecal = getTopLevelGlassPartsWithRecal(this.order.lineItems.glassParts); const windshieldPartWithRecal = partsWithRecal?.length > 0 ? partsWithRecal[0] : null; const safeliteOnly = true; @@ -1026,7 +1030,7 @@ export const useMainStore = defineStore({ getTpaProviders(zipCode, radius) { const { carId } = this.order.vehicle; const damageType = this.damage.isRepair ? 'Repair' : 'Replace'; - const { parentAccountNumber } = this.issConfig; + const { parentAccountNumber } = this.order; const partsWithRecal = getTopLevelGlassPartsWithRecal(this.order.lineItems.glassParts); const windshieldPartWithRecal = partsWithRecal?.length > 0 ? partsWithRecal[0] : null; const safeliteOnly = false; @@ -1047,7 +1051,7 @@ export const useMainStore = defineStore({ return new Promise((resolve, reject) => { globalMethods.callHttpClient({ method: endpoints.GetAccountInfo.method, - endpoint: endpoints.GetAccountInfo.url + this.issConfig.parentAccountNumber + endpoint: endpoints.GetAccountInfo.url + this.order.parentAccountNumber }).then((response) => { this.order.carrierPhoneNumber = response.data.phoneNumber; return resolve(response.data); @@ -1130,6 +1134,7 @@ export const useMainStore = defineStore({ endpoint: `${endpoints.GetGlassFees.url}?${params.toString()}${partNumberListQueryString}` }); }, + // TODO do we even use this anymore? async getSupportingItems() { const { glassParts } = this.lineItems; const { carId } = this.vehicle; @@ -1458,7 +1463,7 @@ export const useMainStore = defineStore({ claimNumber: insuranceCoverage.claimNumber }, payment: { - parentAccountNumber: this.issConfig.parentAccountNumber, + parentAccountNumber: this.order.payment.parentAccountNumber, billToAccountNumber: this.billToAccountNumber, paypalToken: payment.paypalToken, paymentMethod: payment.paymentMethod === paymentMethods.PayNow ? null : payment.paymentMethod, @@ -1556,6 +1561,7 @@ export const useMainStore = defineStore({ payload: { referralNumber: duplicate.referralNumber, referralDate: duplicate.responseDate, + // TODO confirm this is right to use here parentAccountNumber: issConfig.parentAccountNumber, referralCorrelationId: duplicate.correlationId } @@ -2649,7 +2655,7 @@ export const useMainStore = defineStore({ const { order, issConfig } = this; try { const params = new URLSearchParams({ - parentAccountNumber: issConfig.parentAccountNumber.toString(), + parentAccountNumber: order.parentAccountNumber.toString(), providerNumber: this.providerNumber, typeOfClaim: 'GLASS ONLY', lineOfBusiness: 'PERSONAL', From 48eaebc9b52119384314650522ae1692440a3415 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 25 Nov 2024 14:57:58 -0500 Subject: [PATCH 62/99] Missed one --- src/store/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/index.js b/src/store/index.js index fbb2b5fb..55d0b1d7 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -610,7 +610,7 @@ export const useMainStore = defineStore({ payload: { referralCorrelationId: this.order.referralCorrelationId, - accountNumber: this.issConfig.parentAccountNumber?.toString() ?? '', + accountNumber: this.order.parentAccountNumber?.toString() ?? '', policyData: this.order.policy.policyData, isItac: isITAC, insured: { From b253d3d78eeac297aa9b05696fba4975264d777c Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 25 Nov 2024 15:42:50 -0500 Subject: [PATCH 63/99] Just spacing --- src/layouts/entry-page/entry-page.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/entry-page/entry-page.vue b/src/layouts/entry-page/entry-page.vue index 2abbb5a0..455e6c6f 100644 --- a/src/layouts/entry-page/entry-page.vue +++ b/src/layouts/entry-page/entry-page.vue @@ -70,7 +70,7 @@ export default { } if (clientData.parameters?.length > 0) { - const finalParams = this.combineClientParameters(clientData.parameters, {...queryStringParams, ...decryptedParams}); + const finalParams = this.combineClientParameters(clientData.parameters, { ...queryStringParams, ...decryptedParams }); this.populateStoreItemsFromParams(finalParams); } From 7e5938510e8ccd20ca53d3ae29257c74ab5bacae Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 25 Nov 2024 16:08:47 -0500 Subject: [PATCH 64/99] Using order parent account number --- src/store/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index 55d0b1d7..221888a2 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1554,15 +1554,14 @@ export const useMainStore = defineStore({ }); }, async loadSession(duplicate) { - const { order, issConfig } = this; + const { order } = this; const response = await globalMethods.callHttpClient({ method: endpoints.LoadSession.method, endpoint: endpoints.LoadSession.url, payload: { referralNumber: duplicate.referralNumber, referralDate: duplicate.responseDate, - // TODO confirm this is right to use here - parentAccountNumber: issConfig.parentAccountNumber, + parentAccountNumber: order.parentAccountNumber, referralCorrelationId: duplicate.correlationId } }); From d9c6139b21f0f1ce792901eecf71fd8658790dc9 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 25 Nov 2024 16:22:43 -0500 Subject: [PATCH 65/99] fixing response policy account number name --- src/store/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index 221888a2..6abb1775 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -566,8 +566,7 @@ export const useMainStore = defineStore({ const insured = responsePolicy.insureds?.[0]; // populate parent account number - // todo confirm that this is the right policy - order.parentAccountNumber = responsePolicy.parentAccountNumber; + order.parentAccountNumber = responsePolicy.accountNumber; // populate policy holder details from policy lookup order.customer.address.streetAddress = insured?.address; From da5ec8d8e92a092fa2f8e1f9b6994d181c840719 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 25 Nov 2024 16:27:33 -0500 Subject: [PATCH 66/99] Removing comment --- src/store/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/store/index.js b/src/store/index.js index 6abb1775..0be3f3e9 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1133,7 +1133,6 @@ export const useMainStore = defineStore({ endpoint: `${endpoints.GetGlassFees.url}?${params.toString()}${partNumberListQueryString}` }); }, - // TODO do we even use this anymore? async getSupportingItems() { const { glassParts } = this.lineItems; const { carId } = this.vehicle; From 61737998846205ee2b95515c6cf8800146f3da4e Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 25 Nov 2024 17:05:05 -0500 Subject: [PATCH 67/99] Fixing tests --- src/store/index.js | 2 +- src/store/store.spec.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index 0be3f3e9..5b7a8bce 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1461,7 +1461,7 @@ export const useMainStore = defineStore({ claimNumber: insuranceCoverage.claimNumber }, payment: { - parentAccountNumber: this.order.payment.parentAccountNumber, + parentAccountNumber: this.order.parentAccountNumber, billToAccountNumber: this.billToAccountNumber, paypalToken: payment.paypalToken, paymentMethod: payment.paymentMethod === paymentMethods.PayNow ? null : payment.paymentMethod, diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 69242242..c53fa5ac 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -844,7 +844,7 @@ describe('Store', () => { it('calls api with expected payment', async () => { // Arrange const parentAccountNumber = getRandomString(6, 6); - store.issConfig.parentAccountNumber = parentAccountNumber; + store.order.parentAccountNumber = parentAccountNumber; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); // Act @@ -1740,7 +1740,7 @@ describe('Store', () => { const policyData = getRandomString(100, 200); store.order.referralCorrelationId = referralCorrelationId; - store.issConfig.parentAccountNumber = parentAccountNumber; + store.order.parentAccountNumber = parentAccountNumber; store.order.currentDeductible = currentDeductible; store.order.originalDeductible = originalDeductible; store.order.policy.status = status; @@ -1837,7 +1837,7 @@ describe('Store', () => { const policyData = getRandomString(100, 200); store.order.referralCorrelationId = referralCorrelationId; - store.issConfig.parentAccountNumber = parentAccountNumber; + store.order.parentAccountNumber = parentAccountNumber; store.order.currentDeductible = currentDeductible; store.order.originalDeductible = originalDeductible; store.order.policy.status = status; @@ -1939,7 +1939,7 @@ describe('Store', () => { const carId = getRandomString(10, 14); const parentAccountNumber = getRandomString(6, 6); - store.issConfig.parentAccountNumber = parentAccountNumber; + store.order.parentAccountNumber = parentAccountNumber; store.order.damage.isRepair = isRepair; store.order.vehicle.carId = carId; const zipCode = getRandomString(6, 6); From f1df4cc69a79411457c2462838b3b0b8cde88df0 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Tue, 26 Nov 2024 13:33:42 -0500 Subject: [PATCH 68/99] If value truthy then set --- src/store/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/store/index.js b/src/store/index.js index 5b7a8bce..373a38b7 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -566,7 +566,9 @@ export const useMainStore = defineStore({ const insured = responsePolicy.insureds?.[0]; // populate parent account number - order.parentAccountNumber = responsePolicy.accountNumber; + if (responsePolicy.accountNumber) { + order.parentAccountNumber = responsePolicy.accountNumber; + } // populate policy holder details from policy lookup order.customer.address.streetAddress = insured?.address; From 85f105a9aee53e5c000f57b0887b93c68b658736 Mon Sep 17 00:00:00 2001 From: Katie Kroell Date: Tue, 26 Nov 2024 16:35:09 -0500 Subject: [PATCH 69/99] use customer.address.state in ITAC pricing call if damageState isn't available --- src/store/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/index.js b/src/store/index.js index 373a38b7..03b3b356 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1165,7 +1165,7 @@ export const useMainStore = defineStore({ ServerData: this.order.lineItems.serverData, Customer: { PhoneNumber: contactInfo.servicePhone, - State: policy.damageState, + State: policy.damageState || this.order.customer.address.state, ZipCode: policy.policyZipCode }, Account: { From 0d6e8f2cd94601fbcf18821dc050a8110fb91a53 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Wed, 27 Nov 2024 10:58:32 -0600 Subject: [PATCH 70/99] SSR-1924 Fix custom client loader color --- .../site-footer/site-footer.vue | 1 - src/styles/client-customizations.scss | 45 +++++++------------ src/styles/common-styles.scss | 4 ++ src/ux-components/button-main/button-main.vue | 9 ++-- src/ux-components/loader/loader.vue | 19 +------- 5 files changed, 24 insertions(+), 54 deletions(-) diff --git a/src/iss-components/site-footer/site-footer.vue b/src/iss-components/site-footer/site-footer.vue index 8ca18f60..fd41ea76 100644 --- a/src/iss-components/site-footer/site-footer.vue +++ b/src/iss-components/site-footer/site-footer.vue @@ -18,7 +18,6 @@ ref="buttonMain" isPrimary :buttonText="buttonText" - loaderColor="white" :class=" (disableForwardAction || isForwardActionDisabled) && 'form-test-invalid' diff --git a/src/styles/client-customizations.scss b/src/styles/client-customizations.scss index 06af6ea6..bf59ece8 100644 --- a/src/styles/client-customizations.scss +++ b/src/styles/client-customizations.scss @@ -12,6 +12,9 @@ $button-text-color: #181643; $svg-fill-color: '%2309748b'; // Color of calendar icon. Place HEX code *after* %23 + //Variables + --iss-loader-color: #{$button-text-color}; + .site-header { background-color: $header-background; } @@ -47,12 +50,6 @@ } } - - .loader { - &:after { - background-color: $button-text-color; - } - } } &.modal-open { @@ -100,6 +97,9 @@ $button-text-color: #fff; $svg-fill-color: '%231574a1'; // Color of calendar icon. Place HEX code *after* %23 + //Variables + --iss-loader-color: #{$button-text-color}; + .site-header { background-color: $header-background; } @@ -137,12 +137,6 @@ } } - - .loader { - &:after { - background-color: $button-text-color; - } - } } &.modal-open { @@ -190,6 +184,9 @@ $button-text-color: #fff; $svg-fill-color: '%231c57a5'; // Color of calendar icon. Place HEX code *after* %23 + //Variables + --iss-loader-color: #{$button-text-color}; + .site-header { background-color: $header-background; } @@ -225,12 +222,6 @@ } } - - .loader { - &:after { - background-color: $button-text-color; - } - } } &.modal-open { @@ -278,6 +269,9 @@ $button-text-color: #fff; $svg-fill-color: '%23007a3e'; // Color of calendar icon. Place HEX code *after* %23 + //Variables + --iss-loader-color: #{$button-text-color}; + .site-header { background-color: $header-background; } @@ -313,12 +307,6 @@ } } - - .loader { - &:after { - background-color: $button-text-color; - } - } } &.modal-open { @@ -366,6 +354,9 @@ $button-text-color: #fff; $svg-fill-color: '%23007395'; // Color of calendar icon. Place HEX code *after* %23 + //Variables + --iss-loader-color: #{$button-text-color}; + .site-header { background-color: $header-background; } @@ -401,12 +392,6 @@ } } - - .loader { - &:after { - background-color: $button-text-color; - } - } } &.modal-open { diff --git a/src/styles/common-styles.scss b/src/styles/common-styles.scss index 6cecb8ec..66c9534e 100644 --- a/src/styles/common-styles.scss +++ b/src/styles/common-styles.scss @@ -124,4 +124,8 @@ body { height: auto; } } +} + +:root { + --iss-loader-color: white; } \ No newline at end of file diff --git a/src/ux-components/button-main/button-main.vue b/src/ux-components/button-main/button-main.vue index fc04831a..69649e08 100644 --- a/src/ux-components/button-main/button-main.vue +++ b/src/ux-components/button-main/button-main.vue @@ -12,7 +12,7 @@ + :class="[loaderPosition]" /> @@ -28,7 +28,6 @@ export default { isPrimary: Boolean, buttonText: String, isDisabled: Boolean, - loaderColor: String, loaderPosition: String, isFloat: Boolean, suppressLoader: Boolean @@ -69,8 +68,8 @@ export default { @media (hover: hover) { background: linear-gradient(270deg, $blue 0%, $blue-800 100%); } - // Mouse, touch, stylus focus - &:focus-visible { + // Mouse, touch, stylus focus + &:focus-visible { // Keyboard focus for accessibility outline: none; box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700; @@ -112,7 +111,7 @@ export default { @include blue-gradient; } &:focus, // Mouse, touch, stylus focus - &:focus-visible { + &:focus-visible { // Keyboard focus for accessibility outline: none; box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue-700; diff --git a/src/ux-components/loader/loader.vue b/src/ux-components/loader/loader.vue index a84730c5..f8f82966 100644 --- a/src/ux-components/loader/loader.vue +++ b/src/ux-components/loader/loader.vue @@ -21,10 +21,6 @@ export default { type: Boolean, default: false }, - /* Color options: red, green, blue, white, black */ - loaderColor: { - type: String - }, /* Position options: center, right, left (OPTIONAL, do NOT use on btn-* classes) */ loaderPosition: { type: String @@ -112,20 +108,7 @@ export default { } //Spinner color &:after { - //Default spinner color (blue) if no other color is specified from the options below - background-color: $blue; - } - &.red:after { - background-color: $red; - } - &.green:after { - background-color: $green; - } - &.white:after { - background-color: $white; - } - &.black:after { - background-color: $black; + background-color: var(--iss-loader-color); } } From 96c29f38ccfdbd72dab96bdadd6b53a58bfc7d99 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Wed, 27 Nov 2024 11:15:39 -0600 Subject: [PATCH 71/99] SSR-1924 Fix unit tests for changes --- src/layouts/tpa-submit/tpa-submit.spec.js | 1 - .../button-main/button-main.spec.js | 23 ------------------- 2 files changed, 24 deletions(-) diff --git a/src/layouts/tpa-submit/tpa-submit.spec.js b/src/layouts/tpa-submit/tpa-submit.spec.js index 910fabb7..30934db3 100644 --- a/src/layouts/tpa-submit/tpa-submit.spec.js +++ b/src/layouts/tpa-submit/tpa-submit.spec.js @@ -184,7 +184,6 @@ describe('tpa-submit', () => { // Assert expect(mainButton.exists()).toBeTruthy(); expect(mainButton.props().isPrimary).toBe(true); - expect(mainButton.props().loaderColor).toBe('white'); expect(mainButton.classes()).toContain('w-100'); expect(mainButton.classes()).toContain('mb-5'); }); diff --git a/src/ux-components/button-main/button-main.spec.js b/src/ux-components/button-main/button-main.spec.js index 2caf56f1..0494405f 100644 --- a/src/ux-components/button-main/button-main.spec.js +++ b/src/ux-components/button-main/button-main.spec.js @@ -49,29 +49,6 @@ describe('buttonMain.vue', () => { expect(button.attributes()['aria-disabled']).toEqual('true'); }); - it('Should return loader color', async () => { - // Act - const wrapper = shallowMount( - buttonMain, - setupMocks({ - propsData: { - loaderColor: 'blue', - loaderEnabled: true - } - }) - ); - - // Assert - - wrapper.vm.clicked(); - - await nextTick(); - - const loader = wrapper.find('loader-stub'); - - expect(loader.attributes('class')).toContain('blue'); - }); - it('Should return loader position', async () => { // Act const wrapper = shallowMount( From 11b2a1ebd77610bb6329395b5c1f9e35e5c95360 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Mon, 2 Dec 2024 08:10:19 -0600 Subject: [PATCH 72/99] SSR-1934 Change Terms of use to Terms of service --- src/iss-components/site-header/menu-modal/menu-modal.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iss-components/site-header/menu-modal/menu-modal.vue b/src/iss-components/site-header/menu-modal/menu-modal.vue index cc506b49..0abfb855 100644 --- a/src/iss-components/site-header/menu-modal/menu-modal.vue +++ b/src/iss-components/site-header/menu-modal/menu-modal.vue @@ -44,7 +44,7 @@
0) { diff --git a/src/layouts/bailout-page/bailout-page.vue b/src/layouts/bailout-page/bailout-page.vue index 692837e1..a6921804 100644 --- a/src/layouts/bailout-page/bailout-page.vue +++ b/src/layouts/bailout-page/bailout-page.vue @@ -193,6 +193,9 @@ export default { } }, methods: { + arePagePrerequisitesValid() { + return !!this.mainStore.pageData(issPageValues.BAILOUT_PAGE); + }, backButtonAction() { // route to move backwards this.mainStore.resetBailout(); diff --git a/src/layouts/policy-holder-details/policy-holder-details.vue b/src/layouts/policy-holder-details/policy-holder-details.vue index 2758f64f..165fb775 100644 --- a/src/layouts/policy-holder-details/policy-holder-details.vue +++ b/src/layouts/policy-holder-details/policy-holder-details.vue @@ -19,7 +19,7 @@ id="address-questions-wrapper" ref="addressQuestions" v-model="customerQuestions.addressQuestions" - includeStreetAddress2="true" /> + :includeStreetAddress2="true" /> 0 && this.hasValidCarId(), - hasBailedOut: false }; }, computed: { @@ -231,13 +230,12 @@ export default { this.needToLookupVehicle = false; - return null; + return; } let isSelectedGlassAvailableForVehicle = true; if (this.isCarIdDifferentFromTheStore) { - isSelectedGlassAvailableForVehicle = - await isGlassAvailableForCarId(this.vehicleFromLookup.carId); + isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(this.vehicleFromLookup.carId); } // navigate back to vehicle-damage @@ -255,7 +253,7 @@ export default { ); // navigate() doesn't stop the processing flow - return null; + return; } // save vehicle to store if it hasn't already been saved @@ -268,28 +266,23 @@ export default { this.navigationScenarios.CORRECTED_VIN_FROM_POLICY_VEHICLE, this.$route ); - return null; + return; } const partsOrQuestionsResponse = await this.getPartsOrQuestions(); if (partsOrQuestionsResponse.error) { this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); window.console.error('Error on retrieving PartsOrQuestions'); - this.$refs.siteFooter.removeLoader(); - this.hasBailedOut = true; + // this.$refs.siteFooter.removeLoader(); this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, this.$route ); + return; } // Comes from vehicleQuestionsMixin.navigateForward() - if (!this.hasBailedOut) { - await this.navigateForward( - partsOrQuestionsResponse.data.partsOrQuestions, - this - ); - } + await this.navigateForward(partsOrQuestionsResponse.data.partsOrQuestions, this); }, async lookupVehicleByVin(vin) { try { From d47b86e0cdbec9baac1e87c9022d75884aab504f Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Fri, 6 Dec 2024 10:42:34 -0600 Subject: [PATCH 82/99] SSR-1942 Removed commented removeLoader() call --- src/layouts/vin-lookup/vin-lookup.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index 2f8f4d0f..4cc724f5 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -273,7 +273,6 @@ export default { if (partsOrQuestionsResponse.error) { this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); window.console.error('Error on retrieving PartsOrQuestions'); - // this.$refs.siteFooter.removeLoader(); this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, this.$route From 62b7f318c2b092e412c9e7acfe9af244cd7ebd39 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Fri, 6 Dec 2024 11:39:55 -0600 Subject: [PATCH 83/99] SSR-1942 use fillInAddressUsingFirstItem instead for address-questions focus out --- src/iss-components/address-questions/address-questions.vue | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/iss-components/address-questions/address-questions.vue b/src/iss-components/address-questions/address-questions.vue index 8ab07362..55abd09c 100644 --- a/src/iss-components/address-questions/address-questions.vue +++ b/src/iss-components/address-questions/address-questions.vue @@ -13,7 +13,7 @@ disableAutoFill validationRules="street-address-required" @keydown.enter.prevent - @focusout="onAutocompleteFocusOut()" /> + @focusout="fillInAddressUsingFirstItem()" /> 0) { From 068f97d16218cf4b2637626f5156f059ba11f712 Mon Sep 17 00:00:00 2001 From: Jeremy Zimmerman Date: Mon, 9 Dec 2024 09:41:18 -0500 Subject: [PATCH 84/99] Reverting change from SSR-1942 to due blocking issues. --- .../address-questions/address-questions.vue | 2 +- src/layouts/bailout-page/bailout-page.vue | 3 --- .../policy-holder-details.vue | 2 +- src/layouts/vin-lookup/vin-lookup.vue | 20 +++++++++++++------ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/iss-components/address-questions/address-questions.vue b/src/iss-components/address-questions/address-questions.vue index 55abd09c..57b9d0dc 100644 --- a/src/iss-components/address-questions/address-questions.vue +++ b/src/iss-components/address-questions/address-questions.vue @@ -13,7 +13,7 @@ disableAutoFill validationRules="street-address-required" @keydown.enter.prevent - @focusout="fillInAddressUsingFirstItem()" /> + @focusout="fillInAddress()" /> + includeStreetAddress2="true" /> 0 && this.hasValidCarId(), + hasBailedOut: false }; }, computed: { @@ -230,12 +231,13 @@ export default { this.needToLookupVehicle = false; - return; + return null; } let isSelectedGlassAvailableForVehicle = true; if (this.isCarIdDifferentFromTheStore) { - isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(this.vehicleFromLookup.carId); + isSelectedGlassAvailableForVehicle = + await isGlassAvailableForCarId(this.vehicleFromLookup.carId); } // navigate back to vehicle-damage @@ -253,7 +255,7 @@ export default { ); // navigate() doesn't stop the processing flow - return; + return null; } // save vehicle to store if it hasn't already been saved @@ -266,22 +268,28 @@ export default { this.navigationScenarios.CORRECTED_VIN_FROM_POLICY_VEHICLE, this.$route ); - return; + return null; } const partsOrQuestionsResponse = await this.getPartsOrQuestions(); if (partsOrQuestionsResponse.error) { this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); window.console.error('Error on retrieving PartsOrQuestions'); + this.$refs.siteFooter.removeLoader(); + this.hasBailedOut = true; this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, this.$route ); - return; } // Comes from vehicleQuestionsMixin.navigateForward() - await this.navigateForward(partsOrQuestionsResponse.data.partsOrQuestions, this); + if (!this.hasBailedOut) { + await this.navigateForward( + partsOrQuestionsResponse.data.partsOrQuestions, + this + ); + } }, async lookupVehicleByVin(vin) { try { From 4bb75b8d91c784496dffb36a78828494f4d62fd7 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Thu, 12 Dec 2024 10:28:14 -0600 Subject: [PATCH 85/99] Fix some issues caused by previous loader color changes --- .../button-question/button-question.vue | 8 -------- src/digital-components/modal/modal.vue | 1 - src/layouts/tpa-submit/tpa-submit.vue | 1 - src/ux-components/loader/loader.vue | 19 +++++++++++++++++++ 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/digital-components/button-question/button-question.vue b/src/digital-components/button-question/button-question.vue index 70bfdea4..2a2e46fa 100644 --- a/src/digital-components/button-question/button-question.vue +++ b/src/digital-components/button-question/button-question.vue @@ -123,14 +123,6 @@ export default { default: 'text-center' }, selectingInitiatesLoad: Boolean, - loaderColor: { - type: String, - default: 'blue' - }, - loaderPosition: { - type: String, - default: 'right' - }, isRequired: Boolean, isOverflowScrollable: Boolean, isWide: Boolean, diff --git a/src/digital-components/modal/modal.vue b/src/digital-components/modal/modal.vue index a85118fb..8a4664e7 100644 --- a/src/digital-components/modal/modal.vue +++ b/src/digital-components/modal/modal.vue @@ -40,7 +40,6 @@ ref="modalButtonMain" isPrimary class="w-100" - loaderColor="white" :buttonText="footerButtonText" :class="[ (isButtonDisabled || isFooterButtonDisabled) && diff --git a/src/layouts/tpa-submit/tpa-submit.vue b/src/layouts/tpa-submit/tpa-submit.vue index 08f43bec..eded8794 100644 --- a/src/layouts/tpa-submit/tpa-submit.vue +++ b/src/layouts/tpa-submit/tpa-submit.vue @@ -41,7 +41,6 @@ ref="buttonMainOne" isPrimary :buttonText="forwardButtonText" - loaderColor="white" class="w-100 mb-5" @clickEvent="forwardButtonAction" />
diff --git a/src/ux-components/loader/loader.vue b/src/ux-components/loader/loader.vue index f8f82966..6db8dbc5 100644 --- a/src/ux-components/loader/loader.vue +++ b/src/ux-components/loader/loader.vue @@ -21,6 +21,10 @@ export default { type: Boolean, default: false }, + /* Color options: red, green, blue, white, black */ + loaderColor: { + type: String + }, /* Position options: center, right, left (OPTIONAL, do NOT use on btn-* classes) */ loaderPosition: { type: String @@ -110,5 +114,20 @@ export default { &:after { background-color: var(--iss-loader-color); } + &.red:after { + background-color: $red; + } + &.green:after { + background-color: $green; + } + &.blue:after { + background-color: $blue; + } + &.white:after { + background-color: $white; + } + &.black:after { + background-color: $black; + } } From 3a5acb0293d23b5ce9709985cae14f1088d2c99a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:42:06 +0000 Subject: [PATCH 86/99] Bump nanoid from 3.3.7 to 3.3.8 Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8. - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8) --- updated-dependencies: - dependency-name: nanoid dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71cd46a3..db831177 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14334,9 +14334,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", From 86eb076013689804ab975e97ac6ec42359e35cb3 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Thu, 12 Dec 2024 12:55:47 -0500 Subject: [PATCH 87/99] No longer removing loading --- src/layouts/vin-lookup/vin-lookup.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index f469953d..9db38f8c 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -275,7 +275,6 @@ export default { if (partsOrQuestionsResponse.error) { this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); window.console.error('Error on retrieving PartsOrQuestions'); - this.$refs.siteFooter.removeLoader(); this.hasBailedOut = true; this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, From d9373df9b4b6eb226ededd4cc5fa74e5e453d040 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Thu, 12 Dec 2024 12:49:26 -0500 Subject: [PATCH 88/99] Add Feature Toggle payload object to combined quote call Fixed constant for Recycle Fee in one spot. --- src/helpers/experiment-helper.js | 12 +++++++----- src/store/index.js | 7 ++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/helpers/experiment-helper.js b/src/helpers/experiment-helper.js index 861b2ad2..e0f8823b 100644 --- a/src/helpers/experiment-helper.js +++ b/src/helpers/experiment-helper.js @@ -11,14 +11,16 @@ export function getExperimentSettingValue(storeExperimentSettings, settingName) : null; } -export function getFeatureTogglesQueryString(storeExperimentSettings) { +export function getFeatureTogglesPayloadObject(storeExperimentSettings) { const isMobileFeeHidden = getExperimentSettingValue(storeExperimentSettings, experimentSettings.ISS_FEATURE_TOGGLE_IS_MOBILE_FEE_HIDDEN) === 'true'; const isMobileFeeOverridden = getExperimentSettingValue(storeExperimentSettings, experimentSettings.ISS_FEATURE_TOGGLE_IS_MOBILE_FEE_OVERRIDDEN) === 'true'; const isRecycleFeeHidden = getExperimentSettingValue(storeExperimentSettings, experimentSettings.ISS_FEATURE_TOGGLE_IS_RECYCLE_FEE_HIDDEN) === 'true'; const isRecycleFeeOverridden = getExperimentSettingValue(storeExperimentSettings, experimentSettings.ISS_FEATURE_TOGGLE_IS_RECYCLE_FEE_OVERRIDDEN) === 'true'; - return `FeatureToggles.IsMobileFeeHidden=${isMobileFeeHidden}` - + `&FeatureToggles.IsMobileFeeOverridden=${isMobileFeeOverridden}` - + `&FeatureToggles.IsRecycleFeeHidden=${isRecycleFeeHidden}` - + `&FeatureToggles.IsRecycleFeeOverridden=${isRecycleFeeOverridden}`; + return { + isMobileFeeHidden, + isMobileFeeOverridden, + isRecycleFeeHidden, + isRecycleFeeOverridden + }; } diff --git a/src/store/index.js b/src/store/index.js index 3161024a..f5bca90f 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -19,7 +19,7 @@ import globalMethods from '@/global-methods'; // eslint-disable-next-line import/no-cycle import { getSessionKeyValue, getUserIdValue, deleteISSCookie } from '@/helpers/cookie-helper'; import { convertDateStringToDate, getDateDifferenceInDays, militaryToTwelveHourTime } from '@/helpers/date-helper'; -import { getExperimentSettingValue } from '@/helpers/experiment-helper'; +import { getExperimentSettingValue, getFeatureTogglesPayloadObject } from '@/helpers/experiment-helper'; import { findLineItemIndex, getLineItemsFlattened } from '@/helpers/line-items-helper'; import { deductibleForSelectedVehicle, endorsementsForSelectedVehicle, @@ -1255,7 +1255,8 @@ export const useMainStore = defineStore({ ServiceZipCode: serviceLocation.zipCode, IsReplacement: !this.isRepair, ServerData: lineItems.serverData, - LineItems: getLineItemsFlattened(availableLineItems) + LineItems: getLineItemsFlattened(availableLineItems), + FeatureToggles: getFeatureTogglesPayloadObject(this.experimentSettings) }; const response = await globalMethods @@ -1880,7 +1881,7 @@ export const useMainStore = defineStore({ feeData.forEach((fee) => { if (fee.partNumber === partTypeStrings.MOBILE_FEE) { this.updateMobileFee(fee); - } else if (fee.partNumber === partTypeStrings.RECYCLE_FEE) { + } else if (fee.partNumber === partNumberStrings.RECYCLE_FEE) { this.updateRecycleFee(fee); } else { this.addPartNumberFeeItem(fee, fee.partNumber); From 6eada2ed48ff21bab03f8c14019ede9e49f4f7c7 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Thu, 12 Dec 2024 14:48:28 -0500 Subject: [PATCH 89/99] Use v-show instead of v-if since those fields are required. Fields are hidden to start and are shown once a google address is chosen or tab/enter is used to select first. --- src/iss-components/address-questions/address-questions.vue | 5 ++--- src/layouts/policy-holder-details/policy-holder-details.vue | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/iss-components/address-questions/address-questions.vue b/src/iss-components/address-questions/address-questions.vue index 57b9d0dc..f95924f2 100644 --- a/src/iss-components/address-questions/address-questions.vue +++ b/src/iss-components/address-questions/address-questions.vue @@ -12,14 +12,13 @@ hasIcon disableAutoFill validationRules="street-address-required" - @keydown.enter.prevent - @focusout="fillInAddress()" /> + @keydown.enter.prevent /> -
+
diff --git a/src/layouts/policy-holder-details/policy-holder-details.vue b/src/layouts/policy-holder-details/policy-holder-details.vue index 2758f64f..165fb775 100644 --- a/src/layouts/policy-holder-details/policy-holder-details.vue +++ b/src/layouts/policy-holder-details/policy-holder-details.vue @@ -19,7 +19,7 @@ id="address-questions-wrapper" ref="addressQuestions" v-model="customerQuestions.addressQuestions" - includeStreetAddress2="true" /> + :includeStreetAddress2="true" /> Date: Thu, 12 Dec 2024 14:53:16 -0500 Subject: [PATCH 90/99] update test --- .../address-questions/address-questions.spec.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/iss-components/address-questions/address-questions.spec.js b/src/iss-components/address-questions/address-questions.spec.js index cf7003fa..15a1fa77 100644 --- a/src/iss-components/address-questions/address-questions.spec.js +++ b/src/iss-components/address-questions/address-questions.spec.js @@ -73,7 +73,7 @@ describe('address-questions.vue', () => { }); describe('initial state', () => { - test('Should hide addressQuestions sub-components (textbox-questions and dropdown-questions)', async () => { + test('Should hide(exists but not visible) addressQuestions sub-components (textbox-questions and dropdown-questions)', async () => { // Arrange const { wrapper } = setupMocks({}); @@ -86,9 +86,12 @@ describe('address-questions.vue', () => { // Assert expect(streetAddress.exists()).toBe(true); - expect(city.exists()).toBe(false); - expect(state.exists()).toBe(false); - expect(zipCode.exists()).toBe(false); + expect(city.exists()).toBe(true); + expect(city.isVisible(false)); + expect(state.exists()).toBe(true); + expect(state.isVisible(false)); + expect(zipCode.exists()).toBe(true); + expect(zipCode.isVisible(false)); expect(streetAddress2.exists()).toBe(false); }); From 91e2726aa3ce19f1f0f5cae51dcf7eda0ac2ba49 Mon Sep 17 00:00:00 2001 From: Alex Humphries Date: Thu, 19 Dec 2024 10:58:29 -0500 Subject: [PATCH 91/99] New bailout for errors in vehicle lookup on vehicle-selection page --- src/constants/bailoutCode.js | 5 +++-- src/constants/bailoutMessage.js | 10 +++++++--- src/layouts/vehicle-selection/vehicle-selection.vue | 8 ++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/constants/bailoutCode.js b/src/constants/bailoutCode.js index bd0a4b49..fb1ac071 100644 --- a/src/constants/bailoutCode.js +++ b/src/constants/bailoutCode.js @@ -2,7 +2,7 @@ const bailoutCode = Object.freeze({ Unknown: 0, SaveSessionError: 1, VehicleNotFound: 2, - VehicleLookupError: 3, + VehicleVinLookupError: 3, CoverageStatementInvalidState: 4, DoNotSeeMyShop: 5, PricingResponseError: 6, @@ -11,7 +11,8 @@ const bailoutCode = Object.freeze({ HeavyTruckVehicle: 9, NoPartsAvailable: 10, PartsServiceError: 11, - SafeliteNotTheProvider: 12 + SafeliteNotTheProvider: 12, + VehicleYMMSLookupError: 13 }); export default bailoutCode; diff --git a/src/constants/bailoutMessage.js b/src/constants/bailoutMessage.js index e4fa6b84..e26ec247 100644 --- a/src/constants/bailoutMessage.js +++ b/src/constants/bailoutMessage.js @@ -25,8 +25,8 @@ const bailoutMessage = Object.freeze({ code: bailoutCode.VehicleNotFound, message: `Failed to find vehicle in system with vin: ${vin}` }), - vehicleLookupError: (vin, error) => ({ - code: bailoutCode.VehicleLookupError, + vehicleVinLookupError: (vin, error) => ({ + code: bailoutCode.VehicleVinLookupError, message: `An error occurred looking up Vin: ${vin}. Error: ${getItemData(error)}` }), coverageStatementInvalidState: () => ({ @@ -64,7 +64,11 @@ const bailoutMessage = Object.freeze({ SafeliteNotTheProvider: () => ({ code: bailoutCode.SafeliteNotTheProvider, message: 'User selected to continue a referral where a TPA shop was previously selected.' - }) + }), + vehicleYMMSLookupError: (year, make, model, style, error) => ({ + code: bailoutCode.VehicleYMMSLookupError, + message: `An error occurred looking up Year: ${year}, Make: ${make}, Model: ${model}, Style: ${style}. Error: ${getItemData(error)}` + }), }); export default bailoutMessage; diff --git a/src/layouts/vehicle-selection/vehicle-selection.vue b/src/layouts/vehicle-selection/vehicle-selection.vue index bbb4607e..4f4f5ffa 100644 --- a/src/layouts/vehicle-selection/vehicle-selection.vue +++ b/src/layouts/vehicle-selection/vehicle-selection.vue @@ -88,6 +88,7 @@ import settleAllPromises from '@/helpers/layout-helper'; import { Form, defineRule } from 'vee-validate'; import { required } from '@/helpers/validation-rules'; import errorMessages from '@/constants/error-messages'; +import bailoutMessage from '@/constants/bailoutMessage'; // define validation rules defineRule('year-required', required(errorMessages.YEAR_REQUIRED)); @@ -207,6 +208,13 @@ export default { this.navigationScenarios.CLICKED_FORWARD, this.$route ); + }, + (error) => { + this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, this.mainStore.vehicle.style, error)); + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + this.$route + ); }); }, async updateYearValues() { From 4077b2dfefa4ad056c5f5f439982115c887ba2d9 Mon Sep 17 00:00:00 2001 From: Alex Humphries Date: Thu, 19 Dec 2024 11:25:45 -0500 Subject: [PATCH 92/99] Update bailout on policy-vehicles to properly use tweaked message --- src/layouts/policy-vehicles/policy-vehicles.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index cc35fd73..65cc7ea6 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -200,7 +200,7 @@ export default { return this.navigateForward(); } - this.mainStore.setBailout(bailoutMessage.vehicleLookupError( + this.mainStore.setBailout(bailoutMessage.vehicleVinLookupError( vehicle.vin, vehicleLookupResponse.data )); From 5655494e4039021fac46eb436f62faf8699a2076 Mon Sep 17 00:00:00 2001 From: Alex Humphries Date: Thu, 19 Dec 2024 11:38:33 -0500 Subject: [PATCH 93/99] Update test to use tweaked bailout code/message --- src/layouts/policy-vehicles/policy-vehicles.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layouts/policy-vehicles/policy-vehicles.spec.js b/src/layouts/policy-vehicles/policy-vehicles.spec.js index 8b727434..e9df4583 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.spec.js +++ b/src/layouts/policy-vehicles/policy-vehicles.spec.js @@ -279,14 +279,14 @@ describe('policy-vehicles.vue', () => { // Act wrapper.vm.mainStore.applicationUser.pageData[issPageValues.BAILOUT_PAGE] = { 'bailout-page': { - bailoutCode: bailoutCode.VehicleLookupError + bailoutCode: bailoutCode.VehicleVinLookupError } }; await wrapper.vm.forwardButtonAction(); // Assert // eslint-disable-next-line max-len - expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(bailoutMessage.vehicleLookupError(vin, lookupReturnValue.data)); + expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(bailoutMessage.vehicleVinLookupError(vin, lookupReturnValue.data)); expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, undefined, From 009f219415df5e9075ed654ed93ac874ae497417 Mon Sep 17 00:00:00 2001 From: Alex Humphries Date: Fri, 20 Dec 2024 13:58:31 -0500 Subject: [PATCH 94/99] More robust handling of vehicle YMMS lookup errors --- src/constants/bailoutMessage.js | 26 +++++++++-- .../vehicle-selection/vehicle-selection.vue | 44 +++++++++++++++++-- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/constants/bailoutMessage.js b/src/constants/bailoutMessage.js index e26ec247..47ce0657 100644 --- a/src/constants/bailoutMessage.js +++ b/src/constants/bailoutMessage.js @@ -65,10 +65,28 @@ const bailoutMessage = Object.freeze({ code: bailoutCode.SafeliteNotTheProvider, message: 'User selected to continue a referral where a TPA shop was previously selected.' }), - vehicleYMMSLookupError: (year, make, model, style, error) => ({ - code: bailoutCode.VehicleYMMSLookupError, - message: `An error occurred looking up Year: ${year}, Make: ${make}, Model: ${model}, Style: ${style}. Error: ${getItemData(error)}` - }), + vehicleYMMSLookupError: (year, make, model, style, error) => { + let message; + if(!year) { + message = `An error occured looking up vehicle years. Error: ${getItemData(error)}`; + } + else if (!make) { + message = `An error occured looking up vehicle makes for Year: ${year}. Error: ${getItemData(error)}`; + } + else if (!model) { + message = `An error occured looking up vehicle models for Year: ${year}, Make: ${make}. Error: ${getItemData(error)}`; + } + else if (!style) { + message = `An error occured looking up vehicle styles for Year: ${year}, Make: ${make}, Model: ${model}. Error: ${getItemData(error)}`; + } + else { + message = `An error occurred looking up a vehicle with Year: ${year}, Make: ${make}, Model: ${model}, Style: ${style}. Error: ${getItemData(error)}` + } + return { + code: bailoutCode.VehicleYMMSLookupError, + message: message + } + }, }); export default bailoutMessage; diff --git a/src/layouts/vehicle-selection/vehicle-selection.vue b/src/layouts/vehicle-selection/vehicle-selection.vue index 4f4f5ffa..59b96b38 100644 --- a/src/layouts/vehicle-selection/vehicle-selection.vue +++ b/src/layouts/vehicle-selection/vehicle-selection.vue @@ -218,16 +218,52 @@ export default { }); }, async updateYearValues() { - return useMainStore().getVehicleYears(); + return this.mainStore.getVehicleYears().then((response) => { + return response; + }, + (error) => { + this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(null, null, null, null, error)); + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + this.$route + ); + }); }, async updateMakeValues() { - return this.mainStore.getVehicleMakes(); + return this.mainStore.getVehicleMakes().then((response) => { + return response; + }, + (error) => { + this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, null, null, null, error)); + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + this.$route + ); + }); }, async updateModelValues() { - return this.mainStore.getVehicleModels(); + return this.mainStore.getVehicleModels().then((response) => { + return response; + }, + (error) => { + this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, null, null, error)); + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + this.$route + ); + }); }, async updateStyleValues() { - return this.mainStore.getVehicleStyles(); + return this.mainStore.getVehicleStyles().then((response) => { + return response; + }, + (error) => { + this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, null, error)); + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + this.$route + ); + }); } } }; From e4a8413e4c368cd956c27e6951ce73275d40a206 Mon Sep 17 00:00:00 2001 From: Alex Humphries Date: Mon, 23 Dec 2024 13:46:44 -0500 Subject: [PATCH 95/99] Refactored new bailout to reduce redundancy --- src/constants/bailoutMessage.js | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/constants/bailoutMessage.js b/src/constants/bailoutMessage.js index 47ce0657..4215d868 100644 --- a/src/constants/bailoutMessage.js +++ b/src/constants/bailoutMessage.js @@ -66,26 +66,21 @@ const bailoutMessage = Object.freeze({ message: 'User selected to continue a referral where a TPA shop was previously selected.' }), vehicleYMMSLookupError: (year, make, model, style, error) => { - let message; - if(!year) { - message = `An error occured looking up vehicle years. Error: ${getItemData(error)}`; - } - else if (!make) { - message = `An error occured looking up vehicle makes for Year: ${year}. Error: ${getItemData(error)}`; - } - else if (!model) { - message = `An error occured looking up vehicle models for Year: ${year}, Make: ${make}. Error: ${getItemData(error)}`; - } - else if (!style) { - message = `An error occured looking up vehicle styles for Year: ${year}, Make: ${make}, Model: ${model}. Error: ${getItemData(error)}`; - } - else { - message = `An error occurred looking up a vehicle with Year: ${year}, Make: ${make}, Model: ${model}, Style: ${style}. Error: ${getItemData(error)}` - } + const baseMessage = "An error occurred looking up vehicle"; + const errorMessage = `Error: ${getItemData(error)}`; + + const getMessage = (year, make, model, style) => { + if (!year) return `${baseMessage} years. ${errorMessage}`; + if (!make) return `${baseMessage} makes for Year: ${year}. ${errorMessage}`; + if (!model) return `${baseMessage} models for Year: ${year}, Make: ${make}. ${errorMessage}`; + if (!style) return `${baseMessage} styles for Year: ${year}, Make: ${make}, Model: ${model}. ${errorMessage}`; + return `${baseMessage} with Year: ${year}, Make: ${make}, Model: ${model}, Style: ${style}. ${errorMessage}`; + }; + return { code: bailoutCode.VehicleYMMSLookupError, - message: message - } + message: getMessage(year, make, model, style) + }; }, }); From d001299e2a92ddc1329acfe3563b4b1e4163065c Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Thu, 9 Jan 2025 12:33:32 -0500 Subject: [PATCH 96/99] Removing unused call --- src/layouts/tpa-confirmation/tpa-confirmation.vue | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/layouts/tpa-confirmation/tpa-confirmation.vue b/src/layouts/tpa-confirmation/tpa-confirmation.vue index 6c52383c..62646ce4 100644 --- a/src/layouts/tpa-confirmation/tpa-confirmation.vue +++ b/src/layouts/tpa-confirmation/tpa-confirmation.vue @@ -88,7 +88,6 @@ import { toDisplayPhoneNumber, formatAmountInDollars } from '@/helpers/text-helper.js'; -import bailoutMessage from '@/constants/bailoutMessage'; const VERIFYING_COVERAGE = 'Verifying coverage'; @@ -107,16 +106,11 @@ export default { async beforeRouteEnter(to, from, next) { // Call APIs const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); - const accountInfoPromise = useMainStore().getCarrierAccountInfo(); // Settle promises and get results const promiseResultMap = [ { resultKey: 'cmsContent', promise: cmsContentPromise - }, - { - resultKey: 'accountInfo', - promise: accountInfoPromise } ]; // use resultMap to populate layout content. From aba0ceb730a0e11fd5a17f1ee101b46cf9a242e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 06:32:33 +0000 Subject: [PATCH 97/99] Bump vite from 4.5.3 to 4.5.9 Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.5.3 to 4.5.9. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v4.5.9/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v4.5.9/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index db831177..f012fae5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "jsdom": "^22.1.0", "sass": "^1.77.8", "sass-loader": "^12.0.0", - "vite": "^4.4.6", + "vite": "^4.5.9", "vitest": "^0.33.0", "volar-service-vetur": "*" } @@ -17918,10 +17918,11 @@ } }, "node_modules/vite": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", - "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.9.tgz", + "integrity": "sha512-qK9W4xjgD3gXbC0NmdNFFnVFLMWSNiR3swj957yutwzzN16xF/E7nmtAyp1rT9hviDroQANjE4HK3H4WqWdFtw==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", diff --git a/package.json b/package.json index 7d588bbc..deddbded 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "jsdom": "^22.1.0", "sass": "^1.77.8", "sass-loader": "^12.0.0", - "vite": "^4.4.6", + "vite": "^4.5.9", "vitest": "^0.33.0", "volar-service-vetur": "latest" } From 35312ceb4b2a210837cfd4c504cf27e7fb06ccbe Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 29 Jan 2025 16:21:32 -0500 Subject: [PATCH 98/99] updates to mimic fmg for experiment api updates --- src/router/index.js | 4 ++-- src/store/index.js | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/router/index.js b/src/router/index.js index ba20bf96..c0cb576b 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -422,14 +422,14 @@ async function runExperiments(nextPage) { if (!store.applicationUser.triggeredSiteEntry) { await store.runExperimentsForTrigger({ - userId: getDeviceIdValue(), + deviceId: getDeviceIdValue(), triggerEvent: experimentTriggers.SITE_ENTRY, triggerValue: applicationConfig.SITE_ENTRY_TRIGGER_VALUE }); } await store.runExperimentsForTrigger({ - userId: getDeviceIdValue(), + deviceId: getDeviceIdValue(), triggerEvent: experimentTriggers.PAGE_ENTRY, triggerValue: nextPage }); diff --git a/src/store/index.js b/src/store/index.js index f5bca90f..84f40697 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -17,7 +17,7 @@ import webStorageConstants from '@/constants/web-storage-constants'; // eslint-disable-next-line import/no-cycle import globalMethods from '@/global-methods'; // eslint-disable-next-line import/no-cycle -import { getSessionKeyValue, getUserIdValue, deleteISSCookie } from '@/helpers/cookie-helper'; +import { getSessionKeyValue, getUserIdValue, deleteISSCookie, getDeviceIdValue } from '@/helpers/cookie-helper'; import { convertDateStringToDate, getDateDifferenceInDays, militaryToTwelveHourTime } from '@/helpers/date-helper'; import { getExperimentSettingValue, getFeatureTogglesPayloadObject } from '@/helpers/experiment-helper'; import { findLineItemIndex, getLineItemsFlattened } from '@/helpers/line-items-helper'; @@ -2453,6 +2453,7 @@ export const useMainStore = defineStore({ payload: { experimentForLogging: { userId: getUserIdValue(), + deviceId: getDeviceIdValue(), experimentUniverseId: experiment.universeId, experimentUniverseName: experiment.universeName, experimentTestId: experiment.testId, @@ -2592,14 +2593,14 @@ export const useMainStore = defineStore({ }); }, - async runExperimentsForTrigger({ userId, triggerEvent, triggerValue }) { + async runExperimentsForTrigger({ deviceId, triggerEvent, triggerValue }) { if (triggerEvent === experimentTriggers.SITE_ENTRY) { this.updateTriggeredSiteEntry(true); } const payload = { applicationName: applicationConfig.APPLICATION_NAME, - userId, + deviceId, triggerEvent, triggerValue, experimentOrder: this.experimentOrder From 5d3ab0ef592d06043454f110e2af583a75691095 Mon Sep 17 00:00:00 2001 From: chase-safelite Date: Fri, 14 Feb 2025 09:54:11 -0500 Subject: [PATCH 99/99] Added playwright tests into repo (#923) * Initial import of playwright tests * Pipeline changes for automated tests * Modified pipeline for testing * Attempt #2 * Attempt #3 * Added missing paren * Removed debug stuff from pipeline * Changes from playwright repo * Changed pipeline for debugging * Fix for ServiceLocationPage playwright locators * Change pipeline to run with TEST APIs * Moved more of Siraj's changes to this repo * Changed where updating env occurs * Changed location of env update again * Escaped double quotes * Added visible report in Azure * Moved changes into main pipeline * Made it so dotenv only runs config in local --- .gitignore | 7 + Dockerfile.playwright | 21 + azure-pipelines.yml | 125 +- package-lock.json | 2317 ++++++++++++++++- package.json | 18 +- playwright-tests/.dockerignore | 4 + playwright-tests/.env | 14 + playwright-tests/.env.dev | 14 + playwright-tests/.env.example | 10 + playwright-tests/.gitignore | 6 + playwright-tests/.sauce/config.yml | 74 + playwright-tests/.sauceignore | 16 + .../business-logic/data/ClientData.ts | 273 ++ .../business-logic/data/MockPolicyData.ts | 71 + .../rules/RuleEngineBuiltins.ts | 70 + .../business-logic/types/Authentication.ts | 40 + .../business-logic/types/CcisApi.ts | 12 + .../business-logic/types/Client.ts | 10 + .../business-logic/types/CustomerDetails.ts | 81 + .../business-logic/types/DigitalApi.ts | 38 + .../business-logic/types/Enums.ts | 169 ++ .../business-logic/types/FrameworkConfig.ts | 11 + .../business-logic/types/IAddress.ts | 7 + .../business-logic/types/IBailoutFlags.ts | 11 + .../business-logic/types/IDisposable.ts | 29 + .../business-logic/types/ITestCase.ts | 16 + .../business-logic/types/ITestData.ts | 38 + .../business-logic/types/ITestPages.ts | 61 + .../types/IValidationExpectation.ts | 11 + .../business-logic/types/IValidations.ts | 13 + .../business-logic/types/RuleEngine.ts | 241 ++ playwright-tests/business-logic/types/Test.ts | 44 + .../business-logic/types/TestCase.ts | 227 ++ .../business-logic/types/Validations.ts | 29 + .../business-logic/validations/Soft.ts | 179 ++ playwright-tests/eslint.config.js | 4 + .../impl/api/AdminServiceApiUtil.ts | 21 + .../impl/api/ApiResponseInterceptUtil.ts | 47 + playwright-tests/impl/api/CcisApiUtil.ts | 28 + playwright-tests/impl/utils/DateUtils.ts | 26 + playwright-tests/impl/utils/EnumUtils.ts | 76 + playwright-tests/impl/utils/FakerUtils.ts | 56 + playwright-tests/impl/utils/FileUtils.ts | 22 + playwright-tests/impl/utils/HttpUtils.ts | 92 + playwright-tests/impl/utils/LoggingUtils.ts | 128 + playwright-tests/impl/utils/ParsingUtils.ts | 34 + playwright-tests/impl/utils/PropertyUtils.ts | 111 + playwright-tests/impl/utils/TaggingUtils.ts | 32 + playwright-tests/impl/utils/ThrowUtils.ts | 12 + playwright-tests/impl/utils/TimingUtils.ts | 273 ++ playwright-tests/impl/utils/TokenUtils.ts | 34 + playwright-tests/impl/utils/TryUtils.ts | 132 + playwright-tests/pages/AfterpayPage.ts | 56 + playwright-tests/pages/BailoutPage.ts | 49 + playwright-tests/pages/BasePage.ts | 41 + .../pages/CapabilityQuestionsPage.ts | 10 + .../pages/ContactConfirmationPage.ts | 17 + playwright-tests/pages/ContactDetailsPage.ts | 45 + .../pages/CoverageStatementPage.ts | 39 + playwright-tests/pages/DuplicateCheckPage.ts | 20 + playwright-tests/pages/EndorsementsPage.ts | 54 + .../pages/OrderConfirmationPage.ts | 115 + playwright-tests/pages/PartQuestionsPage.ts | 35 + playwright-tests/pages/PaymentMethodPage.ts | 82 + playwright-tests/pages/PaymentPage.ts | 45 + playwright-tests/pages/PaypalPage.ts | 27 + .../pages/PolicyHolderDetailsPage.ts | 20 + playwright-tests/pages/PolicyVehiclesPage.ts | 27 + .../pages/ProviderPreferencePage.ts | 50 + playwright-tests/pages/SchedulePage.ts | 58 + playwright-tests/pages/ServiceLocationPage.ts | 135 + playwright-tests/pages/ServicePackagesPage.ts | 26 + playwright-tests/pages/TpaConfirmationPage.ts | 19 + playwright-tests/pages/TpaSearchPage.ts | 24 + playwright-tests/pages/TpaSubmitPage.ts | 18 + playwright-tests/pages/VehicleDamagePage.ts | 150 ++ .../pages/VehicleLookupAddressPage.ts | 25 + .../pages/VehicleLookupLicensePage.ts | 22 + playwright-tests/pages/VehicleLookupPage.ts | 61 + playwright-tests/pages/VehiclePartsPage.ts | 10 + .../pages/VehicleSelectionPage.ts | 35 + playwright-tests/pages/VinLookupPage.ts | 26 + playwright-tests/pages/WelcomePage.ts | 105 + playwright-tests/pages/forms/AddressForm.ts | 58 + .../pages/forms/VehicleSelectionForm.ts | 16 + playwright-tests/playwright.config.ts | 112 + playwright-tests/tests/0000__M.test.ts | 628 +++++ .../tests/0001_EssentialReplaceStatisAdas.ts | 70 + .../tests/0002_EssentialRepairInShopAcura.ts | 70 + .../0003_EssentialTpaNotEnabledBailout.ts | 81 + .../tests/0004_EssentialTpaEnabled.ts | 83 + .../tests/0005_EssentialReplaceDynamicAdas.ts | 78 + .../0007_EssentialRepairHyundaiMobile.ts | 77 + .../0008_EssentialHeavyVehicleBailout.ts | 81 + .../0009_EssentialPartsServiceErrorBailout.ts | 83 + .../0010_EssentialVehicleNotFoundBailout.ts | 83 + .../0011_EssentialDoNotSeeShopBailout.ts | 81 + .../tests/0012_EssentialUniqueGlass.ts | 84 + .../tests/0013_EssentialReplace.ts | 102 + .../tests/0014_EssentialRepairMobile.ts | 77 + ...5_EssentialReplacePartsQuestionsDropoff.ts | 85 + .../tests/0016_EssentialRepairInShop.ts | 78 + ...17_EssentialTpaNotEnabledBailoutReplace.ts | 86 + .../tests/0018_EssentialTpaEnabledReplace.ts | 87 + .../0019_EssentialTpaEnabledReplaceRecal.ts | 88 + .../0020_EssentialVehicleLookupBailout.ts | 81 + .../0021_EssentialPriceServiceErrorBailout.ts | 82 + playwright-tests/tests/README.md | 101 + .../advanced/0001a_ReplaceInShopCredit.ts | 80 + .../advanced/0002a_ReplaceOemEndorsement.ts | 115 + .../tests/advanced/0003a_MobileAfterpay.ts | 95 + .../tests/advanced/0004a_NoDeductibleAdas.ts | 83 + .../advanced/0005a_CapabilityQuestions.ts | 91 + .../0006a_RepairNoDeductibleMobile.ts | 95 + .../advanced/0007a_RepairStateLanguage.ts | 95 + .../tests/advanced/0008a_RepairTpa.ts | 93 + .../advanced/0009a_NoDeductibleFlorida.ts | 101 + .../tests/advanced/0010a_RearGlass.ts | 102 + .../tests/advanced/0011a_ItacNoAdas.ts | 115 + .../tests/advanced/0012a_ItacDropOff.ts | 90 + .../tests/advanced/0013a_ItacMobile.ts | 90 + .../tests/advanced/0014a_NoCompAdas.ts | 90 + .../advanced/0015a_NoCompPartQuestions.ts | 103 + .../tests/advanced/0016a_NoCompAllGlass.ts | 120 + .../tests/advanced/0017a_NoCompPremium.ts | 96 + .../tests/advanced/0018a_NoCompGlassOnly.ts | 93 + .../tests/advanced/0019a_NoCompEditVehicle.ts | 95 + .../tests/advanced/0020a_NoCompChangeLoc.ts | 92 + .../location/api/v1/location/zip/36116.json | 8 + .../location/api/v1/location/zip/43016.json | 8 + .../location/api/v1/location/zip/75023.json | 8 + .../mockResponses/mockResponsesConfig.json | 12 + .../api/v1/coverage/policy-information.json | 57 + .../api/vi/coverage/policy-information.json | 82 + playwright-tests/tsconfig.json | 62 + 135 files changed, 11467 insertions(+), 137 deletions(-) create mode 100644 Dockerfile.playwright create mode 100644 playwright-tests/.dockerignore create mode 100644 playwright-tests/.env create mode 100644 playwright-tests/.env.dev create mode 100644 playwright-tests/.env.example create mode 100644 playwright-tests/.gitignore create mode 100644 playwright-tests/.sauce/config.yml create mode 100644 playwright-tests/.sauceignore create mode 100644 playwright-tests/business-logic/data/ClientData.ts create mode 100644 playwright-tests/business-logic/data/MockPolicyData.ts create mode 100644 playwright-tests/business-logic/rules/RuleEngineBuiltins.ts create mode 100644 playwright-tests/business-logic/types/Authentication.ts create mode 100644 playwright-tests/business-logic/types/CcisApi.ts create mode 100644 playwright-tests/business-logic/types/Client.ts create mode 100644 playwright-tests/business-logic/types/CustomerDetails.ts create mode 100644 playwright-tests/business-logic/types/DigitalApi.ts create mode 100644 playwright-tests/business-logic/types/Enums.ts create mode 100644 playwright-tests/business-logic/types/FrameworkConfig.ts create mode 100644 playwright-tests/business-logic/types/IAddress.ts create mode 100644 playwright-tests/business-logic/types/IBailoutFlags.ts create mode 100644 playwright-tests/business-logic/types/IDisposable.ts create mode 100644 playwright-tests/business-logic/types/ITestCase.ts create mode 100644 playwright-tests/business-logic/types/ITestData.ts create mode 100644 playwright-tests/business-logic/types/ITestPages.ts create mode 100644 playwright-tests/business-logic/types/IValidationExpectation.ts create mode 100644 playwright-tests/business-logic/types/IValidations.ts create mode 100644 playwright-tests/business-logic/types/RuleEngine.ts create mode 100644 playwright-tests/business-logic/types/Test.ts create mode 100644 playwright-tests/business-logic/types/TestCase.ts create mode 100644 playwright-tests/business-logic/types/Validations.ts create mode 100644 playwright-tests/business-logic/validations/Soft.ts create mode 100644 playwright-tests/eslint.config.js create mode 100644 playwright-tests/impl/api/AdminServiceApiUtil.ts create mode 100644 playwright-tests/impl/api/ApiResponseInterceptUtil.ts create mode 100644 playwright-tests/impl/api/CcisApiUtil.ts create mode 100644 playwright-tests/impl/utils/DateUtils.ts create mode 100644 playwright-tests/impl/utils/EnumUtils.ts create mode 100644 playwright-tests/impl/utils/FakerUtils.ts create mode 100644 playwright-tests/impl/utils/FileUtils.ts create mode 100644 playwright-tests/impl/utils/HttpUtils.ts create mode 100644 playwright-tests/impl/utils/LoggingUtils.ts create mode 100644 playwright-tests/impl/utils/ParsingUtils.ts create mode 100644 playwright-tests/impl/utils/PropertyUtils.ts create mode 100644 playwright-tests/impl/utils/TaggingUtils.ts create mode 100644 playwright-tests/impl/utils/ThrowUtils.ts create mode 100644 playwright-tests/impl/utils/TimingUtils.ts create mode 100644 playwright-tests/impl/utils/TokenUtils.ts create mode 100644 playwright-tests/impl/utils/TryUtils.ts create mode 100644 playwright-tests/pages/AfterpayPage.ts create mode 100644 playwright-tests/pages/BailoutPage.ts create mode 100644 playwright-tests/pages/BasePage.ts create mode 100644 playwright-tests/pages/CapabilityQuestionsPage.ts create mode 100644 playwright-tests/pages/ContactConfirmationPage.ts create mode 100644 playwright-tests/pages/ContactDetailsPage.ts create mode 100644 playwright-tests/pages/CoverageStatementPage.ts create mode 100644 playwright-tests/pages/DuplicateCheckPage.ts create mode 100644 playwright-tests/pages/EndorsementsPage.ts create mode 100644 playwright-tests/pages/OrderConfirmationPage.ts create mode 100644 playwright-tests/pages/PartQuestionsPage.ts create mode 100644 playwright-tests/pages/PaymentMethodPage.ts create mode 100644 playwright-tests/pages/PaymentPage.ts create mode 100644 playwright-tests/pages/PaypalPage.ts create mode 100644 playwright-tests/pages/PolicyHolderDetailsPage.ts create mode 100644 playwright-tests/pages/PolicyVehiclesPage.ts create mode 100644 playwright-tests/pages/ProviderPreferencePage.ts create mode 100644 playwright-tests/pages/SchedulePage.ts create mode 100644 playwright-tests/pages/ServiceLocationPage.ts create mode 100644 playwright-tests/pages/ServicePackagesPage.ts create mode 100644 playwright-tests/pages/TpaConfirmationPage.ts create mode 100644 playwright-tests/pages/TpaSearchPage.ts create mode 100644 playwright-tests/pages/TpaSubmitPage.ts create mode 100644 playwright-tests/pages/VehicleDamagePage.ts create mode 100644 playwright-tests/pages/VehicleLookupAddressPage.ts create mode 100644 playwright-tests/pages/VehicleLookupLicensePage.ts create mode 100644 playwright-tests/pages/VehicleLookupPage.ts create mode 100644 playwright-tests/pages/VehiclePartsPage.ts create mode 100644 playwright-tests/pages/VehicleSelectionPage.ts create mode 100644 playwright-tests/pages/VinLookupPage.ts create mode 100644 playwright-tests/pages/WelcomePage.ts create mode 100644 playwright-tests/pages/forms/AddressForm.ts create mode 100644 playwright-tests/pages/forms/VehicleSelectionForm.ts create mode 100644 playwright-tests/playwright.config.ts create mode 100644 playwright-tests/tests/0000__M.test.ts create mode 100644 playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts create mode 100644 playwright-tests/tests/0002_EssentialRepairInShopAcura.ts create mode 100644 playwright-tests/tests/0003_EssentialTpaNotEnabledBailout.ts create mode 100644 playwright-tests/tests/0004_EssentialTpaEnabled.ts create mode 100644 playwright-tests/tests/0005_EssentialReplaceDynamicAdas.ts create mode 100644 playwright-tests/tests/0007_EssentialRepairHyundaiMobile.ts create mode 100644 playwright-tests/tests/0008_EssentialHeavyVehicleBailout.ts create mode 100644 playwright-tests/tests/0009_EssentialPartsServiceErrorBailout.ts create mode 100644 playwright-tests/tests/0010_EssentialVehicleNotFoundBailout.ts create mode 100644 playwright-tests/tests/0011_EssentialDoNotSeeShopBailout.ts create mode 100644 playwright-tests/tests/0012_EssentialUniqueGlass.ts create mode 100644 playwright-tests/tests/0013_EssentialReplace.ts create mode 100644 playwright-tests/tests/0014_EssentialRepairMobile.ts create mode 100644 playwright-tests/tests/0015_EssentialReplacePartsQuestionsDropoff.ts create mode 100644 playwright-tests/tests/0016_EssentialRepairInShop.ts create mode 100644 playwright-tests/tests/0017_EssentialTpaNotEnabledBailoutReplace.ts create mode 100644 playwright-tests/tests/0018_EssentialTpaEnabledReplace.ts create mode 100644 playwright-tests/tests/0019_EssentialTpaEnabledReplaceRecal.ts create mode 100644 playwright-tests/tests/0020_EssentialVehicleLookupBailout.ts create mode 100644 playwright-tests/tests/0021_EssentialPriceServiceErrorBailout.ts create mode 100644 playwright-tests/tests/README.md create mode 100644 playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts create mode 100644 playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts create mode 100644 playwright-tests/tests/advanced/0003a_MobileAfterpay.ts create mode 100644 playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts create mode 100644 playwright-tests/tests/advanced/0005a_CapabilityQuestions.ts create mode 100644 playwright-tests/tests/advanced/0006a_RepairNoDeductibleMobile.ts create mode 100644 playwright-tests/tests/advanced/0007a_RepairStateLanguage.ts create mode 100644 playwright-tests/tests/advanced/0008a_RepairTpa.ts create mode 100644 playwright-tests/tests/advanced/0009a_NoDeductibleFlorida.ts create mode 100644 playwright-tests/tests/advanced/0010a_RearGlass.ts create mode 100644 playwright-tests/tests/advanced/0011a_ItacNoAdas.ts create mode 100644 playwright-tests/tests/advanced/0012a_ItacDropOff.ts create mode 100644 playwright-tests/tests/advanced/0013a_ItacMobile.ts create mode 100644 playwright-tests/tests/advanced/0014a_NoCompAdas.ts create mode 100644 playwright-tests/tests/advanced/0015a_NoCompPartQuestions.ts create mode 100644 playwright-tests/tests/advanced/0016a_NoCompAllGlass.ts create mode 100644 playwright-tests/tests/advanced/0017a_NoCompPremium.ts create mode 100644 playwright-tests/tests/advanced/0018a_NoCompGlassOnly.ts create mode 100644 playwright-tests/tests/advanced/0019a_NoCompEditVehicle.ts create mode 100644 playwright-tests/tests/advanced/0020a_NoCompChangeLoc.ts create mode 100644 playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/36116.json create mode 100644 playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/43016.json create mode 100644 playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/75023.json create mode 100644 playwright-tests/tests/mockResponses/mockResponsesConfig.json create mode 100644 playwright-tests/tests/mockResponses/scenario1/coverage/api/v1/coverage/policy-information.json create mode 100644 playwright-tests/tests/mockResponses/scenario2/coverage/api/vi/coverage/policy-information.json create mode 100644 playwright-tests/tsconfig.json diff --git a/.gitignore b/.gitignore index 8f48b7f4..c859f2d5 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,13 @@ pnpm-debug.log* *.sln *.sw? +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +artifacts/ + # Misc coverage/* junit.xml diff --git a/Dockerfile.playwright b/Dockerfile.playwright new file mode 100644 index 00000000..6271a937 --- /dev/null +++ b/Dockerfile.playwright @@ -0,0 +1,21 @@ +FROM node:16 + +FROM mcr.microsoft.com/playwright:v1.48.0-noble + +# Set the working directory in the container +WORKDIR /app + +# Copy package.json and package-lock.json +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Install Playwright browsers +RUN npx playwright install --with-deps + +# Copy the rest of the application code +COPY . . + +# Run Playwright tests +CMD ["npx", "playwright", "test"] diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6ff443c1..cedebb5b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -31,12 +31,18 @@ resources: variables: - group: Digital-Infrastructure - group: ISS-BuildBranches + - name: dockerImageName + value: 'playwright-tests' + - name: imageTag + value: '$(Build.BuildId)' + - name: totalShards + value: 4 stages: # PR's - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: - stage: TestPr - displayName: Run Unit Tests For PullRequest + displayName: Run Tests For PullRequest jobs: - template: templates/digital/vue-jest-run-unit-tests.yml@AzureDevOps parameters: @@ -44,6 +50,123 @@ stages: npmLocation: $(Build.SourcesDirectory) testResultsFile: junit.xml summaryFileLocation: $(Build.SourcesDirectory)/coverage/cobertura-coverage.xml + - job: playwright_tests + continueOnError: true + strategy: + matrix: + shard1: + shardNumber: 1 + shard2: + shardNumber: 2 + shard3: + shardNumber: 3 + shard4: + shardNumber: 4 + + steps: + - task: Docker@2 + displayName: 'Build Docker Image' + inputs: + command: build + dockerfile: Dockerfile.playwright + repository: $(dockerImageName) + tags: $(imageTag) + arguments: '--no-cache --pull' + + - script: | + # Create container and run tests + container_id=$(docker create \ + --ipc=host \ + -e CCIS_API_AUTH=$(CCIS_API_AUTH) \ + -e BASE_URL=$(BASE_URL) \ + -e CCIS_API_URL=$(CCIS_API_URL) \ + -e ADMIN_SERVICE_API_URL=$(ADMIN_SERVICE_API_URL) \ + -e SHARD=$(shardNumber) \ + -e CI=true \ + -e NODE_ENV=$(NODE_ENV) \ + $(dockerImageName):$(imageTag) \ + npx concurrently -k -n "server,playwright"\ + "sed -i \"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\" \"./vue.config.js\" && echo \"Updated config file to use TEST APIs\" && npm run serve -- --port=8080"\ + "npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@smoke | @Advanced\"") + + # Start container and stream logs + + echo "Starting tests for shard $(shardNumber)..." + docker start -a $container_id + + # Create directory for test results + echo "Creating test results directory..." + mkdir -p $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber) + + # Copy test results from container + echo "Copying test results..." + docker cp $container_id:/app/blob-report/. $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)/ + + # Remove container + echo "Cleaning up container..." + docker rm $container_id + + # Check if tests failed + if [ $? -ne 0 ]; then + echo "Tests failed in shard $(shardNumber)!" + exit 1 + fi + displayName: 'Run Playwright Tests - Shard $(shardNumber)' + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Test Reports - Shard $(shardNumber)' + condition: always() + inputs: + targetPath: '$(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)' + artifact: 'playwright-report-shard-$(shardNumber)' + publishLocation: 'pipeline' + + - script: | + docker rmi $(dockerImageName):$(imageTag) -f + displayName: 'Cleanup Docker Image' + condition: always() + + - job: download_and_merge_reports + container: + image: mcr.microsoft.com/playwright:v1.48.0-noble + dependsOn: playwright_tests + steps: + - task: DownloadPipelineArtifact@2 + inputs: + targetPath: '$(System.DefaultWorkingDirectory)/playwright-reports' + - script: | + npm i ortoni-report && + for dir in $(System.DefaultWorkingDirectory)/playwright-reports/*/; do + if [ -d "$dir" ]; then + mv "$dir"* $(System.DefaultWorkingDirectory)/playwright-reports/ + rmdir "$dir" + fi + done + npx playwright merge-reports --reporter=ortoni-report,junit $(System.DefaultWorkingDirectory)/playwright-reports + ls + displayName: merge_reports + env: + PLAYWRIGHT_JUNIT_OUTPUT_FILE: "test-results/results.xml" + + # TODO: Enable later when working on PR gate + # - task: PublishTestResults@2 + # displayName: 'Publish test results' + # inputs: + # searchFolder: 'test-results' + # testResultsFormat: 'JUnit' + # testResultsFiles: 'results.xml' + # mergeTestResults: true + # failTaskOnFailedTests: false + # testRunTitle: 'Playwright Tests' + # condition: succeededOrFailed() + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Merged Report' + condition: always() + inputs: + targetPath: '$(System.DefaultWorkingDirectory)/ortoni-report' + artifact: 'playwright-merged-report' + publishLocation: 'pipeline' - ${{ else }}: # Dev Build/Deploy diff --git a/package-lock.json b/package-lock.json index f012fae5..0817cac3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,11 +22,16 @@ "vue-router": "4.2.4" }, "devDependencies": { + "@faker-js/faker": "^9.0.3", "@pinia/testing": "0.1.2", + "@playwright/test": "^1.48.0", "@rushstack/eslint-patch": "^1.3.2", + "@saucelabs/playwright-reporter": "^1.5.0", "@testing-library/jest-dom": "5.16.5", "@testing-library/user-event": "14.4.3", "@testing-library/vue": "6.6.1", + "@types/dotenv-safe": "^8.1.6", + "@types/node": "^22.7.5", "@vitejs/plugin-vue": "4.2.3", "@vitest/coverage-v8": "^0.34.1", "@vue/cli-plugin-babel": "^5.0.8", @@ -35,8 +40,11 @@ "@vue/cli-service": "~5.0.0", "@vue/test-utils": "^2.4.1", "@vue/vue3-jest": "^27.0.0-alpha.1", + "axios": "^1.7.8", "axios-mock-adapter": "^1.21.5", "babel-jest": "^27.0.6", + "concurrently": "^9.1.2", + "dotenv-safe": "^9.1.0", "eslint": "^8.45.0", "eslint-config-airbnb-base": "15.0.0", "eslint-import-resolver-alias": "1.1.2", @@ -48,11 +56,16 @@ "jest-serializer-vue": "^3.1.0", "jsdoc": "^4.0.2", "jsdom": "^22.1.0", + "luxon": "^3.5.0", + "ortoni-report": "^2.0.8", "sass": "^1.77.8", "sass-loader": "^12.0.0", + "saucectl": "^0.188.0", + "typescript-eslint": "^8.11.0", "vite": "^4.5.9", "vitest": "^0.33.0", - "volar-service-vetur": "*" + "volar-service-vetur": "latest", + "wait-on": "^8.0.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2163,19 +2176,21 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -2198,13 +2213,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -2220,6 +2237,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -2232,6 +2250,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -2240,14 +2259,41 @@ } }, "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@faker-js/faker": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.4.0.tgz", + "integrity": "sha512-85+k0AxaZSTowL0gXp8zYWDIrWclTbRPg/pm/V0dSFZ6W6D4lhcG3uuZl4zLsEKfEvs69xDbLN2cHQudwp95JA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -2264,13 +2310,15 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -2291,10 +2339,12 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -3045,6 +3095,66 @@ "node": ">= 8" } }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@one-ini/wasm": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", @@ -3092,6 +3202,22 @@ } } }, + "node_modules/@playwright/test": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz", + "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.50.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -3114,11 +3240,109 @@ "integrity": "sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw==", "dev": true }, - "node_modules/@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "node_modules/@saucelabs/bin-wrapper": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@saucelabs/bin-wrapper/-/bin-wrapper-2.1.1.tgz", + "integrity": "sha512-wzG3tx5wOmzFxdKqCUAq7aPR0zHk6PIiV+rGjTJMApyGyJtjAWSXPGzdfejio06A6sdBNkDeZXm/Fb+yR3h8Bg==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "adm-zip": "^0.5.15", + "axios": "^1.7.5", + "https-proxy-agent": "^7.0.5", + "tar-stream": "^3.1.7" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, + "node_modules/@saucelabs/bin-wrapper/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@saucelabs/bin-wrapper/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@saucelabs/playwright-reporter": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@saucelabs/playwright-reporter/-/playwright-reporter-1.5.0.tgz", + "integrity": "sha512-9x596YwohFLBm0NBlNMWQGnrA3vi8WhZjQm0HtRBpctIvKsNUo2wOLvtLx6TjC2K0+8kzbG6gUe6O8kdbE9QOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@saucelabs/sauce-json-reporter": "4.1.0", + "@saucelabs/testcomposer": "3.0.1", + "axios": "1.7.5", + "debug": "^4.3.6", + "ua-parser-js": "^1.0.39" + }, + "engines": { + "node": ">=16.13.2" + }, + "peerDependencies": { + "@playwright/test": "^1.16.3" + } + }, + "node_modules/@saucelabs/playwright-reporter/node_modules/axios": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@saucelabs/sauce-json-reporter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@saucelabs/sauce-json-reporter/-/sauce-json-reporter-4.1.0.tgz", + "integrity": "sha512-UhqXXsaW4yRA/7v10qeCp1B7tIw09fghsNIr2wuU93XhbkqxXmMJTYItqFX66k1JJSEzHsay8Md7sTkUlJfV6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.13.2" + } + }, + "node_modules/@saucelabs/testcomposer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@saucelabs/testcomposer/-/testcomposer-3.0.1.tgz", + "integrity": "sha512-4Ye6v09vXsxud89YSoQ1Ag6JFUsZUTYYMZtOux/S1sUch2nN9jkhb4Itn6PxmUqwA7vjMNII8qHOPdLByEFQhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "axios": "^1.7.5", + "form-data": "^4.0.0" + }, + "engines": { + "node": ">=16.13.2" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" } @@ -3577,6 +3801,27 @@ "@types/node": "*" } }, + "node_modules/@types/dotenv-safe": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@types/dotenv-safe/-/dotenv-safe-8.1.6.tgz", + "integrity": "sha512-ftZXu3WGT6ALq+f98IX2gWriGMPds+0ku5h8kZewNpY47ua+Z+XNcin9apZ2kVd4B9LV1vMfUOyDf1/hhreR0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "dotenv": "^8.2.0" + } + }, + "node_modules/@types/dotenv-safe/node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -3711,10 +3956,14 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.2.tgz", - "integrity": "sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw==", - "dev": true + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -3831,6 +4080,245 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.23.0.tgz", + "integrity": "sha512-vBz65tJgRrA1Q5gWlRfvoH+w943dq9K1p1yDBY2pc+a1nbBLZp7fB9+Hk8DaALUbzjqlMfgaqlVPT1REJdkt/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.23.0", + "@typescript-eslint/type-utils": "8.23.0", + "@typescript-eslint/utils": "8.23.0", + "@typescript-eslint/visitor-keys": "8.23.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.23.0.tgz", + "integrity": "sha512-h2lUByouOXFAlMec2mILeELUbME5SZRN/7R9Cw2RD2lRQQY08MWMM+PmVVKKJNK1aIwqTo9t/0CvOxwPbRIE2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.23.0", + "@typescript-eslint/types": "8.23.0", + "@typescript-eslint/typescript-estree": "8.23.0", + "@typescript-eslint/visitor-keys": "8.23.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.23.0.tgz", + "integrity": "sha512-OGqo7+dXHqI7Hfm+WqkZjKjsiRtFUQHPdGMXzk5mYXhJUedO7e/Y7i8AK3MyLMgZR93TX4bIzYrfyVjLC+0VSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.23.0", + "@typescript-eslint/visitor-keys": "8.23.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.23.0.tgz", + "integrity": "sha512-iIuLdYpQWZKbiH+RkCGc6iu+VwscP5rCtQ1lyQ7TYuKLrcZoeJVpcLiG8DliXVkUxirW/PWlmS+d6yD51L9jvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.23.0", + "@typescript-eslint/utils": "8.23.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.23.0.tgz", + "integrity": "sha512-1sK4ILJbCmZOTt9k4vkoulT6/y5CHJ1qUYxqpF1K/DBAd8+ZUL4LlSCxOssuH5m4rUaaN0uS0HlVPvd45zjduQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.23.0.tgz", + "integrity": "sha512-LcqzfipsB8RTvH8FX24W4UUFk1bl+0yTOf9ZA08XngFwMg4Kj8A+9hwz8Cr/ZS4KwHrmo9PJiLZkOt49vPnuvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.23.0", + "@typescript-eslint/visitor-keys": "8.23.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.23.0.tgz", + "integrity": "sha512-uB/+PSo6Exu02b5ZEiVtmY6RVYO7YU5xqgzTIVZwTHvvK3HsL8tZZHFaTLFtRG3CsV4A5mhOv+NZx5BlhXPyIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.23.0", + "@typescript-eslint/types": "8.23.0", + "@typescript-eslint/typescript-estree": "8.23.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.23.0.tgz", + "integrity": "sha512-oWWhcWDLwDfu++BGTZcmXWqpwtkwb5o7fxUIGksMQQDSdPW9prsSnfIOZMlsj4vBOSrcnjIUZMiIjODgGosFhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.23.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, "node_modules/@vitejs/plugin-vue": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz", @@ -5164,6 +5652,16 @@ "node": ">= 10.0.0" } }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -5176,6 +5674,37 @@ "node": ">= 6.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -5307,6 +5836,15 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, "node_modules/arch": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", @@ -5336,6 +5874,23 @@ "node": ">=14" } }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -5427,7 +5982,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, "node_modules/at-least-node": { "version": "1.0.0", @@ -5484,9 +6040,11 @@ } }, "node_modules/axios": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", - "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -5538,6 +6096,13 @@ "is-retry-allowed": "^2.2.0" } }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/babel-jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", @@ -5805,6 +6370,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bare-events": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", + "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -5849,6 +6422,17 @@ "node": ">=8" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -6076,6 +6660,53 @@ "node": ">=8" } }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -6274,6 +6905,17 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -6307,6 +6949,18 @@ "node": ">= 10.0" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6510,6 +7164,18 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "color-support": "bin.js" + } + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -6526,6 +7192,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -6608,6 +7275,168 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concurrently": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz", + "integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/concurrently/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/concurrently/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concurrently/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/concurrently/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/condense-newlines": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", @@ -6647,6 +7476,15 @@ "node": ">=0.8" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, "node_modules/consolidate": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", @@ -7145,12 +7983,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -7167,6 +8006,23 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -7217,6 +8073,17 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -7417,10 +8284,20 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "engines": { "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -7440,6 +8317,17 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -7622,6 +8510,16 @@ "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", "dev": true }, + "node_modules/dotenv-safe": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/dotenv-safe/-/dotenv-safe-9.1.0.tgz", + "integrity": "sha512-2qwVAnUN+EDpu41pIK1XiJpHXKHV9Dnti3cE1EnUXT1/BV5+B7xuSZtgZ/4LExkCpp5F6BGikraezQL+8hKCOA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "dotenv": ">= 8.2.0" + } + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -7751,6 +8649,33 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -7782,6 +8707,27 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -8000,27 +8946,30 @@ } }, "node_modules/eslint": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", - "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -8289,10 +9238,11 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -8751,6 +9701,17 @@ "node": ">= 0.8.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/expect": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", @@ -8871,17 +9832,25 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -8965,6 +9934,14 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -9063,6 +10040,7 @@ "version": "1.15.6", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, "funding": [ { "type": "individual", @@ -9091,6 +10069,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -9131,6 +10110,14 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -9146,6 +10133,20 @@ "node": ">=10" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fs-monkey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", @@ -9208,6 +10209,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -9291,6 +10315,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -9484,6 +10516,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, "node_modules/hash-sum": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", @@ -9661,6 +10702,15 @@ "entities": "^2.0.0" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true + }, "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -9763,6 +10813,18 @@ "node": ">=10.17.0" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -9820,10 +10882,11 @@ } }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -9896,6 +10959,15 @@ "node": ">=8" } }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -9932,6 +11004,31 @@ "node": ">= 0.4" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "peer": true + }, "node_modules/ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -10178,6 +11275,15 @@ "node": ">=8" } }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -11488,6 +12594,28 @@ "node": ">=10" } }, + "node_modules/jest-environment-jsdom/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/jest-environment-jsdom/node_modules/xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -13157,15 +14285,16 @@ } }, "node_modules/joi": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz", - "integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==", + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.0", + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, @@ -13266,6 +14395,15 @@ "xmlcreate": "^2.0.4" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/jsdoc": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", @@ -13391,27 +14529,6 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/jsdom/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -13917,6 +15034,16 @@ "node": ">=10" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/lz-string": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", @@ -13952,6 +15079,65 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -14123,6 +15309,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "engines": { "node": ">= 0.6" } @@ -14131,6 +15318,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -14147,6 +15335,20 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -14247,10 +15449,14 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minipass": { "version": "3.3.4", @@ -14264,6 +15470,101 @@ "node": ">=8" } }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -14276,6 +15577,14 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/mlly": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.0.tgz", @@ -14304,10 +15613,11 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -14350,6 +15660,14 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -14387,6 +15705,42 @@ "tslib": "^2.0.3" } }, + "node_modules/node-abi": { + "version": "3.74.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", + "integrity": "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -14438,6 +15792,84 @@ "node": ">= 6.13.0" } }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -14528,6 +15960,25 @@ "node": ">=4" } }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -14811,6 +16262,21 @@ "node": ">=8" } }, + "node_modules/ortoni-report": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/ortoni-report/-/ortoni-report-2.0.9.tgz", + "integrity": "sha512-3i95ZM2kajVQuvT9q/TWOT6zOlJIBG0I5snuzHsWIJpHSszugHrTO+NsTHf4Ez9aC5UlwCy6dMXCK/llaZav6Q==", + "dev": true, + "license": "GPL-3.0-only", + "bin": { + "ortoni-report": "dist/cli/cli.js" + }, + "peerDependencies": { + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7", + "ws": "^8.18.0" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -14847,6 +16313,24 @@ "node": ">=8" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -15124,6 +16608,38 @@ "pathe": "^1.1.0" } }, + "node_modules/playwright": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", + "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.50.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", + "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/portfinder": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", @@ -15691,6 +17207,34 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prettier": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", @@ -15779,6 +17323,43 @@ "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" } }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -15823,7 +17404,8 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true }, "node_modules/pseudomap": { "version": "1.0.2", @@ -15956,6 +17538,34 @@ "node": ">= 0.8" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "peer": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -16338,6 +17948,16 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -16419,6 +18039,23 @@ } } }, + "node_modules/saucectl": { + "version": "0.188.0", + "resolved": "https://registry.npmjs.org/saucectl/-/saucectl-0.188.0.tgz", + "integrity": "sha512-YJIVyGbc14zBkwdwzsOh307Px8ULLWXQTJdiub85iP3NZE5KqTiEYBfvxI8dg+7i8n+C4ySPIiiUWGFAIsGl8Q==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@saucelabs/bin-wrapper": "^2.1.0" + }, + "bin": { + "saucectl": "index.js" + }, + "engines": { + "node": ">=16.13.2" + } + }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -16524,12 +18161,6 @@ "node": ">= 0.8" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -16632,6 +18263,15 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -16698,10 +18338,17 @@ } }, "node_modules/shell-quote": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", - "dev": true + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/side-channel": { "version": "1.0.6", @@ -16733,6 +18380,55 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sirv": { "version": "1.0.19", "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", @@ -16762,6 +18458,19 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -16782,6 +18491,40 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -16877,6 +18620,40 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sqlite": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz", + "integrity": "sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, "node_modules/ssri": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", @@ -16944,6 +18721,20 @@ "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==", "dev": true }, + "node_modules/streamx": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", + "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -17228,6 +19019,102 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", + "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/tar-fs/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -17316,6 +19203,16 @@ "node": ">=8" } }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -17496,6 +19393,29 @@ "node": ">=14" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/tsconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", @@ -17635,6 +19555,20 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -17691,6 +19625,56 @@ "node": ">=4.2.0" } }, + "node_modules/typescript-eslint": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.23.0.tgz", + "integrity": "sha512-/LBRo3HrXr5LxmrdYSOCvoAMm7p2jNizNfbIpCgvG4HMsnoprRUOce/+8VJ9BDYWW68rqIENE/haVLWPeFZBVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.23.0", + "@typescript-eslint/parser": "8.23.0", + "@typescript-eslint/utils": "8.23.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/ua-parser-js": { + "version": "1.0.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", + "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -17724,6 +19708,13 @@ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -17764,6 +19755,30 @@ "node": ">=4" } }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -18381,6 +20396,26 @@ "node": ">=14" } }, + "node_modules/wait-on": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.2.tgz", + "integrity": "sha512-qHlU6AawrgAIHlueGQHQ+ETcPLAauXbnoTKl3RKq20W0T8x0DKVAo5xWIYjHSyvHxQlcYbFdR0jp4T9bDVITFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.7.9", + "joi": "^17.13.3", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "rxjs": "^7.8.1" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -18578,6 +20613,28 @@ "node": ">=8" } }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/webpack-chain": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.5.1.tgz", @@ -18784,27 +20841,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/webpack-merge": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", @@ -18987,6 +21023,18 @@ "node": ">=8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", @@ -19062,16 +21110,17 @@ } }, "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { diff --git a/package.json b/package.json index deddbded..998968d8 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "serve": "vue-cli-service serve", "build": "vue-cli-service build", "test:unit": "vue-cli-service test:unit --coverage --ci --colors", - "test:unit:lite": "vue-cli-service test:unit --ci" + "test:unit:lite": "vue-cli-service test:unit --ci", + "test:playwright": "playwright test --config=playwright-tests/playwright.config.ts" }, "dependencies": { "axios": "^1.4.0", @@ -29,11 +30,16 @@ "vue-router": "4.2.4" }, "devDependencies": { + "@faker-js/faker": "^9.0.3", "@pinia/testing": "0.1.2", + "@playwright/test": "^1.48.0", "@rushstack/eslint-patch": "^1.3.2", + "@saucelabs/playwright-reporter": "^1.5.0", "@testing-library/jest-dom": "5.16.5", "@testing-library/user-event": "14.4.3", "@testing-library/vue": "6.6.1", + "@types/dotenv-safe": "^8.1.6", + "@types/node": "^22.7.5", "@vitejs/plugin-vue": "4.2.3", "@vitest/coverage-v8": "^0.34.1", "@vue/cli-plugin-babel": "^5.0.8", @@ -42,8 +48,11 @@ "@vue/cli-service": "~5.0.0", "@vue/test-utils": "^2.4.1", "@vue/vue3-jest": "^27.0.0-alpha.1", + "axios": "^1.7.8", "axios-mock-adapter": "^1.21.5", "babel-jest": "^27.0.6", + "concurrently": "^9.1.2", + "dotenv-safe": "^9.1.0", "eslint": "^8.45.0", "eslint-config-airbnb-base": "15.0.0", "eslint-import-resolver-alias": "1.1.2", @@ -55,10 +64,15 @@ "jest-serializer-vue": "^3.1.0", "jsdoc": "^4.0.2", "jsdom": "^22.1.0", + "luxon": "^3.5.0", + "ortoni-report": "^2.0.8", "sass": "^1.77.8", "sass-loader": "^12.0.0", + "saucectl": "^0.188.0", + "typescript-eslint": "^8.11.0", "vite": "^4.5.9", "vitest": "^0.33.0", - "volar-service-vetur": "latest" + "volar-service-vetur": "latest", + "wait-on": "^8.0.2" } } diff --git a/playwright-tests/.dockerignore b/playwright-tests/.dockerignore new file mode 100644 index 00000000..3f9fe19d --- /dev/null +++ b/playwright-tests/.dockerignore @@ -0,0 +1,4 @@ +.git +*Dockerfile* +*docker-compose* +node_modules \ No newline at end of file diff --git a/playwright-tests/.env b/playwright-tests/.env new file mode 100644 index 00000000..22fc8ecc --- /dev/null +++ b/playwright-tests/.env @@ -0,0 +1,14 @@ +# CLIENT_NAME="undefined" # TODO: evaluate necessity of this +# CLIENT_TAG="undefined" +# BASE_URL="https://selfservice.dev.glassclaim.com" + +CCIS_API_AUTH= "" # Input manually + +# DEV +BASE_URL="https://selfservice.test.glassclaim.com" +CCIS_API_URL="https://api.test.belronus.io" # Test API connects to our dev environment for some reason +ADMIN_SERVICE_API_URL="https://issadminapi.dev.sagaws.net/iss-admin/api/v1/" + +# QA +# BASE_URL="https://selfservice.test.glassclaim.com" +# CCIS_API_URL="https://api.test.belronus.io" diff --git a/playwright-tests/.env.dev b/playwright-tests/.env.dev new file mode 100644 index 00000000..493f0442 --- /dev/null +++ b/playwright-tests/.env.dev @@ -0,0 +1,14 @@ +# CLIENT_NAME="undefined" # TODO: evaluate necessity of this +# CLIENT_TAG="undefined" +# BASE_URL="https://selfservice.dev.glassclaim.com" + +CCIS_API_AUTH="" # Input manually + +# DEV +BASE_URL="https://selfservice.dev.glassclaim.com" +CCIS_API_URL="https://api.test.belronus.io" # Test API connects to our dev environment for some reason +ADMIN_SERVICE_API_URL="https://issadminapi.dev.sagaws.net/iss-admin/api/v1/" + +# QA +# BASE_URL="https://selfservice.test.glassclaim.com" +# CCIS_API_URL="https://api.test.belronus.io" \ No newline at end of file diff --git a/playwright-tests/.env.example b/playwright-tests/.env.example new file mode 100644 index 00000000..dcf93683 --- /dev/null +++ b/playwright-tests/.env.example @@ -0,0 +1,10 @@ +CCIS_API_AUTH= + +# DEV +BASE_URL="https://selfservice.dev.glassclaim.com" +CCIS_API_URL="https://api.dev.belronus.io" + + +# QA +# BASE_URL="https://selfservice.test.glassclaim.com" +# CCIS_API_URL="https://api.test.belronus.io" diff --git a/playwright-tests/.gitignore b/playwright-tests/.gitignore new file mode 100644 index 00000000..c0a7b918 --- /dev/null +++ b/playwright-tests/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +artifacts/ diff --git a/playwright-tests/.sauce/config.yml b/playwright-tests/.sauce/config.yml new file mode 100644 index 00000000..a42a0fa9 --- /dev/null +++ b/playwright-tests/.sauce/config.yml @@ -0,0 +1,74 @@ +apiVersion: v1alpha +kind: playwright +showConsoleLog: true +sauce: + region: us-west-1 + concurrency: 20 + sauceignore: .sauceignore +playwright: + version: 1.48.2 + configFile: playwright.config.ts + +suites: +- name: 'ISS-NextGen-chromium' + numShards: 20 + testMatch: + - tests/0000__M.test.ts + platformName: Windows 10 + env: + DEBUG: "pw:worker" + SAUCE_USERNAME: $SAUCE_USERNAME + SAUCE_ACCESS_KEY: $SAUCE_ACCESS_KEY + params: + browserName: chrome + project: "chromium" + artifacts: "**test-results\\index.html" +artifacts: + cleanup: true + download: + match: + - '*' + when: always + directory: ./artifacts + +# - name: 'Mobile Android Chrome Tests' +# shard: spec +# testMatch: +# - e2e/ +# platformName: Windows 11 +# params: +# browserName: chrome +# project: "Mobile Android Tests" + +# - name: 'Desktop Safari' +# testMatch: +# - .ts +# platformName: Windows 10 +# params: +# browserName: webkit +# project: "webkit" + + +# - name: 'Mobile iOS Tests' +# shard: spec +# testMatch: +# - e2e/ +# platformName: macOS 13 +# params: +# browserName: webkit +# project: "Mobile iOS Tests" +# env: +# DEBUG: "pw:worker" + +docker: + file: Dockerfile + image: isscqa-playwright-image + +rootDir: ./ +reporters: + spotlight: # Prints an overview of failed or otherwise interesting jobs. + enabled: true +npm: + dependencies: + - "package.json" + diff --git a/playwright-tests/.sauceignore b/playwright-tests/.sauceignore new file mode 100644 index 00000000..18a59d43 --- /dev/null +++ b/playwright-tests/.sauceignore @@ -0,0 +1,16 @@ +# This file instructs saucectl to not package any files mentioned here. +.git/ +.github/ +.DS_Store +.hg/ +.vscode/ +.idea/ +.gitignore +.hgignore +.gitlab-ci.yml +.npmrc +*.gif +screenshots +artifacts +# Remove this to have node_modules uploaded with code +# node_modules/ diff --git a/playwright-tests/business-logic/data/ClientData.ts b/playwright-tests/business-logic/data/ClientData.ts new file mode 100644 index 00000000..12344da0 --- /dev/null +++ b/playwright-tests/business-logic/data/ClientData.ts @@ -0,0 +1,273 @@ +import { IClient } from "@business-logic/types/Client"; +import { IPaymentDetails } from "@business-logic/types/CustomerDetails"; +import { PaymentType } from "@business-logic/types/Enums"; + +const essentialClients: IClient[] = [ + { + clientTag: 'A8156D39-5943-4D3B-88C3-0D1A8810B51', + accountName: 'Acadia', + clientFlags: {} + }, + { + clientTag: '4fc3010a-ef5f-434d-93aa-7fdf4878d667', + accountName: 'AIG Private Client', + clientFlags: {} + }, + { + clientTag: '93FE1F55-19FA-4B7D-8F49-6E6C9F0E2B42', + accountName: 'Alfa Alliance', + clientFlags: {} + }, + { + clientTag: '1FC7999E-E0AC-484C-9AC9-267D1A2E5493', + accountName: 'American Family Insurance', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: '65FD47E0-F0DB-47FD-8CBB-E6F4F3BBF457', + accountName: 'Apparent Insurance', + clientFlags: {} + }, + { + clientTag: '009E3AE9-8E4E-40B7-9C7B-4516F640F10', + accountName: 'Berkley One', + clientFlags: {} + }, + { + clientTag: '0B3A3B02-5267-4302-A305-E742E1F1BFC5', + accountName: 'Branch Insurance', + clientFlags: {} + }, + { + clientTag: '7DF15A1A-0FD3-42D3-A6B1-26FB36550BDC', + accountName: 'Brethren Mutual', + clientFlags: {} + }, + { + clientTag: 'E2D26369-1739-402C-9A85-F5F748C72647', + accountName: 'Certainly', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: '394EF217-7882-4281-B4B8-FA11FDA7D500', + accountName: 'Continental Western', + clientFlags: {} + }, + { + clientTag: 'BF29B5A9-EFD8-4138-B301-CAD9C2495E74', + accountName: 'Donegal Mutual', + clientFlags: {} + }, + { + clientTag: 'FDEAB808-9922-4851-A692-7128ECF9F879', + accountName: 'Elephant Insurance', + clientFlags: {} + }, + { + clientTag: '42AC5AFB-0A08-4845-935A-9FA3A1BBF2D0', + accountName: 'Encompass', + clientFlags: {} + }, + { + clientTag: '1BED97B1-4593-4E09-AF1A-9DDD2CCAD0A4', + accountName: 'Farm Bureau', + clientFlags: {} + }, + { + clientTag: '05CC1609-3631-4044-B45A-E78E13343B9A', + accountName: 'Federated Insurance', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: 'C05C2F61-26EB-4849-99DD-0172EDA4C70F', + accountName: 'Fremont Insurance', + clientFlags: {} + }, + { + clientTag: 'B6911ACB-4B6A-4549-B674-0FC79AA6E782', + accountName: 'Grange Insurance Association', + clientFlags: {} + }, + { + clientTag: 'BDA2D606-D34F-4ED3-83DD-7BF56AC9C2CE', + accountName: 'GuideOne Insurance', + clientFlags: {} + }, + { + clientTag: '3CC58C8F-9207-44AF-B5F1-E1BA84CFADD1', + accountName: 'Hagerty', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: '6D13AEF6-CE16-400F-9A29-A1AED5C47D48', + accountName: 'Liberty Mutual', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: 'F680CCE4-6D88-4845-9FA4-746CF54B4AC', + accountName: 'M Plate', + clientFlags: {} + }, + { + clientTag: '988C2DCB-C95E-452D-9AE3-23B719FB3991', + accountName: 'Main Street America', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: '1873b283-a0b5-4d10-b112-b6bab94cd9dd', + accountName: 'Maine Mutual Group', + clientFlags: {} + }, + { + clientTag: 'A639E2E5-6347-4E38-92B3-37DEF73BE51A', + accountName: 'Merchants Insurance Group', + clientFlags: {} + }, + { + clientTag: '9B37A4B6-B4C2-4FCA-9E7C-FF8F0AB48067', + accountName: 'Midvale', + clientFlags: {} + }, + { + clientTag: 'F2617197-C39D-451C-A3D1-61C39BA0BD57', + accountName: 'M-Plate', + clientFlags: {} + }, + { + clientTag: 'f4730f4c-f83f-41de-a543-3ce761f66802', + accountName: 'Mutual Benefit', + clientFlags: {} + }, + { + clientTag: '1DE5F9C7-34F0-4115-9582-4BFDF89F8504', + accountName: 'Nationwide Insurance', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: 'EA776DDA-0686-43F3-B222-70F1531B9610', + accountName: 'Nationwide Private Client', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: 'D3AA7E5D-2184-4761-99C3-69ED8515916E', + accountName: 'Northstar', + clientFlags: {} + }, + { + clientTag: '8879E236-AFEA-4B46-A94D-06078B84B641', + accountName: 'OnStar Insurance', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: '29EF1D8A-C3AF-4037-8FDB-AC7DBD50B1B5', + accountName: 'Pioneer State Mutual', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: 'BBC83F09-82D7-492D-A8E8-64594215E559', + accountName: 'Preferred Mutual', + clientFlags: {} + }, + { + clientTag: 'D27EBC04-A912-4974-A9D1-E3EBC2B2575B', + accountName: 'QBE', + clientFlags: {} + }, + { + clientTag: 'A5F7D473-29C4-4E2C-AD08-A3AB13FE0314', + accountName: 'Safeco Insurance', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: '32978a45-dd2e-4fc6-b2ca-7178efb40373', + accountName: 'Secura', + clientFlags: {} + }, + { + clientTag: 'C471BDE5-7005-47C2-A544-933D988BCB7F', + accountName: 'Travelers Insurance', + clientFlags: { isTpaEnabled: true } + }, + { + clientTag: 'd6ba651f-1611-4f32-a6f6-0267e1968eed', + accountName: 'Utica', + clientFlags: { isAuthenticationEnabled: true} + }, + { + clientTag: '1065734B-2292-4392-8CAF-205F07752BBE', + accountName: 'Wayne Insurance', + clientFlags: {} + } +] + +const advancedClients: IClient[] = [ + { + clientTag: '1216EA5F-64D1-462A-A03F-34C0A652430E', + accountName: 'Liberty Mutual', + clientFlags: {} + } +] + +const defaultCreditCardDetails: IPaymentDetails = { + paymentType: PaymentType.Credit, + cardNumber: '4111111111111111', + expirationMonth: '12 - December', + expirationYear: '2029', + cvv: '555', + billingAddress: { + street: '2088 Tuller St', + city: 'Columbus', + state: 'OH', + postalCode: '43028', + country: 'US' + } +} + +const defaultAfterpayDetails: IPaymentDetails = { + paymentType: PaymentType.AfterPay, + username: 'itqatest@safelite.com', + password: 'Safelite1', + cardNumber: '4111 1111 1111 1111', + expirationMonth: '12', + expirationYear: '34', + cvv: '000' +} + +const defaultPaypalDetails: IPaymentDetails = { + paymentType: PaymentType.Paypal, + password: 'Safelite1' +} + +export default class ClientData { + static getEssentialClients() { + return essentialClients.sort(() => 0.5 - Math.random()); + } + + static getEssentialClientsWithTpaEnabled() { + return essentialClients.sort(() => 0.5 - Math.random()).filter(value => { + return value.clientFlags.isTpaEnabled; + }); + } + + static getEssentialClientsWithTpaDisabled() { + return essentialClients.sort(() => 0.5 - Math.random()).filter(value => { + return !value.clientFlags.isTpaEnabled; + }); + } + + static getAdvancedClients() { + return advancedClients; + } + + static getDefaultCreditCardDetails() { + return defaultCreditCardDetails; + } + + static getDefaultAfterpayDetails() { + return defaultAfterpayDetails; + } + + static getDefaultPaypalDetails() { + return defaultPaypalDetails; + } +} \ No newline at end of file diff --git a/playwright-tests/business-logic/data/MockPolicyData.ts b/playwright-tests/business-logic/data/MockPolicyData.ts new file mode 100644 index 00000000..d14fe805 --- /dev/null +++ b/playwright-tests/business-logic/data/MockPolicyData.ts @@ -0,0 +1,71 @@ +import { IPostSaveFakeResponseRequestBody } from "@business-logic/types/CcisApi"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; + +const policySoapByScenario: { [x: string]: string } = { + '0001a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2023-10-19T20:26:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2023-10-19T20:26:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:PolicyInqRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:TransactionResponseDt>2023-10-19T20:26:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-10-01 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>+${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>OR <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-21 <_xml:ExpirationDt>2099-03-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3102863830064 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>BMW <_xml:Model>740 <_xml:ModelYear>2020 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>OR <_xml:VehIdentificationNumber>WBA7T2C01LGL17632 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>250.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0002a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2023-12-05T13:23:39Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2023-12-05T13:23:39Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>45261f68-e90b-4547-af3e-12361d73996f <_xml:PolicyInqRs> <_xml:RqUID>45261f68-e90b-4547-af3e-12361d73996f <_xml:TransactionResponseDt>2023-12-05T13:23:39Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2016-01-22 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>secondary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>TX <_xml:ContractTerm> <_xml:EffectiveDt>2023-11-01 <_xml:ExpirationDt>2099-11-01 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3471321775119 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>CHRY <_xml:Model>300 <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>TX <_xml:VehIdentificationNumber>2C3KA53G06H407823 <_xml:VehRateGroupInfo> <_xml:RateGroup>036 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>50.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0003a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2025-01-09T13:34:06Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2025-01-09T13:34:06Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>5e567bbe-83e3-4b67-9723-38544441e881 <_xml:PolicyInqRs> <_xml:RqUID>5e567bbe-83e3-4b67-9723-38544441e881 <_xml:TransactionResponseDt>2025-01-09T13:34:06Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2016-01-22 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>secondary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>TX <_xml:ContractTerm> <_xml:EffectiveDt>2016-01-22 <_xml:ExpirationDt>2017-01-22 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3471321775119 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>CHEV <_xml:Model>SUBURBAN <_xml:ModelYear>2003 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>TX <_xml:VehIdentificationNumber>1GNEC16Z63J180454 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>50.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>2 <_xml:Manufacturer>CHRY <_xml:Model>300 <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>TX <_xml:VehIdentificationNumber>2C3KA53G06H407823 <_xml:VehRateGroupInfo> <_xml:RateGroup>036 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>50.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0004a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2025-01-30T13:28:30Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2025-01-30T13:28:30Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>c9ebcb14-a7e3-4b74-b6c2-f6252e456088 <_xml:PolicyInqRs> <_xml:RqUID>c9ebcb14-a7e3-4b74-b6c2-f6252e456088 <_xml:TransactionResponseDt>2025-01-30T13:28:30Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2022-07-26 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:OtherGivenName>M <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications /> <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:OtherGivenName>G <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications /> <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>secondary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>FL <_xml:ContractTerm> <_xml:EffectiveDt>2023-11-01 <_xml:ExpirationDt>2099-11-01 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3822587884076 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>DODG <_xml:Model>GRAND CARA <_xml:ModelYear>2012 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>FL <_xml:VehIdentificationNumber>2C4RDGBG8CR254922 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>2000.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>2 <_xml:Manufacturer>SBRU <_xml:Model>OUTBACK <_xml:ModelYear>2021 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>FL <_xml:VehIdentificationNumber>4S4BTAFC7M3163249 <_xml:VehRateGroupInfo> <_xml:RateGroup>005 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>2000.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0005a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2023-10-19T20:26:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2023-10-19T20:26:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:PolicyInqRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:TransactionResponseDt>2023-10-19T20:26:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-10-01 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>SC <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-21 <_xml:ExpirationDt>2099-03-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3102863830064 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>LINCOLN <_xml:Model>MKS <_xml:ModelYear>2014 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>SC <_xml:VehIdentificationNumber>1LNHL9DK2EG608557 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>0 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0006a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2023-10-19T20:26:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2023-10-19T20:26:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:PolicyInqRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:TransactionResponseDt>2023-10-19T20:26:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-10-01 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>KY <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-21 <_xml:ExpirationDt>2099-03-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3102863830064 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>TOYO <_xml:Model>CAMRY <_xml:ModelYear>2011 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>KY <_xml:VehIdentificationNumber>4T1BF3EK6BU168140 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>50.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0007a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2023-10-19T20:26:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2023-10-19T20:26:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:PolicyInqRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:TransactionResponseDt>2023-10-19T20:26:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-10-01 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>SC <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-21 <_xml:ExpirationDt>2099-03-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3102863830064 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>TOYO <_xml:Model>CAMRY <_xml:ModelYear>2011 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>SC <_xml:VehIdentificationNumber>4T1BF3EK6BU168140 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>50.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0008a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2023-10-19T20:26:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2023-10-19T20:26:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:PolicyInqRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:TransactionResponseDt>2023-10-19T20:26:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-10-01 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>SC <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-21 <_xml:ExpirationDt>2099-03-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3102863830064 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>VOLKSWAGEN <_xml:Model>GOLF <_xml:ModelYear>2019 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>SC <_xml:VehIdentificationNumber>3VWW57AU1KM032197 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>0 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0009a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2023-10-19T20:26:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2023-10-19T20:26:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:PolicyInqRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:TransactionResponseDt>2023-10-19T20:26:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-10-01 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>SC <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-21 <_xml:ExpirationDt>2099-03-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3102863830064 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>TOYO <_xml:Model>C-HR <_xml:ModelYear>2019 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>SC <_xml:VehIdentificationNumber>NMTKHMBX7KR081855 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>0 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">001 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0010a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2023-10-19T20:26:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2023-10-19T20:26:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:PolicyInqRs> <_xml:RqUID>d30c40e1-3a1e-4351-85b9-40abdc061fad <_xml:TransactionResponseDt>2023-10-19T20:26:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-10-01 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>SC <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-21 <_xml:ExpirationDt>2099-03-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3102863830064 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>TOYO <_xml:Model>C-HR <_xml:ModelYear>2019 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>SC <_xml:VehIdentificationNumber>NMTKHMBX7KR081855 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>1000.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">001 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0011a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2024-11-21T18:14:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2024-11-21T18:14:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>346e69fa-cc7e-4da3-be05-75191f5187c7 <_xml:PolicyInqRs> <_xml:RqUID>346e69fa-cc7e-4da3-be05-75191f5187c7 <_xml:TransactionResponseDt>2024-11-21T18:14:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-09-01 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>secondary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>AL <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-01 <_xml:ExpirationDt>2099-03-01 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3542759911464 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>CHEV <_xml:Model>SILVERADO <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>1GCEK19Z46Z159437 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>100.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>2 <_xml:Manufacturer>JEEP <_xml:Model>WRANGLER <_xml:ModelYear>2017 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>1C4BJWDG6HL624721 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>100.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>3 <_xml:Manufacturer>TOYO <_xml:Model>SIENNA <_xml:ModelYear>2001 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>4T3ZF13C51U390298 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>4 <_xml:Manufacturer>JEEP <_xml:Model>WRANGLER <_xml:ModelYear>2005 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>1J4FA49S95P359651 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>5 <_xml:Manufacturer>wild <_xml:Model>178bhfkx <_xml:ModelYear>2022 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>5ZT2WDGC0NG202493 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>0.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>6 <_xml:Manufacturer>HOND <_xml:Model>ELEMENT <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>5J6YH27746L026807 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>500.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">001 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0012a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2024-11-21T18:14:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2024-11-21T18:14:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>346e69fa-cc7e-4da3-be05-75191f5187c7 <_xml:PolicyInqRs> <_xml:RqUID>346e69fa-cc7e-4da3-be05-75191f5187c7 <_xml:TransactionResponseDt>2024-11-21T18:14:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-09-01 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>secondary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>AL <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-01 <_xml:ExpirationDt>2099-03-01 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3542759911464 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>CHEV <_xml:Model>SILVERADO <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>1GCEK19Z46Z159437 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>100.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>2 <_xml:Manufacturer>JEEP <_xml:Model>WRANGLER <_xml:ModelYear>2017 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>1C4BJWDG6HL624721 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>100.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>3 <_xml:Manufacturer>TOYO <_xml:Model>SIENNA <_xml:ModelYear>2001 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>4T3ZF13C51U390298 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>4 <_xml:Manufacturer>JEEP <_xml:Model>WRANGLER <_xml:ModelYear>2005 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>1J4FA49S95P359651 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>5 <_xml:Manufacturer>wild <_xml:Model>178bhfkx <_xml:ModelYear>2022 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>5ZT2WDGC0NG202493 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>0.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>6 <_xml:Manufacturer>HOND <_xml:Model>ELEMENT <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>5J6YH27746L026807 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>500.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">001 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0013a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2024-11-21T18:14:10Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2024-11-21T18:14:10Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>346e69fa-cc7e-4da3-be05-75191f5187c7 <_xml:PolicyInqRs> <_xml:RqUID>346e69fa-cc7e-4da3-be05-75191f5187c7 <_xml:TransactionResponseDt>2024-11-21T18:14:10Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-09-01 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>secondary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>AL <_xml:ContractTerm> <_xml:EffectiveDt>2023-03-01 <_xml:ExpirationDt>2099-03-01 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3542759911464 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>CHEV <_xml:Model>SILVERADO <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>1GCEK19Z46Z159437 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>100.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>2 <_xml:Manufacturer>JEEP <_xml:Model>WRANGLER <_xml:ModelYear>2017 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>1C4BJWDG6HL624721 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>100.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>3 <_xml:Manufacturer>TOYO <_xml:Model>SIENNA <_xml:ModelYear>2001 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>4T3ZF13C51U390298 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>4 <_xml:Manufacturer>JEEP <_xml:Model>WRANGLER <_xml:ModelYear>2005 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>1J4FA49S95P359651 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>5 <_xml:Manufacturer>wild <_xml:Model>178bhfkx <_xml:ModelYear>2022 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>5ZT2WDGC0NG202493 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>0.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>6 <_xml:Manufacturer>HOND <_xml:Model>ELEMENT <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>AL <_xml:VehIdentificationNumber>5J6YH27746L026807 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:Coverage> <_xml:CoverageCd>GLSS <_xml:CoverageDesc>ACV <_xml:Deductible> <_xml:FormatCurrencyAmt> <_xml:Amt>500.00 <_xml:Option> <_xml:OptionCd>V <_xml:OptionValue>1 <_xml:OptionValueDesc>COVERAGE_LIMIT_IND <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">001 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0014a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2024-07-02T12:23:46Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2024-07-02T12:23:46Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>1516164a-d60e-46b9-82d3-52324c25d381 <_xml:PolicyInqRs> <_xml:RqUID>1516164a-d60e-46b9-82d3-52324c25d381 <_xml:TransactionResponseDt>2024-07-02T12:23:46Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-08-01 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>PA <_xml:ContractTerm> <_xml:EffectiveDt>2023-05-01 <_xml:ExpirationDt>2099-05-01 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>3492840353261 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>TOYO <_xml:Model>C-HR <_xml:ModelYear>2019 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>PA <_xml:VehIdentificationNumber>NMTKHMBX5KR086519 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">001 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0015a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2024-12-11T01:13:55Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2024-12-11T01:13:55Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>157211b7-875b-419d-a5cc-267318f37377 <_xml:PolicyInqRs> <_xml:RqUID>157211b7-875b-419d-a5cc-267318f37377 <_xml:TransactionResponseDt>2024-12-11T01:13:55Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2019-11-20 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications /> <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>VA <_xml:ContractTerm> <_xml:EffectiveDt>2019-09-27 <_xml:ExpirationDt>2099-09-27 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>998891142443758 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>SBRU <_xml:Model>FORESTER <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>VA <_xml:VehIdentificationNumber>JF1SG63616B121212 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0016a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2025-01-13T18:01:11Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2025-01-13T18:01:11Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>5da3e764-374a-41e0-9baf-9fe76bbc4a6e <_xml:PolicyInqRs> <_xml:RqUID>5da3e764-374a-41e0-9baf-9fe76bbc4a6e <_xml:TransactionResponseDt>2025-01-13T18:01:11Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-01-01 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>NH <_xml:ContractTerm> <_xml:EffectiveDt>2022-12-21 <_xml:ExpirationDt>2099-12-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>998411168732470 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>SBRU <_xml:Model>WRX <_xml:ModelYear>2021 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>NH <_xml:VehIdentificationNumber>JF1VA1A63M9801802 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0017a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2024-12-11T01:13:55Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2024-12-11T01:13:55Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>157211b7-875b-419d-a5cc-267318f37377 <_xml:PolicyInqRs> <_xml:RqUID>157211b7-875b-419d-a5cc-267318f37377 <_xml:TransactionResponseDt>2024-12-11T01:13:55Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2019-11-20 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications /> <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>VA <_xml:ContractTerm> <_xml:EffectiveDt>2019-09-27 <_xml:ExpirationDt>2099-09-27 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>998891142443758 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>SBRU <_xml:Model>FORESTER <_xml:ModelYear>2006 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>VA <_xml:VehIdentificationNumber>JF1SG63616B121212 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0018a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2023-12-11T21:27:05Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2023-12-11T21:27:05Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>df38d1a8-f583-4ee6-a23d-950c1930c150 <_xml:PolicyInqRs> <_xml:RqUID>df38d1a8-f583-4ee6-a23d-950c1930c150 <_xml:TransactionResponseDt>2023-12-11T21:27:05Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-01-01 <_xml:Requestor id= \"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id= \"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id= \"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>F <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>CT <_xml:ContractTerm> <_xml:EffectiveDt>2022-12-21 <_xml:ExpirationDt>2099-12-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>998281163922817 <_xml:Location> <_xml:ItemIdInfo id= \"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>SBRU <_xml:Model>Outback <_xml:ModelYear>2021 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>CT <_xml:VehIdentificationNumber>4S4BTAFC7M3163249 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:RemarkText id= \"endorsementId_1\" IdRef= \"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0019a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2025-01-13T18:01:11Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2025-01-13T18:01:11Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>5da3e764-374a-41e0-9baf-9fe76bbc4a6e <_xml:PolicyInqRs> <_xml:RqUID>5da3e764-374a-41e0-9baf-9fe76bbc4a6e <_xml:TransactionResponseDt>2025-01-13T18:01:11Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-01-01 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>NH <_xml:ContractTerm> <_xml:EffectiveDt>2022-12-21 <_xml:ExpirationDt>2099-12-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>998411168732470 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>SBRU <_xml:Model>WRX <_xml:ModelYear>2021 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>NH <_xml:VehIdentificationNumber>JF1VA1A63M9801802 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ', + '0020a': ' <_xml:ACORD> <_xml:SignonRs> <_xml:CustId> <_xml:SPName>Safelite <_xml:CustPermId>00005 <_xml:ClientDt>2025-01-13T18:01:11Z <_xml:CustLangPref>EN <_xml:ClientApp> <_xml:Org>Liberty Mutual <_xml:Name>PM CNG <_xml:Version>1.0 <_xml:ServerDt>2025-01-13T18:01:11Z <_xml:Language>EN <_xml:InsuranceSvcRs> <_xml:RqUID>5da3e764-374a-41e0-9baf-9fe76bbc4a6e <_xml:PolicyInqRs> <_xml:RqUID>5da3e764-374a-41e0-9baf-9fe76bbc4a6e <_xml:TransactionResponseDt>2025-01-13T18:01:11Z <_xml:MsgStatus> <_xml:MsgStatusCd>Success <_xml:AsOfDt>2023-01-01 <_xml:Requestor id=\"RequestorId_1\" /> <_xml:PartyInqInfo> <_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\" /> <_xml:PolInfo> <_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"> <_xml:InsuredOrPrincipal> <_xml:GeneralPartyInfo> <_xml:NameInfo> <_xml:PersonName> <_xml:Surname>${lastName} <_xml:GivenName>${firstName} <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:Communications> <_xml:PhoneInfo> <_xml:PhoneTypeCd>Home <_xml:PhoneNumber>${phoneNumber} <_xml:InsuredOrPrincipalInfo> <_xml:InsuredOrPrincipalRoleCd>primary <_xml:PersonInfo> <_xml:GenderCd>M <_xml:PersPolicy> <_xml:PolicyNumber>${policyNumber} <_xml:PolicyVersion>GRS <_xml:CompanyProductCd>liberty <_xml:LOBCd>AUTOP <_xml:ControllingStateProvCd>NH <_xml:ContractTerm> <_xml:EffectiveDt>2022-12-21 <_xml:ExpirationDt>2099-12-21 <_xml:GroupId>000 <_xml:MiscParty> <_xml:ItemIdInfo> <_xml:InsurerId>998411168732470 <_xml:Location> <_xml:ItemIdInfo id=\"ItemIdInfoId_1\" /> <_xml:Addr> <_xml:Addr1>${streetAddress} <_xml:City>${city} <_xml:StateProvCd>${state} <_xml:PostalCode>${postalCode} <_xml:Country>US <_xml:PersAutoLineBusiness> <_xml:LOBCd>AUTOP <_xml:PersVeh> <_xml:ItemIdInfo> <_xml:InsurerId>1 <_xml:Manufacturer>SBRU <_xml:Model>WRX <_xml:ModelYear>2021 <_xml:Registration> <_xml:RegistrationId>UNKNOWN <_xml:StateProvCd>NH <_xml:VehIdentificationNumber>JF1VA1A63M9801802 <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>service_level_ind <_xml:VehRateGroupInfo> <_xml:RateGroup>000 <_xml:CoverageCd>parking_guard <_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">000 <_xml:PolicySummaryInfo> <_xml:PolicyStatusCd>ACTIVE ' +} + +const claimRegistrationValue = "{\"claimNumber\":\"058913992\",\"reportedDate\":\"2023-11-01T18:56:08.264Z\",\"howReported\":\"digital\",\"status\":\"registered\",\"lossCategory\":\"glassOnly\",\"lossCause\":\"glassOnly\",\"lossCauseDetail\":null,\"lossDate\":\"2023-11-01\",\"lossTime\":\"00:00\",\"lossDescription\":\"ROCK FROM ROAD - NO ONE AT FAULT\",\"lossLocation\":{\"primary\":true,\"line1\":null,\"line2\":null,\"city\":\"Columbus\",\"county\":null,\"state\":\"OR\",\"postalCode\":\"\",\"country\":\"US\",\"type\":null,\"locationName\":null,\"subType\":null},\"_links\":{\"reporter\":{\"id\":\"65429f485e8cd37072a5bb5d\",\"href\":\"claims/058913992/contacts/65429f485e8cd37072a5bb5d\",\"title\":\"BRANDON JOHNSON\"},\"primaryContact\":null,\"insureds\":[{\"id\":\"65429f485e8cd37072a5bb5d\",\"href\":\"claims/058913992/contacts/65429f485e8cd37072a5bb5d\",\"title\":\"BRANDON JOHNSON\"}],\"pedestrianCyclists\":[],\"contacts\":{\"id\":null,\"href\":\"claims/058913992/contacts\"},\"claimDamage\":{\"id\":null,\"href\":\"claims/058913992/claim-damage\"},\"vehicleIncidents\":[{\"id\":\"65429f4b31209b23b788c38e\",\"href\":\"claims/058913992/vehicle-incidents/65429f4b31209b23b788c38e\",\"vehicle\":\"BMW740\"}],\"propertyIncidents\":{\"dwelling\":null,\"otherStructure\":null,\"personalProperty\":null,\"livingExpense\":null},\"injuryIncidents\":[]}}"; + +export default class MockPolicyData { + + static interpolatePolicyDetails(policySoap: string, customerDetails: ICustomerDetails, policyNumber: string) { + return policySoap.replaceAll('${firstName}', customerDetails.firstName) + .replaceAll('${lastName}', customerDetails.lastName) + .replaceAll('${streetAddress}', customerDetails.address.street) + .replaceAll('${city}', customerDetails.address.city) + .replaceAll('${state}', customerDetails.address.state) + .replaceAll('${postalCode}', customerDetails.address.postalCode) + .replaceAll('${policyNumber}', policyNumber) + .replaceAll('${phoneNumber}', customerDetails.phoneNumber); + } + + static generateCreateMockPolicyRequest(policyNumber: string, soapValue: string) { + const request: IPostSaveFakeResponseRequestBody = { + accountNumber: '550036', // TODO: Update when we have more than just liberty mutual + responseType: 'Policy', + key: policyNumber, + value: soapValue + } + return request; + } + + static generateCreateClaimRegistrationRequest(policyNumber: string) { + const request: IPostSaveFakeResponseRequestBody = { + accountNumber: '550036', // TODO: Update when we have more than just liberty mutual + responseType: 'ClaimRegistration', + key: policyNumber, + value: claimRegistrationValue + } + + return request; + } + + static getPolicySoapByScenario(scenarioNumber: string, customerDetails: ICustomerDetails, policyNumber: string) { + return this.interpolatePolicyDetails(policySoapByScenario[scenarioNumber], customerDetails, policyNumber); + } + + static getNoCompPolicySoap(customerDetails: ICustomerDetails, policyNumber: string) { + const noCompPolicyValue = `<_xml:ACORD><_xml:SignonRs><_xml:CustId><_xml:SPName>Safelite<_xml:CustPermId>00005<_xml:ClientDt>2024-08-20T13:23:48Z<_xml:CustLangPref>EN<_xml:ClientApp><_xml:Org>Liberty Mutual<_xml:Name>PM CNG<_xml:Version>1.0<_xml:ServerDt>2024-08-20T13:23:48Z<_xml:Language>EN<_xml:InsuranceSvcRs><_xml:RqUID>ce3e4434-4905-4b76-b8b0-d0b1caa516cd<_xml:PolicyInqRs><_xml:RqUID>ce3e4434-4905-4b76-b8b0-d0b1caa516cd<_xml:TransactionResponseDt>2024-08-20T13:23:48Z<_xml:MsgStatus><_xml:MsgStatusCd>Success<_xml:AsOfDt>2019-11-20<_xml:Requestor id=\"RequestorId_1\"/><_xml:PartyInqInfo><_xml:InsuredOrPrincipal id=\"InsuredOrPrincipalId_1\"/><_xml:PolInfo><_xml:PersAutoPolicy id=\"PersAutoPolicyID_1\"><_xml:InsuredOrPrincipal><_xml:GeneralPartyInfo><_xml:NameInfo><_xml:PersonName><_xml:Surname>${customerDetails.lastName}<_xml:GivenName>${customerDetails.firstName}<_xml:Addr><_xml:Addr1>${customerDetails.address.street}<_xml:City>${customerDetails.address.city}<_xml:StateProvCd>${customerDetails.address.state}<_xml:PostalCode>${customerDetails.address.postalCode}<_xml:Country>US<_xml:Communications/><_xml:InsuredOrPrincipalInfo><_xml:InsuredOrPrincipalRoleCd>primary<_xml:PersonInfo><_xml:GenderCd>M<_xml:PersPolicy><_xml:PolicyNumber>${policyNumber}<_xml:PolicyVersion>GRS<_xml:CompanyProductCd>liberty<_xml:LOBCd>AUTOP<_xml:ControllingStateProvCd>VA<_xml:ContractTerm><_xml:EffectiveDt>2000-09-27<_xml:ExpirationDt>2099-09-27<_xml:GroupId>000<_xml:MiscParty><_xml:ItemIdInfo><_xml:InsurerId>998891142443758<_xml:Location><_xml:ItemIdInfo id=\"ItemIdInfoId_1\"/><_xml:Addr><_xml:Addr1>${customerDetails.address.street}<_xml:City>${customerDetails.address.city}<_xml:StateProvCd>${customerDetails.address.state}<_xml:PostalCode>${customerDetails.address.postalCode}<_xml:Country>US<_xml:PersAutoLineBusiness><_xml:LOBCd>AUTOP<_xml:PersVeh><_xml:ItemIdInfo><_xml:InsurerId>1<_xml:Manufacturer>SBRU<_xml:Model>FORESTER<_xml:ModelYear>2006<_xml:Registration><_xml:RegistrationId>UNKNOWN<_xml:StateProvCd>VA<_xml:VehIdentificationNumber>JF1SG63616B121212<_xml:VehRateGroupInfo><_xml:RateGroup>000<_xml:CoverageCd>service_level_ind<_xml:VehRateGroupInfo><_xml:RateGroup>000<_xml:CoverageCd>parking_guard<_xml:RemarkText id=\"endorsementId_1\" IdRef=\"PersAutoPolicyID_1\">000<_xml:PolicySummaryInfo><_xml:PolicyStatusCd>ACTIVE` + return noCompPolicyValue; + } +} \ No newline at end of file diff --git a/playwright-tests/business-logic/rules/RuleEngineBuiltins.ts b/playwright-tests/business-logic/rules/RuleEngineBuiltins.ts new file mode 100644 index 00000000..cf28d615 --- /dev/null +++ b/playwright-tests/business-logic/rules/RuleEngineBuiltins.ts @@ -0,0 +1,70 @@ +//import { AircraftTypes, AssignmentTypes, EnhancementBundleOptions, EnhancementOptions, ModificationTypes, OpportunityTypes, ProductAttributes, ProgramTypes, TransactionSubTypes, TwentyFiveHrLeaseProducts } from "@business-logic/types/Enums"; +import TestCase from "@business-logic/types/TestCase"; +import { Rule } from "../types/RuleEngine"; +import EnumUtils from "../../impl/utils/EnumUtils"; +//import { validateEnhancementOptions, validateProductAttributes } from "./ApplicableModifications"; +//import TransactionData from "@business-logic/types/TransactionData"; + +export enum BuiltInRules { + TransactionExists = 1000, + PricingExists = 1001, + TerminationIsMod = 1002, + TerminationHasReason = 1003, + AircraftRequirement = 1004, + ModificationTypeAssignment = 1005, + ModificationTypeTrade = 1006, + TwentyFiveHourLeaseSpecialty = 1007, + TransactionPremiumOrNonPremiumOnly = 1008, + OldTransactionPremiumOrNonPremiumOnly = 1009, + PremiumSelectionRequirement = 1010, + OldTransactionRequiresPremiumSelection = 1011, + TerminationAndRepurchaseAdjustmentAmount = 1012, + ProgramSpecificAttributes = 1013, + ProgramSpecificEnhancements = 1014, + InterimLeaseHasAircraftRate = 1015, + ShareBinderNoPremiumSelection = 1016, + TwentyFiveHourLeaseMustDefineTwentyFiveHourProduct = 1017, + PartialAssignmentHoursToBeAssigned = 1018, + MinicartLineItemsCannotBeEmpty = 1019, + WaiveInternationalFeesValue = 1020, + EarlyOutOptionTimeframeAndFeeStatus = 1021, + DelayedStartDateOffsetRequiresValue = 1020, + EnhancementsRequireAValue = 1021, + NonPremiumMMFIncentiveRequirements = 1022, + QSExecutiveRequirements = 1023 +} + +// Built-Ins shouldn't depend on other rules, custom rules however are supposed to depend on them +export const builtInRules: Rule[] = +[ +//{ +// id: BuiltInRules.TransactionExists, +// name: "Transaction Exists Rule", +// check: (testCase: TestCase) => { +// return testCase.transaction != null; +// } +// }, { +// id: BuiltInRules.PricingExists, +// name: "Pricing must exist on Transaction Rule", +// check: (testCase: TestCase) => { +// return testCase.transaction != null && testCase.transaction.pricing != null; +// } +// }, { +// id: BuiltInRules.TerminationIsMod, +// name: "OpportunityType Termination requires TransactionSubType Mod", +// check: (testCase: TestCase) => { + +// // Check old transactions +// for (var transaction of testCase.oldTransactions) { +// if (transaction.opportunityType == OpportunityTypes.Termination) +// return transaction.subType == TransactionSubTypes.Mod; +// } + +// // Check transaction +// if (testCase.transaction.opportunityType == OpportunityTypes.Termination) +// return testCase.transaction.subType == TransactionSubTypes.Mod; +// return true; +// }, +// dependsOn: [BuiltInRules.TransactionExists] +// }, { +]; \ No newline at end of file diff --git a/playwright-tests/business-logic/types/Authentication.ts b/playwright-tests/business-logic/types/Authentication.ts new file mode 100644 index 00000000..4e29daf2 --- /dev/null +++ b/playwright-tests/business-logic/types/Authentication.ts @@ -0,0 +1,40 @@ +import { Authentication, CertificateType, SignatureAlgorithm, SubType } from "./Enums" + +export interface IClientSignatureRequest { + clientTag: string, + token: string, + certificateFileName: string, + certificateKey: string, + certificateAlgorithm: SignatureAlgorithm, + certificateType: CertificateType +} + +export interface IClientSignatureResponse { + signature: string, + encrypted: string +} + +export interface ICertificateInfo { + name: string; + key: string; + type: CertificateType; + algorithm: SignatureAlgorithm; +} + +interface IClientAuthenticationFlags { + claimRegistrationRequired: boolean; + tpaEnabled: boolean; + clientDisplayName: string; +} + +export interface IClientAuthentication { + clientTag: string; + accountName: string; + accountNumber: string; + active: boolean; + authentication: Authentication; + certificateInfo: ICertificateInfo[]; + clientFlags: IClientAuthenticationFlags; + parameters: string[]; + subType: SubType; +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/CcisApi.ts b/playwright-tests/business-logic/types/CcisApi.ts new file mode 100644 index 00000000..452cf760 --- /dev/null +++ b/playwright-tests/business-logic/types/CcisApi.ts @@ -0,0 +1,12 @@ +export interface IPostSaveFakeResponseRequestBody { + accountNumber: string, + responseType: string, + key: string, + value: string +} + +export interface IDeleteFakeResponseParams { + accountNumber: string, + key: string, + responseType: string +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/Client.ts b/playwright-tests/business-logic/types/Client.ts new file mode 100644 index 00000000..37ffcd0a --- /dev/null +++ b/playwright-tests/business-logic/types/Client.ts @@ -0,0 +1,10 @@ +export interface IClient { + clientTag: string, + accountName: string + clientFlags: Partial +} + +export interface IClientFlags { + isTpaEnabled: boolean; + isAuthenticationEnabled: boolean; +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/CustomerDetails.ts b/playwright-tests/business-logic/types/CustomerDetails.ts new file mode 100644 index 00000000..0c1be71a --- /dev/null +++ b/playwright-tests/business-logic/types/CustomerDetails.ts @@ -0,0 +1,81 @@ +import { DamageLocation, DamageSubLocation, DamageType as DamageCause, WindshieldDamage, ServiceLocation, EndorsementType, VehicleLookupType, PartQuestionType, PaymentType } from "./Enums"; +import { IAddress } from "./IAddress"; + +export interface ICustomerDetails { + firstName: string, + lastName: string, + email: string, + phoneNumber: string, + notes: string, + address: IAddress, + apptDate?: string, + packagePrice?: String +} + +export interface IClaimDetails { + policyNumber: string, + policyDeductible: number, + damageDate: string, + damageCause: DamageCause +} + +export interface IEndorsementDetails { + endorsementType: EndorsementType, + isOnPolicy: boolean, // Should we expect this endorsement to appear? + isClickYes: boolean // Should we click Yes or No? +} + +export interface IVehicleDetails { + year: string, + make: string, + model: string, + style?: string, + vin?: string, + licensePlateNumber?: string, + licensePlateState?: string, + vehicleLookupType?: VehicleLookupType, +} + +export interface IAppointmentDetails { + serviceLocation: ServiceLocation, + appointmentDate?: Date, + shopAddress?: string, // Used for in-shop + serviceAddress?: IAddress, // Used for mobile + isVehicleProtected?: boolean // Used for mobile + alternateServiceZip?: string // Used for in-shop and drop-off if doing service in a different ZIP +} + +export interface IPartQuestion { + isOnPage: boolean, + optionToSelect: string, + partQuestionType: PartQuestionType, + secondaryQuestionOptionToSelect?: string +} + +export interface IPaymentDetails { + paymentType: PaymentType, + username?: string, + password?: string, + cardNumber?: string, + expirationMonth?: string, + expirationYear?: string, + cvv?: string, + billingAddress?: IAddress, +} + +// export interface IVehicleDamage { +// isRearWindowDamage?: boolean, +// windshieldDamage?: WindshieldDamage, +// windowDamage?: IWindowDamage +// } + +// interface ISideDoorDamage { +// isFrontDoor: boolean, +// isBackDoor: boolean, +// isQuarterPanel: boolean +// } + +// export interface IWindowDamage { +// driverSideDamage?: ISideDoorDamage, +// passengerSideDamage?: ISideDoorDamage +// } \ No newline at end of file diff --git a/playwright-tests/business-logic/types/DigitalApi.ts b/playwright-tests/business-logic/types/DigitalApi.ts new file mode 100644 index 00000000..7317639d --- /dev/null +++ b/playwright-tests/business-logic/types/DigitalApi.ts @@ -0,0 +1,38 @@ +export interface IPartsOrQuestionsResponse { + partsOrQuestions: IPartOrQuestion[] +} + +export interface IPartOrQuestion { + glassPiece: IGlassPiece, + parts: IPart[], + partQuestions: IPartQuestion[] | null; +} + +export interface IGlassPiece { + name: string, + location: string +} + +export interface IPart { + childPartQuestions: IPartQuestion[], + basePartNumber: string, + safelitePartNumber: string, + color: string, + requiresRecalibration: boolean, + recalibrationType: null, // TODO: Add types + canSafeliteRecalibrate: boolean, + requiresCapabilityQuestions: boolean, + childParts: IChildPart[], + partNumber: string, + description: string, + partType: string +} + +export interface IPartQuestion { + // TODO: Define +} + +export interface IChildPart { + partNumber: string, + safelitePartNumber: string +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/Enums.ts b/playwright-tests/business-logic/types/Enums.ts new file mode 100644 index 00000000..5221d0ea --- /dev/null +++ b/playwright-tests/business-logic/types/Enums.ts @@ -0,0 +1,169 @@ +export enum DamageLocation {} + +export enum DamageSubLocation {} + +export enum DamageType { + Rock = 'Rock from road', + Vandalism = 'Vandalism', + Theft = 'Attempted theft or theft', + Hail = 'Hailstorm', + HurricaneStorm = 'Hurricane/storm', + Collision = 'Collision', + Object = 'Object hit glass', + Other = 'Other/unknown' +} + +export enum ServiceLocation {} + +export enum ServicePackage { + GlassOnly = 'Glass service only', + Standard = 'Standard', + Premium = 'Premium' +} + +export enum EndorsementType { + Educator = '01', + EmployeeParking = '03' +} + +export enum ConsoleColor { + Default = "", + Red = "\x1b[31m", + Green = "\x1b[32m", + Yellow = "\x1b[33m", + Orange = "\x1b[202m", + Blue = "\x1b[34m", + Magenta = "\x1b[35m", + Cyan = "\x1b[36m", + Reset = "\x1b[0m" +} + +export enum ResultTypes { + None = 0, + Skipped = 1, + Success = 2, + Failure = 3 +} + +export enum VehicleDamage { + WindshieldOneChip, + WindshieldTwoChips, + WindshieldThreeChips, + WindshieldCrack, + RearWindow, + DriverFrontDoor, + DriverRearDoor, + DriverVentGlass, + DriverQuarterPanel, + DriverSlidingDoor, + PassengerFrontDoor, + PassengerRearDoor, + PassengerVentGlass, + PassengerQuarterPanel +} + +export enum VehicleLookupType { + Vin, + Address, + LicensePlateNumber +} + +export enum ServiceLocation { + Mobile, + InShop, + DropOff +} + +export enum PartQuestionType { + WindshieldColor = 'Windshield-Single', + DriverFrontColor = 'Driver-Front', + DriverQuarterColor = 'Driver-Quarter', + DriverRearColor = 'Driver-Back', + DriverVentColor = 'Driver-Vent', + PassengerFrontColor = 'Passenger-Front', + PassengerQuarterColor = 'Passenger-Quarter', + PassengerRearColor = 'Passenger-Back', + PassengerVentColor = 'Passenger-Vent', + RearWindowColor = 'Rear-Stationary', + LeatherSeats = 'question-0-1', + DriverSideColor = 'Driver-SideDoor', + LaneKeepAssist = 'question-0-1' +} + +export enum PaymentType{ + Credit = "Credit", + AfterPay = "AfterPay", + Paypal = "Paypal", + PayAtService = "Pay at Service" +} + +// Enums from original ISSCQA Project +// TODO: re-evaluate +export enum WindshieldDamage{ + Crack, + OneChip, + TwoChips, + ThreeChips +} + +export enum NumChips{ + One = 1, + Two, + Three +} + +export enum AppointmentType{ + Inshop = "Inshop", + Mobile = "Mobile", + DropOff = "Drop-off" +} + +export enum ServiceProvider{ + Safelite = "Safelite", + ThirdParty = "Third Party" +} + +export enum SideDoorDamage{ + Passenger = "Passenger", + Driver = "Driver" +} + +export enum BailoutCode { + Unknown = 0, + SaveSessionError, + VehicleNotFound, + VehicleLookupError, + CoverageStatementInvalidState, + DoNotSeeMyShop, + PricingResponseError, + TPANotEnabled, + RequestCallback, + HeavyTruckVehicle, + NoPartsAvailable, + PartsServiceError, + SafeliteNotTheProvider +} + +export enum SignatureAlgorithm { + SHA1, + SHA256, + SHA512 +} + +export enum CertificateType { + RSA +} + +export enum SubType { + Advanced = 'Advanced', + Essential = 'Essential', + Unknown = 'Unknown' +} + +export enum Authentication { + None = 'None', + RSAToken = 'RSAToken', + RSATokenEncParams = 'RSATokenEncParams', + RSATokenEncParamsOneTimeUse = 'RSATokenEncParamsOneTimeUse', + Unknown = 'Unknown', +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/FrameworkConfig.ts b/playwright-tests/business-logic/types/FrameworkConfig.ts new file mode 100644 index 00000000..92409c2b --- /dev/null +++ b/playwright-tests/business-logic/types/FrameworkConfig.ts @@ -0,0 +1,11 @@ + +type FrameworkConfig = { + // company: NetJetsCompanies; + // companyString: string; + // currency: CurrencyTypes; + createResources: boolean; + destroyResources: boolean; + maxAllotmentHours: number; +}; + +export default FrameworkConfig; \ No newline at end of file diff --git a/playwright-tests/business-logic/types/IAddress.ts b/playwright-tests/business-logic/types/IAddress.ts new file mode 100644 index 00000000..2e0b0fd8 --- /dev/null +++ b/playwright-tests/business-logic/types/IAddress.ts @@ -0,0 +1,7 @@ +export interface IAddress { + street: string, + city: string, + state: string, + postalCode: string, + country: string +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/IBailoutFlags.ts b/playwright-tests/business-logic/types/IBailoutFlags.ts new file mode 100644 index 00000000..790d4209 --- /dev/null +++ b/playwright-tests/business-logic/types/IBailoutFlags.ts @@ -0,0 +1,11 @@ +export default interface IBailoutFlags { + isVehicleSelectBailout: boolean, + isDoNotSeeMyShopBailout: boolean, + isTpaNotEnabledBailout: boolean, + isRequestCallbackBailout: boolean, + isHeavyTruckVehicleBailout: boolean, + isPartsServiceErrorBailout: boolean, + isSafeliteNotTheProviderBailout: boolean, + isVehicleLookupBailout: boolean, + isPriceServiceErrorBailout: boolean +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/IDisposable.ts b/playwright-tests/business-logic/types/IDisposable.ts new file mode 100644 index 00000000..427581e2 --- /dev/null +++ b/playwright-tests/business-logic/types/IDisposable.ts @@ -0,0 +1,29 @@ +import LoggingUtils from "@impl/utils/LoggingUtils"; +import { ConsoleColor } from "@business-logic/types/Enums"; +import TestCase from "@business-logic/types/TestCase"; + +export interface IDisposable { + disposeAll(): void; + setupAll(): void; +} + +export abstract class DisposableBase implements IDisposable { + protected abstract setup(): Promise; + protected abstract dispose(): Promise; + + public async setupAll(): Promise { + if (!TestCase.FrameworkConfig.destroyResources) { + LoggingUtils.log(TestCase.Constants.CREATION_HALTED, ConsoleColor.Yellow); + return; + } + await this.setup(); + } + + public async disposeAll(): Promise { + if (!TestCase.FrameworkConfig.destroyResources || !TestCase.FrameworkConfig.createResources) { + LoggingUtils.log(TestCase.Constants.DISPOSE_HALTED, ConsoleColor.Yellow); + return; + } + await this.dispose(); + } +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/ITestCase.ts b/playwright-tests/business-logic/types/ITestCase.ts new file mode 100644 index 00000000..0dedc07c --- /dev/null +++ b/playwright-tests/business-logic/types/ITestCase.ts @@ -0,0 +1,16 @@ +import ITestPages from "@business-logic/types/ITestPages"; +import Validations from "./Validations"; +import { ITestData } from "./ITestData"; + +export default interface ITestCase { + readonly testID?: string; + readonly name: string; + readonly tags: string[]; + + readonly validations?: Validations; + readonly tempData?: any[] + + readonly testData: Partial; + + pages?: ITestPages; +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/ITestData.ts b/playwright-tests/business-logic/types/ITestData.ts new file mode 100644 index 00000000..49c1ed7b --- /dev/null +++ b/playwright-tests/business-logic/types/ITestData.ts @@ -0,0 +1,38 @@ +import { ServicePackage, VehicleDamage } from "./Enums" +import { IAppointmentDetails, IClaimDetails, ICustomerDetails, IEndorsementDetails, IPartQuestion, IPaymentDetails, IVehicleDetails } from "./CustomerDetails" +import IBailoutFlags from "./IBailoutFlags" +import { IPart } from "./DigitalApi" + +export interface ITestData { + isMockTesting: boolean, + clientTag: string, + isDuplicateClaim: boolean, + isPolicyFound: boolean, // Effective difference between advanced and essential + isUseVehicleOnPolicy: boolean, // Should we use the vehicle on the policy? + isNoComp: boolean, // Is this a NoComp policy? + isItac: boolean, // Is this an ITAC scenario? + hasStateLawPopup: boolean, // Are we expecting a state law pop-up on ProviderSelectionPage? + hasOemEndorsement: boolean, // OEM Endorsement does not appear on endorsements page, so it has a separate flag. + hasMilitaryWarning: boolean, // Are we expecting military base warning on the Service Location page? + bailoutFlags: Partial, + endorsements: IEndorsementDetails[], + isReplace: boolean, + partQuestions: IPartQuestion[], // part-questions page has unrelated part questions like leather seats + vehiclePartQuestions: IPartQuestion[], // vehicle-parts page usually has glass question + capabilityQuestions: IPartQuestion[], // Capability questions page usually has questions about autonomous driving features + policySoap: string, // Include policy soap if you want to create a mock policy + isSafelite: boolean, + servicePackage: ServicePackage, + customerDetails: ICustomerDetails, + claimDetails: IClaimDetails, + vehicleDetails: IVehicleDetails, + editVehicleDetails: IVehicleDetails, // Vehicle details entered after clicking "Edit vehicle" on Vehicle Details page + otherVehiclesOnPolicy: IVehicleDetails[], // IF defined, we validate that the vehicles are present. + vehicleDamage: VehicleDamage[], // Array of vehicle damage + appointmentDetails: IAppointmentDetails, + paymentDetails: IPaymentDetails // Payment information + isRecalNotification: boolean, + isRecalWarning: boolean, + isSeparateApptsWarning: boolean, // IF true, check for the separate appts warning on VehicleDamagePage + isAuthenticationRequired: boolean +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/ITestPages.ts b/playwright-tests/business-logic/types/ITestPages.ts new file mode 100644 index 00000000..58729752 --- /dev/null +++ b/playwright-tests/business-logic/types/ITestPages.ts @@ -0,0 +1,61 @@ +import { BailoutPage } from "../../pages/BailoutPage"; +import CapabilityQuestionsPage from "../../pages/CapabilityQuestionsPage"; +import { ContactConfirmationPage } from "../../pages/ContactConfirmationPage"; +import { ContactDetailsPage } from "../../pages/ContactDetailsPage"; +import { CoverageStatementPage } from "../../pages/CoverageStatementPage"; +import { DuplicateCheckPage } from "../../pages/DuplicateCheckPage"; +import { EndorsementsPage } from "../../pages/EndorsementsPage"; +import { OrderConfirmationPage } from "../../pages/OrderConfirmationPage"; +import { PartQuestionsPage } from "../../pages/PartQuestionsPage"; +import { PaymentMethodPage } from "../../pages/PaymentMethodPage"; +import { PaymentPage } from "../../pages/PaymentPage"; +import { PaypalPage } from "../../pages/PaypalPage"; +import { PolicyHolderDetailsPage } from "../../pages/PolicyHolderDetailsPage"; +import { PolicyVehiclesPage } from "../../pages/PolicyVehiclesPage"; +import { ProviderPreferencePage } from "../../pages/ProviderPreferencePage"; +import { SchedulePage } from "../../pages/SchedulePage"; +import { ServiceLocationPage } from "../../pages/ServiceLocationPage"; +import { ServicePackagesPage } from "../../pages/ServicePackagesPage"; +import { TpaConfirmationPage } from "../../pages/TpaConfirmationPage"; +import { TpaSearchPage } from "../../pages/TpaSearchPage"; +import { TpaSubmitPage } from "../../pages/TpaSubmitPage"; +import { VehicleDamagePage } from "../../pages/VehicleDamagePage"; +import { VehicleLookupAddressPage } from "../../pages/VehicleLookupAddressPage"; +import { VehicleLookupLicensePage } from "../../pages/VehicleLookupLicensePage"; +import { VehicleLookupPage } from "../../pages/VehicleLookupPage"; +import VehiclePartQuestionsPage from "../../pages/VehiclePartsPage"; +import { VehicleSelectionPage } from "../../pages/VehicleSelectionPage"; +import { VinLookupPage } from "../../pages/VinLookupPage"; +import { WelcomePage } from "../../pages/WelcomePage"; + +export default interface ITestPages { + bailoutPage: BailoutPage, + capabilityQuestionsPage: CapabilityQuestionsPage, + contactConfirmationPage: ContactConfirmationPage, + contactDetailsPage: ContactDetailsPage, + coverageStatementPage: CoverageStatementPage, + duplicateCheckPage: DuplicateCheckPage, + endorsementsPage: EndorsementsPage, + orderConfirmationPage: OrderConfirmationPage, + partQuestionsPage: PartQuestionsPage, + paymentMethodPage: PaymentMethodPage, + paymentPage: PaymentPage, + paypalPage: PaypalPage, + policyHolderDetailsPage: PolicyHolderDetailsPage, + policyVehiclesPage: PolicyVehiclesPage, + providerPreferencePage: ProviderPreferencePage, + schedulePage: SchedulePage, + serviceLocationPage: ServiceLocationPage, + servicePackagesPage: ServicePackagesPage, + tpaConfirmationPage: TpaConfirmationPage, + tpaSearchPage: TpaSearchPage, + tpaSubmitPage: TpaSubmitPage, + vehicleDamagePage: VehicleDamagePage, + vehicleLookupPage: VehicleLookupPage, + vehiclePartQuestionsPage: VehiclePartQuestionsPage, + vehicleSelectionPage: VehicleSelectionPage, + vinLookupPage: VinLookupPage, + vehicleLookupAddressPage: VehicleLookupAddressPage, + vehicleLookupLicensePage: VehicleLookupLicensePage, + welcomePage: WelcomePage +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/IValidationExpectation.ts b/playwright-tests/business-logic/types/IValidationExpectation.ts new file mode 100644 index 00000000..86a948ef --- /dev/null +++ b/playwright-tests/business-logic/types/IValidationExpectation.ts @@ -0,0 +1,11 @@ +export default interface IValidationExpectation { + // readonly Minicart: boolean; + // readonly SummaryTotals: boolean; + // readonly SummarySection: boolean; + // readonly AgreementPricing: boolean; + // readonly AllotmentStatus: boolean; + // readonly ChevronStatus: boolean; + // readonly AgreementDates: boolean; + // readonly LineItems: boolean; + // readonly AccountingTotal: boolean; +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/IValidations.ts b/playwright-tests/business-logic/types/IValidations.ts new file mode 100644 index 00000000..75d904c3 --- /dev/null +++ b/playwright-tests/business-logic/types/IValidations.ts @@ -0,0 +1,13 @@ + +export default interface IValidations { + // readonly minicartLineItems: boolean; + // readonly minicartTotals: boolean; + // readonly cartSummary: boolean; + // readonly agreementPricing: boolean; + // readonly dealDescription: boolean; + // readonly allotmentStatus: boolean; + // readonly chevronStatus: boolean; + // readonly agreementDates: boolean; + // readonly lineItems: boolean; + // readonly accountingTotal: boolean; +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/RuleEngine.ts b/playwright-tests/business-logic/types/RuleEngine.ts new file mode 100644 index 00000000..8459c03d --- /dev/null +++ b/playwright-tests/business-logic/types/RuleEngine.ts @@ -0,0 +1,241 @@ +import { BuiltInRules, builtInRules } from "@business-logic/rules/RuleEngineBuiltins"; +import LoggingUtils from "@impl/utils/LoggingUtils"; +import { ConsoleColor, ResultTypes } from "@business-logic/types/Enums"; + +export type Rule = { + id: number; + name: string; + check: (obj: T) => boolean; + dependsOn?: number[]; +}; + +export class ValidationOptions { + public skipBuiltIns: boolean; + public exclude: number[]; + public throwOnError: boolean = true; + public errorsOnly: boolean = false; + + constructor(options: { + throwOnError?: boolean; + errorsOnly?: boolean; + skipBuiltIns?: boolean; + exclude?: number[]; + } = {}) { + this.throwOnError = options.throwOnError ?? true, this.errorsOnly = options.errorsOnly ?? true, this.skipBuiltIns = options.skipBuiltIns ?? false; + this.exclude = options.exclude ?? []; + } +} + +export class RuleEngine { + private rules: Rule[] = []; + private nextCustomRuleId = 1; + + static readonly BuiltInRuleIds: Record = {} as Record; + + constructor() { + this.addBuiltInRules(builtInRules as { id: BuiltInRules; name: string; check: (obj: T) => boolean; dependsOn?: BuiltInRules[]; }[]); + } + + private addBuiltInRules(builtInRules: { id: BuiltInRules; name: string; check: (obj: T) => boolean; dependsOn?: BuiltInRules[]; }[]): void { + builtInRules.forEach(rule => this.addRuleInternal(rule.name, rule.check, rule.id, rule.dependsOn)); + } + + private addRuleInternal(name: string, check: (obj: T) => boolean, builtInId: BuiltInRules, dependsOn?: BuiltInRules[]): number { + const id = builtInId; + const dependsOnIds = dependsOn?.map(dep => dep as number); + this.rules.push({ id, name, check, dependsOn: dependsOnIds }); + RuleEngine.BuiltInRuleIds[builtInId] = id; + return id; + } + + addRule(name: string, check: (obj: T) => boolean, options?: { dependsOn?: (number | BuiltInRules)[]; }): Rule { + if (this.nextCustomRuleId >= 1000) + throw new Error("Maximum number of custom rules (999) has been reached."); + + const id = this.nextCustomRuleId++; + const dependsOn = options?.dependsOn?.map(dep => typeof dep === "number" ? dep : RuleEngine.BuiltInRuleIds[dep]); + const rule: Rule = { id, name, check, dependsOn }; + this.rules.push(rule); + return rule; + } + + when(condition: (obj: T) => boolean): WhenClause { + return new WhenClause(this, condition); + } + + checkRuleById(id: number | BuiltInRules, obj: T): boolean { + const ruleId = typeof id === "number" ? id : RuleEngine.BuiltInRuleIds[id]; + const rule = this.rules.find(r => r.id === ruleId); + return rule ? rule.check(obj) : true; + } + + validate(obj: T): ValidationResult[] { + const sortedRules = this.sortRules(); + const results = new Map(); + const resultSummary: ValidationResult[] = []; + + sortedRules.forEach(rule => { + const dependenciesValid = rule.dependsOn ? rule.dependsOn.every(dep => results.get(dep)) : true; + const result = dependenciesValid && rule.check(obj); + results.set(rule.id, result); + + const resultType: ResultTypes = dependenciesValid ? result ? ResultTypes.Success : ResultTypes.Failure : ResultTypes.Skipped; + + resultSummary.push(ValidationResult.FromRule(rule.name, rule.id, resultType, `Rule: "${rule.name}". Result: ${ResultTypes[resultType]}`)); + }); + return resultSummary; + } + + private sortRules(): Rule[] { + const sortedRules: Rule[] = []; + const rulesMap = new Map(this.rules.map(rule => [rule.id, rule])); + + const visit = (rule: Rule, visited: Set, stack: Set) => { + if (stack.has(rule.id)) + throw new Error(`Circular dependency detected in rule: ${rule.name}!`); + + if (!visited.has(rule.id)) { + stack.add(rule.id); + if (rule.dependsOn) { + for (const dependency of rule.dependsOn) { + const dependencyRule = rulesMap.get(dependency); + if (dependencyRule) + visit(dependencyRule, visited, stack); + } + } + stack.delete(rule.id); + visited.add(rule.id); + sortedRules.push(rule); + } + }; + + const visited = new Set(); + for (const rule of this.rules) + visit(rule, visited, new Set()); + + return sortedRules; + } + + getRulesByName(name: string): Rule[] { + return this.rules.filter(rule => rule.name === name); + } + + getRulesByID(id: number): Rule[] { + return this.rules.filter(rule => rule.id === id); + } + + static PrintValidationResults(results: ValidationResult[], options: ValidationOptions = new ValidationOptions()) { + if (options.skipBuiltIns) + results = results.filter(x => x.ruleID < 1000); + + if (options.exclude) + results = results.filter(x => !options.exclude!.includes(x.ruleID)); + + if (options.errorsOnly) + results = results.filter(x => x.result == ResultTypes.Failure); + + if (results.length > 0) { + LoggingUtils.log("Rule Validation:", ConsoleColor.Blue); + results.forEach(x => LoggingUtils.log(` ${LoggingUtils.icon(x.result!)} Rule: [${x.ruleID}] "${x.ruleName}". Result: ${ResultTypes[x.result!]}`, x.result == ResultTypes.Success ? ConsoleColor.Green : ConsoleColor.Red)); + console.log(); + } + + if (options.throwOnError && results.filter(x => x.result != ResultTypes.Success).length > 0) + throw new Error("Rule Validation Errors"); + } +} + +export class WhenClause { + constructor(private ruleEngine: RuleEngine, private condition: (obj: T) => boolean) { } + + then(consequent: (obj: T) => boolean): DescriptionClause { + return new DescriptionClause(this.ruleEngine, (obj: T) => { + return !this.condition(obj) || consequent(obj); + }, []); + } +} + +export class DescriptionClause { + private dependencies: (number | BuiltInRules)[] = []; + + constructor(private ruleEngine: RuleEngine, private check: (obj: T) => boolean, dependencies: (number | BuiltInRules)[] = []) { + this.dependencies = dependencies; + } + + because(description: string): Rule { + return this.ruleEngine.addRule(description, this.check, { dependsOn: this.dependencies }); + } + + dependsOn(...dependencies: (number | BuiltInRules | (number | BuiltInRules)[])[]): DescriptionClause { + const flatDependencies = dependencies.flat(); + const tmp = (obj: T) => { + const dependenciesMet = flatDependencies.every(depId => this.ruleEngine.checkRuleById(depId, obj)); + return dependenciesMet && this.check(obj); + }; + return new DescriptionClause(this.ruleEngine, tmp, [...this.dependencies, ...flatDependencies]); + } +} +export class ValidationResult { + value: any; + ruleName: string; + ruleID: number; + error: string | null; + message: string; + result: ResultTypes | null; + + public static FromRule(ruleName: string, ruleID: number, result: ResultTypes, message: string) { + const retval = new ValidationResult(); + retval.ruleName = ruleName; + retval.ruleID = ruleID; + retval.result = result; + retval.message = message; + return retval; + } + + public static FromSuccess(value: any, message: string): ValidationResult { + const retval = new ValidationResult(); + retval.value = value; + retval.message = message; + retval.result = ResultTypes.Success; + return retval; + } + + public static FromFailure(error: string): ValidationResult { + const retval = new ValidationResult(); + retval.error = error; + retval.result = ResultTypes.Failure; + return retval; + } + + public static PrintValidationResults(results: ValidationResult[], options: ValidationOptions) { + var errors = results.filter(x => x.result == ResultTypes.Failure); + if (errors.length > 0) { + LoggingUtils.log("Type Validation Errors:", ConsoleColor.Red); + errors.forEach((message) => LoggingUtils.log(` ${LoggingUtils.icon(false)} ${message.error}`)); + console.log(); + } + + var successes = results.filter(x => x.result != ResultTypes.Failure); + + const suffix = "is valid."; + successes.sort((a, b) => { + const aa = a.message.endsWith(suffix); + const bb = b.message.endsWith(suffix); + + return Number(bb) - Number(aa); + }); + + if (!options.errorsOnly && successes.length > 0) { + LoggingUtils.log("Type Validation Messages:", ConsoleColor.Green); + successes.forEach((message) => LoggingUtils.log(` ${LoggingUtils.icon(true)} ${message.message}`)); + console.log(); + } + + if (options.throwOnError && errors.length > 0) + throw new Error("Validation Errors"); + } + + public static HasError(results: ValidationResult[]): boolean { + return results.filter(x => x.result == ResultTypes.Failure).length > 0; + } +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/Test.ts b/playwright-tests/business-logic/types/Test.ts new file mode 100644 index 00000000..d665727b --- /dev/null +++ b/playwright-tests/business-logic/types/Test.ts @@ -0,0 +1,44 @@ +import { test as base } from "@playwright/test"; +import type { Page, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestInfo as PlaywrightTestInfo } from "@playwright/test"; +import ITestCase from "@business-logic/types/ITestCase"; +import { RuleEngine, ValidationOptions } from "@business-logic/types/RuleEngine"; +import TestCase from "@business-logic/types/TestCase"; +import Soft from "@business-logic/validations/Soft"; +import FakerUtils from "@impl/utils/FakerUtils"; +import LoggingUtils from "@impl/utils/LoggingUtils"; + +export type TestFunction = (args: PlaywrightTestArgs & PlaywrightTestOptions & PlaywrightWorkerArgs & PlaywrightWorkerOptions, testInfo: TestInfo) => void | Promise; +export type TestRunnerFunction = (page: Page, testInfo: TestInfo, /*testCase: TestCase*/) => void | Promise; + +export interface TestInfo extends PlaywrightTestInfo { + testCase: TestCase; +} + +export const test = base.extend<{ testInfo: TestInfo; }>({ + // Do not use the fixture because it is not extended, and will cause a circular reference error + // Use ({}, use, testInfo) not ({ testInfo }, use) + testInfo: async ({}, use, testInfo) => { + await use(testInfo as TestInfo); + } +}); + +export function addSmokeTagToRandomTest(testCases: ITestCase[]) { + const index = Math.floor(Math.random() * testCases.length); + testCases.at(index)?.tags.push("@smoke"); +} + +export function prepareTest(testData: ITestCase, testRunner: TestRunnerFunction, validationOptions: ValidationOptions, ruleEngine: RuleEngine): [string, object, TestFunction] { + const name = testData.name; + const attributes = { tag: TestCase.getTags(testData) }; + const testFunction: TestFunction = ({ page }, testInfo) => { + // Do not instantiate TestCase outside of this function, + // otherwise it will be instantiated several times for each test case + Soft.initialize(testInfo, page); + testInfo.testCase = new TestCase(testData, validationOptions, FakerUtils.getRandomTestID()); + const results = ruleEngine.validate(testInfo.testCase); + RuleEngine.PrintValidationResults(results, validationOptions); + console.log(LoggingUtils.logValidate(`TestID: ${testInfo.testCase.testID}`, true)); + return testRunner(page, testInfo, /*testInfo.testCase*/); + }; + return [name, attributes, testFunction]; +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/TestCase.ts b/playwright-tests/business-logic/types/TestCase.ts new file mode 100644 index 00000000..ac7bba6d --- /dev/null +++ b/playwright-tests/business-logic/types/TestCase.ts @@ -0,0 +1,227 @@ +import PropertyUtils from "@impl/utils/PropertyUtils"; +import { formatTag } from "@impl/utils/TaggingUtils"; +import { Page } from "@playwright/test"; +import { TestInfo } from "@business-logic/types/Test"; +import { ConsoleColor } from "@business-logic/types/Enums"; +import FrameworkConfig from "@business-logic/types/FrameworkConfig"; +import { DisposableBase } from "@business-logic/types/IDisposable"; +import ITestCase from "@business-logic/types/ITestCase"; +import { ValidationOptions, ValidationResult } from "@business-logic/types/RuleEngine"; +import Soft, { SoftError } from "@business-logic/validations/Soft"; +import Validations from "./Validations"; +import FakerUtils from "@impl/utils/FakerUtils"; +import { ITestData } from "./ITestData"; +import { BailoutPage } from "../../pages/BailoutPage"; +import { ContactConfirmationPage } from "../../pages/ContactConfirmationPage"; +import { CoverageStatementPage } from "../../pages/CoverageStatementPage"; +import ITestPages from "./ITestPages"; +import { DuplicateCheckPage } from "../../pages/DuplicateCheckPage"; +import { OrderConfirmationPage } from "../../pages/OrderConfirmationPage"; +import { PartQuestionsPage } from "../../pages/PartQuestionsPage"; +import { PaymentMethodPage } from "../../pages/PaymentMethodPage"; +import { PaymentPage } from "../../pages/PaymentPage"; +import { PaypalPage } from "../../pages/PaypalPage"; +import { PolicyHolderDetailsPage } from "../../pages/PolicyHolderDetailsPage"; +import { PolicyVehiclesPage } from "../../pages/PolicyVehiclesPage"; +import { ProviderPreferencePage } from "../../pages/ProviderPreferencePage"; +import { SchedulePage } from "../../pages/SchedulePage"; +import { ServiceLocationPage } from "../../pages/ServiceLocationPage"; +import { ServicePackagesPage } from "../../pages/ServicePackagesPage"; +import { TpaConfirmationPage } from "../../pages/TpaConfirmationPage"; +import { TpaSearchPage } from "../../pages/TpaSearchPage"; +import { TpaSubmitPage } from "../../pages/TpaSubmitPage"; +import { VehicleDamagePage } from "../../pages/VehicleDamagePage"; +import { VehicleLookupPage } from "../../pages/VehicleLookupPage"; +import { VehicleSelectionPage } from "../../pages/VehicleSelectionPage"; +import { VinLookupPage } from "../../pages/VinLookupPage"; +import { WelcomePage } from "../../pages/WelcomePage"; +import { ContactDetailsPage } from "../../pages/ContactDetailsPage"; +import { EndorsementsPage } from "../../pages/EndorsementsPage"; +import { VehicleLookupAddressPage } from "../../pages/VehicleLookupAddressPage"; +import { VehicleLookupLicensePage } from "../../pages/VehicleLookupLicensePage"; +import CcisApiUtil from "@impl/api/CcisApiUtil"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import VehiclePartQuestionsPage from "../../pages/VehiclePartsPage"; +import CapabilityQuestionsPage from "../../pages/CapabilityQuestionsPage"; + +export default class TestCase extends DisposableBase implements ITestCase { + public static FrameworkConfig: FrameworkConfig = { + // company: NetJetsCompanies.NJA, + // companyString: NetJetsCompanies[NetJetsCompanies.NJA], + // currency: CurrencyTypes.USD, + createResources: true, //process.env.FW_CREATE_RESOURCES! === "true", + destroyResources: true, // process.env.FW_DESTROY_RESOURCES! === "true", + maxAllotmentHours: Number(process.env.FW_MAX_ALLOTMENT_HOURS) + }; + + public static readonly Constants = class { + static readonly DISPOSE_HALTED: string = "FrameworkConfig is set to NOT destroy resources. Teardown halted!"; + static readonly CREATION_HALTED: string = "FrameworkConfig is set to NOT create resources. Preparation halted!"; + }; + + public readonly testID?: string; + public readonly name: string; + public readonly tags: string[]; + + public readonly testData: Partial; + + public readonly validations?: Validations; + public readonly tempData?: any[] = []; + + + public location: Location; + public alternateLocation?: Location; + + public pages: ITestPages; + + public constructor(data: ITestCase, validationOptions: ValidationOptions = new ValidationOptions(), testID: string) { + super(); + Object.assign(this, data); + this.testID = FakerUtils.getRandomTestID(); + + const validationResults: ValidationResult[] = []; + + this.name = PropertyUtils.getValue(data, TestCase.name, validationResults, { isRequired: true }, x => x.name); + this.tags = PropertyUtils.getValue(data, TestCase.name, validationResults, { isRequired: true }, x => x.tags); + this.validations = PropertyUtils.getValue(data, TestCase.name, validationResults, { isRequired: false }, x => x.validations); + + // if (PropertyUtils.hasProperty(data, TestCase.name, validationResults, { isRequired: false }, x => x.oldTransactions)) + // this.oldTransactions = data.oldTransactions.map((item: any) => new TransactionData(item, validationResults, this.testID)); + + // if (PropertyUtils.hasProperty(data, TestCase.name, validationResults, { isRequired: true }, x => x.transaction)) + // this.transaction = new TransactionData(data.transaction, validationResults, this.testID); + + ValidationResult.PrintValidationResults(validationResults, validationOptions); + } + + public static getTags(testCase: ITestCase): string[] { + let retval = [ + ...testCase.tags, + formatTag(testCase.name), + // formatTag(testCase.transaction.programType) + ]; + + // if (testCase.transaction.aircraft) + // retval.push(formatTag(testCase.transaction.aircraft.type)); + + // testCase.oldTransactions.forEach((t) => { + // retval.push(formatTag(t.programType)); + // if (t.aircraft) + // retval.push(formatTag(t.aircraft.type)); + // }); + return retval; + } + + public static async afterEachMethod(page: Page, testInfo: TestInfo) { + const originalStatus = testInfo.status; + + if (Soft.hasFailedAssertions()) + testInfo.status = "failed"; + + for (let a of Soft.getFailedAssertions()) + testInfo.errors.push(new SoftError(a)); + + // NOTE: This try catch is here because the tests when locally, frequently + // fail tests on screenshot, which we do not want, is it make it + // harder to parse any other real errors we do care about. T.S. 9.5.2024 + try { + await testInfo.attach("End of Test Screenshot", { + body: await page.screenshot({ fullPage: true }), + contentType: 'image/png' + }); + } catch (error) { + console.error(error); + } + + + if (testInfo.testCase) + await testInfo.testCase.disposeAll(); + + const seconds: string = String(testInfo.duration / 1000); + const minutes: string = (testInfo.duration / 1000 / 60).toFixed(2); + const validationErrors: string = Soft.hasFailedAssertions() ? ` with ${Soft.getFailureCount()} validation errors` : ""; + + if (originalStatus == "failed") + console.log(`${ConsoleColor.Red}Test failed after ${seconds} seconds, or roughly ${minutes} minutes${validationErrors}.${ConsoleColor.Reset}`); + else { + if (testInfo.status == "passed") + console.log(`${ConsoleColor.Green}Test finished successfully in ${seconds} seconds, or roughly ${minutes} minutes.${ConsoleColor.Reset}`); + else + console.log(`${ConsoleColor.Orange}Test finished in ${seconds} seconds, or roughly ${minutes} minutes${validationErrors}.${ConsoleColor.Reset}`); + } + console.log(page.url()); + } + + public async setup(): Promise { + if (this.testData.policySoap && this.testData.claimDetails) { + const apiUtil = new CcisApiUtil(); + + // Create Policy + const req = MockPolicyData.generateCreateMockPolicyRequest(this.testData.claimDetails.policyNumber, this.testData.policySoap); + console.log('Policy Number: ' + this.testData.claimDetails.policyNumber + ' Damage Date: ' + this.testData.claimDetails.damageDate + ' Postal code: ' + this.testData.customerDetails?.address.postalCode!); + const res = await apiUtil.createFakeResponse(req); + console.log("Fake Policy creation status" + res.status); // For debug use console.dir(res); + + // Create Claim Registration + const claimRegReq = MockPolicyData.generateCreateClaimRegistrationRequest(this.testData.claimDetails.policyNumber); + const claimRegRes = await apiUtil.createFakeResponse(claimRegReq); + console.log("Fake claim registration creation status: " + claimRegRes.status); //For debug use console.dir(claimRegRes); + } + } + + public setupPages(page: Page): void { + this.pages = { + bailoutPage: new BailoutPage(page), + capabilityQuestionsPage: new CapabilityQuestionsPage(page), + contactConfirmationPage: new ContactConfirmationPage(page), + contactDetailsPage: new ContactDetailsPage(page), + coverageStatementPage: new CoverageStatementPage(page), + duplicateCheckPage: new DuplicateCheckPage(page), + endorsementsPage: new EndorsementsPage(page), + orderConfirmationPage: new OrderConfirmationPage(page), + partQuestionsPage: new PartQuestionsPage(page), + paymentMethodPage: new PaymentMethodPage(page), + paymentPage: new PaymentPage(page), + paypalPage: new PaypalPage(page), + policyHolderDetailsPage: new PolicyHolderDetailsPage(page), + policyVehiclesPage: new PolicyVehiclesPage(page), + providerPreferencePage: new ProviderPreferencePage(page), + schedulePage: new SchedulePage(page), + serviceLocationPage: new ServiceLocationPage(page), + servicePackagesPage: new ServicePackagesPage(page), + tpaConfirmationPage: new TpaConfirmationPage(page), + tpaSearchPage: new TpaSearchPage(page), + tpaSubmitPage: new TpaSubmitPage(page), + vehicleDamagePage: new VehicleDamagePage(page), + vehicleLookupPage: new VehicleLookupPage(page), + vehiclePartQuestionsPage: new VehiclePartQuestionsPage(page), + vehicleSelectionPage: new VehicleSelectionPage(page), + vinLookupPage: new VinLookupPage(page), + vehicleLookupAddressPage: new VehicleLookupAddressPage(page), + vehicleLookupLicensePage: new VehicleLookupLicensePage(page), + welcomePage: new WelcomePage(page) + }; + } + + protected async dispose(): Promise { + if (this.testData.policySoap && this.testData.claimDetails) { + const apiUtil = new CcisApiUtil(); + + // Delete policy + const policyDeleteRes = await apiUtil.deleteFakeResponse({ + accountNumber: '550036', // TODO: Change when we have more clients + key: this.testData.claimDetails.policyNumber, + responseType: 'Policy' + }); + console.log("Delete 'Fake policy' status: " + policyDeleteRes.status); //For debug use console.dir(policyDeleteRes); + + // Delete Claim Registration + const crDeleteRes = await apiUtil.deleteFakeResponse({ + accountNumber: '550036', // TODO: Change when we have more clients + key: this.testData.claimDetails.policyNumber, + responseType: 'ClaimRegistration' + }); + console.log("Delete 'Fake Claim Registration policy' status: " + crDeleteRes.status); //For debug use console.dir(crDeleteRes); + } + } +} \ No newline at end of file diff --git a/playwright-tests/business-logic/types/Validations.ts b/playwright-tests/business-logic/types/Validations.ts new file mode 100644 index 00000000..51f1d27f --- /dev/null +++ b/playwright-tests/business-logic/types/Validations.ts @@ -0,0 +1,29 @@ +import PropertyUtils from "@impl/utils/PropertyUtils"; +import IValidations from "./IValidations"; +import { ValidationResult } from "./RuleEngine"; + +export default class Validations implements IValidations { + public readonly minicartLineItems: boolean; + public readonly minicartTotals: boolean; + public readonly cartSummary: boolean; + public readonly agreementPricing: boolean; + public readonly dealDescription: boolean; + public readonly allotmentStatus: boolean; + public readonly chevronStatus: boolean; + public readonly agreementDates: boolean; + public readonly lineItems: boolean; + public readonly accountingTotal: boolean; + + public constructor(json: any, validationResults: ValidationResult[]) { + this.minicartLineItems = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.minicartLineItems); + this.minicartTotals = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.minicartTotals); + this.cartSummary = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.cartSummary); + this.dealDescription = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.dealDescription); + this.agreementPricing = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.agreementPricing); + this.allotmentStatus = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.allotmentStatus); + this.chevronStatus = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.chevronStatus); + this.agreementDates = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.agreementDates); + this.lineItems = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.lineItems); + this.accountingTotal = PropertyUtils.getValue(json, Validations.name, validationResults, { isRequired: true }, x => x.accountingTotal); + } +} diff --git a/playwright-tests/business-logic/validations/Soft.ts b/playwright-tests/business-logic/validations/Soft.ts new file mode 100644 index 00000000..69fbfb73 --- /dev/null +++ b/playwright-tests/business-logic/validations/Soft.ts @@ -0,0 +1,179 @@ +import LoggingUtils from '@impl/utils/LoggingUtils'; +import { Page } from '@playwright/test'; +import { TestInfo } from '@business-logic/types/Test'; +import { TestInfoError } from "@playwright/test"; +import { DateTime } from 'luxon'; +import { expect as pw_expect } from '@playwright/test'; + +export default class Soft { + private static _instance: Soft | null = null; + private testInfo: TestInfo; + private page: Page; + private failedAssertions: string[] = []; + private errorCounter: number = 0; + private errorsOnly: boolean = false; + + private constructor(testInfo: TestInfo, page: Page) { + this.testInfo = testInfo; + this.page = page; + } + + public static initialize(testInfo: TestInfo, page: Page): void { + Soft._instance = new Soft(testInfo, page); + } + + public static setOptions(options: { errorsOnly: boolean }): void { + Soft.getInstance().errorsOnly = options.errorsOnly; + } + + public static getOptions(): { errorsOnly: boolean } { + return { errorsOnly: Soft.getInstance().errorsOnly }; + } + + private static getInstance(): Soft { + if (!Soft._instance) + throw new Error("Soft is not initialized. Call Soft.initialize(testInfo, page) first!"); + return Soft._instance; + } + + public async handleAssertion( + matcherFull: string, + matcherDisplay: string, + matcherFunction: () => Promise, + reason?: string): Promise { + reason = reason ? reason : '' + const reasonText = reason ? `'${reason}' ` : ''; + try { + await matcherFunction(); + if (!this.errorsOnly) + console.log(LoggingUtils.logValidate(`Validation ${reasonText}passed: ${matcherDisplay}!`, true)); + } catch (error) { + console.log(LoggingUtils.logValidate(`Validation ${reasonText}failed: ${matcherDisplay}!`, false)); + //const errorMessage = error instanceof Error ? error.message : String(error); + this.failedAssertions.push(`\n${++this.errorCounter}_Validation ${reasonText}failed:\n${LoggingUtils.replaceEmptyLinesWithMiddleDot(matcherFull)}!\n${error.stack}\n`); + // NOTE: I find it more helpful for the stack trace to be included here, so we know which line the validation is failing + try { + const screenshot: Buffer = await this.page.screenshot({ fullPage: true }); + const name: string = LoggingUtils.sanitizeFileName(`${this.errorCounter}_Validation_${reason}${DateTime.now().valueOf()}`); + await this.testInfo.attach(name, { + body: screenshot, + contentType: 'image/png' + }); + } catch(err){ + // ohwell + console.error(error) + } + + } + } + + public static expect(value: any, reason?: string): ExpectationChain { + return new ExpectationChain(value, Soft.getInstance(), reason); + } + + public static getFailedAssertions(): string[] { + return Soft.getInstance().failedAssertions; + } + + public static hasFailedAssertions(): boolean { + return Soft.getInstance().failedAssertions.length > 0; + } + + public static getFailureCount(): number { + return Soft.getInstance().failedAssertions.length; + } + + public static clearFailedAssertions(): void { + Soft.getInstance().failedAssertions = []; + } +} + +export class SoftError implements TestInfoError { + public readonly message?: string | undefined; + constructor(msg: string) { + this.message = msg; + } +} + +export class ExpectationChain { + constructor(private value: any, private soft: Soft, private reason?: string) { } + + private formatValue(value: any): string { + return LoggingUtils.truncateString(value); + } + + public async toBe(expected: any): Promise { + await this.soft.handleAssertion( + `expect(${this.value}).toBe(${expected})`, + `expect(${this.formatValue(this.value)}).toBe(${this.formatValue(expected)})`, + async () => await pw_expect(this.value).toBe(expected), + this.reason + ); + return this; + } + + public async toEqual(expected: any): Promise { + await this.soft.handleAssertion( + `expect(${JSON.stringify(this.value)}).toEqual(${expected})`, + `expect(${this.formatValue(JSON.stringify(this.value))}).toEqual(${this.formatValue(expected)})`, + async () => await pw_expect(this.value).toEqual(expected), + this.reason + ); + return this; + } + + public async toContain(expected: any): Promise { + await this.soft.handleAssertion( + `expect(${this.value}).toContain(${expected})`, + `expect(${this.formatValue(this.value)}).toContain(${this.formatValue(expected)})`, + async () => await pw_expect(this.value).toContain(expected), + this.reason + ); + return this; + } + + public async toHaveText(expected: string): Promise { + await this.soft.handleAssertion( + `expect(${this.value}).toHaveText(${expected})`, + `expect(${this.formatValue(this.value)}).toHaveText(${this.formatValue(expected)})`, + async () => { + if (typeof this.value.textContent !== 'function') { + throw new Error('value does not have a textContent method'); + } + const text = await this.value.textContent(); + await pw_expect(text).toHaveText(expected); + }, this.reason + ); + return this; + } + + public async toBeGreaterThan(expected: number): Promise { + await this.soft.handleAssertion( + `expect(${this.value}).toBeGreaterThan(${expected})`, + `expect(${this.formatValue(this.value)}).toBeGreaterThan(${this.formatValue(expected)})`, + async () => await pw_expect(this.value).toBeGreaterThan(expected), + this.reason + ); + return this; + } + + public async toBeTruthy(): Promise { + await this.soft.handleAssertion( + `expect(${this.value}).toBeTruthy`, + `expect(${this.formatValue(this.value)}).toBeTruthy`, + async () => await pw_expect(this.value).toBeTruthy(), + this.reason + ); + return this; + } + + public async toBeFalsy(): Promise { + await this.soft.handleAssertion( + `expect(${this.value}).toBeFalsy`, + `expect(${this.formatValue(this.value)}).toBeFalsy`, + async () => await pw_expect(this.value).toBeFalsy(), + this.reason + ); + return this; + } +} \ No newline at end of file diff --git a/playwright-tests/eslint.config.js b/playwright-tests/eslint.config.js new file mode 100644 index 00000000..56dbdf53 --- /dev/null +++ b/playwright-tests/eslint.config.js @@ -0,0 +1,4 @@ +// import tsEslint from "typescript-eslint" +const tsEslint = require('typescript-eslint'); +module.exports = + tsEslint.configs.strict \ No newline at end of file diff --git a/playwright-tests/impl/api/AdminServiceApiUtil.ts b/playwright-tests/impl/api/AdminServiceApiUtil.ts new file mode 100644 index 00000000..9d2c479c --- /dev/null +++ b/playwright-tests/impl/api/AdminServiceApiUtil.ts @@ -0,0 +1,21 @@ +import axios, { type AxiosInstance } from 'axios'; +import { buildQueryString, httpGet } from '@utils/HttpUtils' +import { IClientAuthentication, IClientSignatureRequest, IClientSignatureResponse } from '@business-logic/types/Authentication'; + + +const adminServiceUrl = process.env.ADMIN_SERVICE_API_URL || ''; + +const axiosInstance: AxiosInstance = axios.create({ + baseURL: adminServiceUrl, + headers: {'Content-Type': 'application/json'} +}); + +const INSURANCE_SERVICE_ROUTE = 'insurance'; + +export const getClientAuthByClientTag = async (clientTag: string): Promise => { + return await httpGet(axiosInstance, `${INSURANCE_SERVICE_ROUTE}/client-info?clientTag=${clientTag}`); +} +export const getClientSignature = async (request: IClientSignatureRequest): Promise => { + const params = buildQueryString(request); + return await httpGet(axiosInstance, `${INSURANCE_SERVICE_ROUTE}/signature?${params.toString()}`); +} \ No newline at end of file diff --git a/playwright-tests/impl/api/ApiResponseInterceptUtil.ts b/playwright-tests/impl/api/ApiResponseInterceptUtil.ts new file mode 100644 index 00000000..23c31ece --- /dev/null +++ b/playwright-tests/impl/api/ApiResponseInterceptUtil.ts @@ -0,0 +1,47 @@ +import { IPartsOrQuestionsResponse } from "@business-logic/types/DigitalApi"; +import { ITestData } from "@business-logic/types/ITestData"; +import { expect, Response } from "@playwright/test"; + +export default class ApiResponseInterceptUtil { + readonly testData: Partial; + + constructor(testData: Partial) { + this.testData = testData; + + // Bind callback functions to the class instance so 'this' is usable within a callback + this.handleInterceptResponse = this.handleInterceptResponse.bind(this); + this.handlePartsOrQuestionsResponse = this.handlePartsOrQuestionsResponse.bind(this); + } + + async handleInterceptResponse(response: Response) { + if (!response.url().includes('safelite.io')) { + return; + } + + const urlPath = response.url().split('safelite.io')[1]; // get api path + + switch(urlPath) { + case '/parts/api/v1/parts/parts-or-questions': + await this.handlePartsOrQuestionsResponse(response); + break; + default: + break; + } + if (response.url().endsWith('/parts/parts-or-questions') && response.status() === 200) { + + } + } + + async handlePartsOrQuestionsResponse(response: Response) { + if (this.testData.hasOemEndorsement) { + const partsRes = (await response.json()) as IPartsOrQuestionsResponse; + for ( const partOrQuestion of partsRes.partsOrQuestions) { + for (const part of partOrQuestion.parts) { + expect.soft(part.partNumber.endsWith('OEM'), + `handlePartsOrQuestionsResponse>> OEM endorsement was expected, but "${part.partType}" with part number "${part.partNumber}" is not OEM.` + ).toEqual(true); + } + } + } + } +} \ No newline at end of file diff --git a/playwright-tests/impl/api/CcisApiUtil.ts b/playwright-tests/impl/api/CcisApiUtil.ts new file mode 100644 index 00000000..d1d2dd87 --- /dev/null +++ b/playwright-tests/impl/api/CcisApiUtil.ts @@ -0,0 +1,28 @@ +import { IDeleteFakeResponseParams, IPostSaveFakeResponseRequestBody } from "@business-logic/types/CcisApi"; +import axios from "axios"; + +const authToken = process.env.CCIS_API_AUTH || ''; +const ccisUrl = process.env.CCIS_API_URL || ''; + +export default class CcisApiUtil { + readonly baseUrl = ccisUrl; + readonly postCreateMockPolicyUrl = `${this.baseUrl}/ccis/api/v1/admin/fakeResponses/save` + readonly deleteFakeResponseUrl = `${this.baseUrl}/ccis/api/v1/admin/fakeResponses` + + createFakeResponse(requestBody: IPostSaveFakeResponseRequestBody) { + return axios.post(this.postCreateMockPolicyUrl, requestBody, { + headers: { + 'X-Mule-Origin-Verify': authToken + } + }); + } + + deleteFakeResponse(params: IDeleteFakeResponseParams) { + const deleteUrl = `${this.deleteFakeResponseUrl}/${params.accountNumber}/${params.key}/${params.responseType}`; + return axios.delete(deleteUrl, { + headers: { + 'X-Mule-Origin-Verify': authToken + } + }); + } +} \ No newline at end of file diff --git a/playwright-tests/impl/utils/DateUtils.ts b/playwright-tests/impl/utils/DateUtils.ts new file mode 100644 index 00000000..7348ea60 --- /dev/null +++ b/playwright-tests/impl/utils/DateUtils.ts @@ -0,0 +1,26 @@ +export function formatDate(date: Date) { + const isoString = date.toISOString(); + return isoString.slice(0, 10); +} + +export function formatTime(date: Date) { + return date.toLocaleTimeString('en-US', { + hour: 'numeric', + minute: '2-digit', + hour12: true + }); +} + +export function getNextWeekday(date?: Date) { + if (!date) { + date = new Date(); + date.setHours(8,0,0,0); + } + const dayOfWeek = date.getDay(); + const daysToAdd = dayOfWeek === 5? 3: 1; // Add 3 days if today is friday. Otherwise add 1. + + const nextDay = new Date(date); + nextDay.setDate(date.getDate() + daysToAdd); + + return nextDay; +} \ No newline at end of file diff --git a/playwright-tests/impl/utils/EnumUtils.ts b/playwright-tests/impl/utils/EnumUtils.ts new file mode 100644 index 00000000..3635989c --- /dev/null +++ b/playwright-tests/impl/utils/EnumUtils.ts @@ -0,0 +1,76 @@ +function isFlags(enumObj: object): boolean { + const values = Object.values(enumObj).filter(v => typeof v === "number"); + return values.some(v => v !== 0 && (v & (v - 1)) === 0); +} + +function isValidEnumValue(value: any, enumType: object): boolean { + if (isFlags(enumType)) { + const allFlags = Object.values(enumType).reduce((acc, val) => typeof val === "number" ? acc | val : acc, 0); + return typeof value === "number" && (value & allFlags) === value; + } else { + return Object.values(enumType).includes(value); + } +} + +function getEnumValues(enumObj: object): string[] | number[] { + if (!isFlags(enumObj)) + return Object.values(enumObj); + return Object.values(enumObj).filter(value => typeof value === "number") as number[]; +} + +function getEnumString(enumObj: T, flags: number): string { + if (flags === 0) + return Object.keys(enumObj).find(key => enumObj[key] === 0) || 'None'; + + const attributes = Object.entries(enumObj).filter(([key, value]) => + typeof value === 'number' && value !== 0 && (flags & value) === value) + .map(([key]) => key); + + return attributes.length === 1 ? attributes[0] : attributes.join(', '); +} + +function validateEnumProperty(json: any, property: string, enumType: object): number | string | null { + if (json.hasOwnProperty(property) && isValidEnumValue(json[property], enumType)) + return json[property]; + return null; +} + +function hasFlag(value: number | string, flag: number | string, enumType: object): boolean { + if (isFlags(enumType)) + return typeof value === "number" && typeof flag === "number" && (value & flag) === flag; + else + return value === flag; +} + +function addFlag(value: number, flag: number, enumType: object): number { + if (isFlags(enumType)) + return value | flag; + else + throw new Error("Attempted to add flag on non-flags enum"); +} + +function removeFlag(value: number, flag: number, enumType: object): number { + if (isFlags(enumType)) + return value & ~flag; + else + throw new Error("Attempted to remove flag on non-flags enum"); +} + +function toggleFlag(value: number, flag: number, enumType: object): number { + if (isFlags(enumType)) + return value ^ flag; + else + throw new Error("Attempted to toggle flag on non-flags enum"); +} + +const EnumUtils = { + hasFlag, + addFlag, + removeFlag, + toggleFlag, + validateEnumProperty, + getEnumValues, + getEnumString +}; + +export default EnumUtils; \ No newline at end of file diff --git a/playwright-tests/impl/utils/FakerUtils.ts b/playwright-tests/impl/utils/FakerUtils.ts new file mode 100644 index 00000000..d406b13a --- /dev/null +++ b/playwright-tests/impl/utils/FakerUtils.ts @@ -0,0 +1,56 @@ +export default class FakerUtils { + private static NUMBERS = '0123456789'; + private static UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + private static LOWERCASE = 'abcdefghijklmnopqrstuvwxyz'; + private static ALPHABET = FakerUtils.UPPERCASE + FakerUtils.LOWERCASE; + private static ALPHANUMERIC = FakerUtils.NUMBERS + FakerUtils.ALPHABET; + + private static generateRandomString(length: number, characters: string): string { + return Array.from(crypto.getRandomValues(new Uint8Array(length))) + .map(byte => characters[byte % characters.length]) + .join(''); + } + + public static generateRandomNumber(min: number, max: number): number { + const range = max - min + 1; + const bytesNeeded = Math.ceil(Math.log2(range) / 8); + const randomBytes = new Uint8Array(bytesNeeded); + crypto.getRandomValues(randomBytes); + const randomValue = randomBytes.reduce((acc, byte) => (acc << 8) + byte, 0); + return min + (randomValue % range); + } + + public static getRandomTestID(): string { + return FakerUtils.generateRandomString(8, FakerUtils.ALPHANUMERIC); + } + + public static getRandomProperty(obj: Record): string { + const keys = Object.keys(obj); + const randomIndex = FakerUtils.generateRandomNumber(0, keys.length - 1); + return keys[randomIndex]; + } + + public static getRandomTail(registrationPrefix: string = "XX", testID: string = ""): string { + return FakerUtils.formatString(FakerUtils.generateRandomString(8, FakerUtils.ALPHANUMERIC)); + } + + public static getRandomEmail(domainSuffix: string = "@test.com", testID: string = ""): string { + const retval = FakerUtils.formatString("{0}{1}", FakerUtils.generateRandomString(8, FakerUtils.ALPHANUMERIC), domainSuffix); + return retval; + } + + public static getRandomLastName(testID: string = " - "): string { + return FakerUtils.formatString(" - {0}", FakerUtils.generateRandomString(21, FakerUtils.ALPHABET)); + } + + public static getObjectName(prefix: string, testID: string = ""): string { + return FakerUtils.formatString("{0} - {1}", prefix, FakerUtils.generateRandomString(21, FakerUtils.ALPHANUMERIC)); + } + + private static formatString(template: string, ...args: (string | (() => string))[]): string { + return template.replace(/\{(\d+)\}/g, (match, index) => { + const arg = args[parseInt(index)]; + return typeof arg === 'function' ? arg() : arg || ''; + }); + } +} \ No newline at end of file diff --git a/playwright-tests/impl/utils/FileUtils.ts b/playwright-tests/impl/utils/FileUtils.ts new file mode 100644 index 00000000..3f81f0c0 --- /dev/null +++ b/playwright-tests/impl/utils/FileUtils.ts @@ -0,0 +1,22 @@ +import fs from 'fs'; +import path from 'path'; + +export function writeFileToLocalCache(fileNameWithExtension: string, fileContents: string) { + const folderPath = path.join(process.cwd(), '.debug', '.cache'); + const filePath = path.join(folderPath, fileNameWithExtension); + + + try { + // Create the folder if it doesn't exist + if (!fs.existsSync(folderPath)) { + fs.mkdirSync(folderPath, { recursive: true }); + } + + // Write the data to the file + fs.writeFileSync(filePath, fileContents); + + console.log(`File "${filePath}" created successfully.`); + } catch (error) { + console.error('Error creating the file:', error); + } +} diff --git a/playwright-tests/impl/utils/HttpUtils.ts b/playwright-tests/impl/utils/HttpUtils.ts new file mode 100644 index 00000000..ec39d803 --- /dev/null +++ b/playwright-tests/impl/utils/HttpUtils.ts @@ -0,0 +1,92 @@ +import { Page } from '@playwright/test'; +import { type AxiosInstance, type AxiosResponse } from 'axios'; +import * as fs from 'fs'; +import * as path from 'path'; + +export async function httpGet(client: AxiosInstance, url: string): Promise { + const [isSuccess, response] = await handleHttp(client.get(url)); + if(isSuccess) { + return response; + } + + console.error(`An error occurred calling GET ${url}\nError:${response}`); + throw response; +} + +export async function httpPost(client: AxiosInstance, url: string, data: D): Promise { + const [isSuccess, response] = await handleHttp(client.post(url, data)); + if(isSuccess) { + return response; + } + + console.error(`An error occurred calling POST ${url}\nError:${response}`); + throw response; +} + +export function handleHttp(request: Promise>): Promise<[isSuccess: true, data: T] | [isSuccess: false, error: Error]> { + return request.then(data => { + return [true, data.data] as [true, T] + }).catch((error: Error) => { + return [false, error] as [false, Error] + }) +} + +export function buildQueryString(data: T): URLSearchParams { + const params: Record = {}; + for (const key in data) { + const value = data[key]; + params[key] = `${value}`; + } + return new URLSearchParams(params); +} + +export function forceAPIError(page: Page, endpoint: string) { + page.route('**/*', (route) => { + return route.request().url().includes(endpoint) + ? route.abort() + : route.continue() + }); +} + +// Utility method to return mock response based on endpoint and scenario +export function getMockedApiResponse(endpoint: string, scenario: string): object | null { + const mockResponsesDir = path.resolve(__dirname, '../../tests/mockResponses'); + const configFilePath = path.join(mockResponsesDir, 'mockResponsesConfig.json'); + + if (fs.existsSync(configFilePath)) { + const config = JSON.parse(fs.readFileSync(configFilePath, 'utf-8')); + const scenarioConfig = config[scenario]; + const commonConfig = config['common']; + + let mockFilePath = scenarioConfig ? scenarioConfig[endpoint] : null; + if (!mockFilePath && commonConfig) { + mockFilePath = commonConfig[endpoint]; + } + + if (mockFilePath) { + const filePath = path.join(mockResponsesDir, mockFilePath); + if (fs.existsSync(filePath)) { + const mockResponse = fs.readFileSync(filePath, 'utf-8'); + return JSON.parse(mockResponse); + } + } + } + + return null; +} + +// Utility method for mocking API responses +export function mockApiResponse(page: Page, endpoint: string, scenario: string, mockTestingFlag: boolean) { + const mockResponse = getMockedApiResponse(endpoint, scenario); + page.route(`**/${endpoint}`, route => { + if (mockResponse && mockTestingFlag) { + route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify(mockResponse) + }); + } else { + route.continue(); + } + }); +} \ No newline at end of file diff --git a/playwright-tests/impl/utils/LoggingUtils.ts b/playwright-tests/impl/utils/LoggingUtils.ts new file mode 100644 index 00000000..20ee0163 --- /dev/null +++ b/playwright-tests/impl/utils/LoggingUtils.ts @@ -0,0 +1,128 @@ +import { ConsoleColor, ResultTypes } from "@business-logic/types/Enums"; + +export default class LoggingUtils { + + public static CONSOLE_WIDTH: number = 100; + //public static ICON_OK: string = "✅"; + public static ICON_OK: string = "\u2705"; + //public static ICON_SKIP: string = "⏩"; + public static ICON_SKIP: string = "\u23ED"; + //public static ICON_WARNING: string = "⚠️"; + public static ICON_WARNING: string = "\u26A0\uFE0F"; + //public static ICON_FAIL: string = "❗"; + public static ICON_FAIL: string = "\u2757"; + + public static log(message: string | null, color: ConsoleColor = ConsoleColor.Default): void { + if (message == null) + return; + console.log(`${color}%s${ConsoleColor.Reset}`, message); + } + + public static icon(value: boolean): string; + public static icon(value: ResultTypes): string; + public static icon(value: any): string { + if (typeof value === "boolean") + return value ? this.ICON_OK : this.ICON_FAIL; + + switch (value) { + case ResultTypes.Failure: + return this.ICON_FAIL; + case ResultTypes.Success: + return this.ICON_OK; + case ResultTypes.Skipped: + return this.ICON_SKIP; + } + return ""; + } + + public static logFunc(name: string, value: string | null = null, result: boolean | null = null): string { + var icon = this.ICON_SKIP; + if (result != null) + icon = result ? this.ICON_OK : this.ICON_FAIL; + + if (value != null) + return `${this.getShortDateTime()} [${this.centerPadString(`${name}: ${this.truncateString(value)}`)}] -> ${icon}`; + + return `${this.getShortDateTime()} [${this.centerPadString(name)}] -> ${icon}`; + } + + public static logValidate(text: string, success: boolean) { + return `${this.getShortDateTime()} [${this.centerPadString(text)}] -> ${success ? this.ICON_OK : this.ICON_FAIL}`; + } + + public static truncateString(value: any, maxLength: number = this.CONSOLE_WIDTH): string { + let str = typeof value === 'string' ? value : String(value); + str = str.replace(/\s+/g, ' ').trim(); + if (str.length <= maxLength) { + return str; + } + return str.slice(0, maxLength - 2) + '..'; + } + + public static sanitizeFileName(input: string): string { + // Remove characters that are invalid in both Windows and Linux file systems + let sanitized = input.replace(/[<>:"/\\|?*\x00-\x1F]/g, ''); + + // Remove leading and trailing spaces and dots + sanitized = sanitized.trim().replace(/^\.+|\.+$/g, ''); + + // Replace remaining dots and spaces with underscores + sanitized = sanitized.replace(/[\s.]+/g, '_'); + + // Ensure the name isn't empty after sanitization + if (sanitized.length === 0) { + sanitized = 'unnamed'; + } + + // Truncate to a reasonable maximum length (e.g., 255 characters) + sanitized = sanitized.slice(0, 255); + + return sanitized; + } + + public static replaceEmptyLinesWithMiddleDot(input: string): string { + const emptyLinesRegex: RegExp = /(.+?)(\n\s*\n)+/g; + + return input.replace(emptyLinesRegex, (_match, line) => { + return line + '·\n'; + }).replace(/\n$/, ''); + } + + private static centerPadString(str: string, length: number = this.CONSOLE_WIDTH): string { + if (str.length >= length) { + return this.truncateString(str); + } + + str = this.truncateString(str); + + const totalPadding = length - str.length; + const leftPadding = Math.ceil(totalPadding / 2); + const rightPadding = Math.floor(totalPadding / 2); + + return ' '.repeat(leftPadding) + str + ' '.repeat(rightPadding); + } + + private static getShortDateTime() { + return new Date().toLocaleString('en-US', { + year: '2-digit', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false + }); + } + + public static normalizeSalesForceType(input: string, prefix: string = "Apttus_Config2__", suffix: string = "__c") { + let result = input; + + if (result.startsWith(prefix)) + result = result.slice(prefix.length); + + if (result.endsWith(suffix)) + result = result.slice(0, -suffix.length); + + return result; + } +} \ No newline at end of file diff --git a/playwright-tests/impl/utils/ParsingUtils.ts b/playwright-tests/impl/utils/ParsingUtils.ts new file mode 100644 index 00000000..29930e4c --- /dev/null +++ b/playwright-tests/impl/utils/ParsingUtils.ts @@ -0,0 +1,34 @@ + +/** + * Parses a string containing a representation of currency, and returns a typed number. Can handle + * different types of currency represenations. + * + * USD 12,345.65 -> 12345.65 + * (USD 12345.00) -> -12345 + * + * @param text String containing currency representation + * @param currencyCode Optionally define different currency code + * @returns Parsed currency with type number + */ +export function parseCurrency(text: string, currencyCode: string = "USD"): number { + // TODO: Add ability to handle null fields/not treat null as 0 - KK 9/12/24 + // Base case, if text can be cast as a number then work is done + if (!isNaN(+text)) return +text; + // Remove parentheses and continue parsing, multiply return by -1 to preserve negative value + if (text.charAt(0) === '(') return -1 * parseCurrency(text.substring(1, text.length - 1)); + // Remove currency code prefix and continue parsing + if (text.split(' ')[0] === currencyCode) return parseCurrency(text.split(' ')[1]); + // Remove commas and cast to number + return Number(text.split(',').join('')); +} + +/** + * @param text + * @returns + */ +export function parseNumberOrCurrency(text: string): Number | string { + if (!isNaN(+text)) return +text; + else if (text.charAt(0) === '(') return -1 * parseCurrency(text.substring(1, text.length - 1)); + else if (text.split(' ')[0] === "USD") return parseCurrency(text); + else return text; +} diff --git a/playwright-tests/impl/utils/PropertyUtils.ts b/playwright-tests/impl/utils/PropertyUtils.ts new file mode 100644 index 00000000..ac95d8a7 --- /dev/null +++ b/playwright-tests/impl/utils/PropertyUtils.ts @@ -0,0 +1,111 @@ +import { ValidationResult } from "@business-logic/types/RuleEngine"; +import EnumUtils from "./EnumUtils"; + +export type ExtractName = { [K in keyof T]: () => K; }; + +type GetValueOptions = { + isRequired: boolean; +}; + +function isNullOrWhiteSpace(input: unknown): boolean { + if (typeof input !== "string") + return input == null; + return input.trim().length === 0; +} + +// Matches: '() => _Enums.*****', e.g.: '() => _Enums.ModificationTypes' +// Matches: '() => *****', e.g.: '() => ModificationTypes' +// Returns: EnumName, e.g.: 'ModificationTypes' +function getEnumName(propertySelector: () => object): string { + const functionString = propertySelector.toString(); + const match = functionString.match(/\(\s*\)\s*=>\s*(?:_?[A-Z]\w*\.)?(\w+)(?::)?/); + return match ? match[1] : "Unknown"; +} + +// Matches: 'x => x.*****', e.g.: 'x => x.name', 'x => x.tags' +// Returns: PropertyName, e.g.: 'name', 'tags' +function getNameof(propertySelector: (obj: T) => any): string { + const propertyString = propertySelector.toString(); + const match = propertyString.match(/(?:=>|return)\s*([\w\s.]+)/); + + if (match && match[1]) { + const parts = match[1].split('.'); + return parts[parts.length - 1].trim(); + } + + throw new Error(`Invalid property selector: ${propertyString}`); +} + +function getValueOrNull(json: any, propertySelector: (obj: T) => any): any | null { + if (json == null) + return null; + + const property = getNameof(propertySelector); + + if (json.hasOwnProperty(property)) + return json[property]; + + return null; +} + +function getValue(json: any, typeName: string, validationResults: ValidationResult[], options: GetValueOptions, propertySelector: (obj: T) => any): any | null { + const retval = getValueOrNull(json, propertySelector); + + if (retval == null) + validationResults.push(options.isRequired ? ValidationResult.FromFailure(`Value for ${typeName}.${getNameof(propertySelector)} is null but is required!`) : ValidationResult.FromSuccess(retval, `Value for ${typeName}.${getNameof(propertySelector)} is null but is NOT required.`)); + else { + if (isNullOrWhiteSpace(retval)) + validationResults.push(ValidationResult.FromFailure(`Value for ${typeName}.${getNameof(propertySelector)} is empty!`)); + else + validationResults.push(ValidationResult.FromSuccess(retval, `Value for ${typeName}.${getNameof(propertySelector)} is valid.`)); + } + + return retval; +} + +function hasProperty(json: any, typeName: string, validationResults: ValidationResult[], options: GetValueOptions, propertySelector: (obj: ExtractName) => () => keyof T): boolean { + const retval = json.hasOwnProperty(getNameof(propertySelector)); + + if (retval) + validationResults.push(ValidationResult.FromSuccess(retval, `Value for ${typeName}.${getNameof(propertySelector)} is valid.`)); + else + validationResults.push(options.isRequired ? ValidationResult.FromSuccess(retval, `Value for ${typeName}.${getNameof(propertySelector)} is not defined but is required!`) : ValidationResult.FromFailure(`Value for ${typeName}.${getNameof(propertySelector)} is not defined but is NOT required!`)); + + return retval; +} + +function getEnumValueOrNull(json: any, enumType: object, propertySelector: (obj: T) => any): any | null { + if (json == null) + return null; + + const property = getNameof(propertySelector); + const retval = EnumUtils.validateEnumProperty(json, property, enumType); + + if (retval != null) + return retval; + else if (Object.values(enumType).includes(json[property])) + return json[property]; + + return null; +} + +function getEnumValue(json: any, typeName: string, validationResults: ValidationResult[], enumType: object, enumTypeInstance: () => object, options: GetValueOptions, propertySelector: (obj: T) => any): any | null { + const retval = getEnumValueOrNull(json, enumType, propertySelector); + + if (retval == null) + validationResults.push(options.isRequired ? ValidationResult.FromFailure(`Value for ${typeName}.${getNameof(propertySelector)} [${getEnumName(enumTypeInstance)}] is null but is required!`) : ValidationResult.FromSuccess(retval, `Value for ${typeName}.${getNameof(propertySelector)} [${getEnumName(enumTypeInstance)}] is null but is NOT required.`)); + else + validationResults.push(ValidationResult.FromSuccess(retval, `Value for ${typeName}.${getNameof(propertySelector)} is valid.`)); + + return retval; +} + +export { getEnumName, getEnumValueOrNull, getNameof, getValueOrNull }; + +const PropertyUtils = { + hasProperty, + getValue, + getEnumValue +}; + +export default PropertyUtils; \ No newline at end of file diff --git a/playwright-tests/impl/utils/TaggingUtils.ts b/playwright-tests/impl/utils/TaggingUtils.ts new file mode 100644 index 00000000..00a99fae --- /dev/null +++ b/playwright-tests/impl/utils/TaggingUtils.ts @@ -0,0 +1,32 @@ +export function formatTag(text: string): string { + return "@" + text.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, (match, index) => { + if (+match === 0) return ""; // Remove non-alphanumeric characters + return index === 0 ? match.toLowerCase() : match.toUpperCase(); + }); +} +/** + * @description - Use this to add calculated/standardized/randomized tags to scenarioData prior to running. Randomly adds a smoke tag to 1 of the testCases. + * @param scenarioData - the data from your test case + * @returns scenarioData, but with added tags. + */ + +export function getRandomTestName(scenarioData: any) : string { + const testCaseNames: string[] = Object.keys(scenarioData); + const totalKeys = testCaseNames.length + const randomIndex = Math.floor(Math.random() * totalKeys) - 1; + const randomKey = testCaseNames[randomIndex]; + + return randomKey; +} + +export function metaTags(currentTestName: string, randomTestName: string) : string [] { + return currentTestName == randomTestName ? ["@smoke", "@standardRegression"] : ["@standardRegression"] +} + +export function matchAndReplaceContactDataTag(data: string[], replacement: string): string[] { + return data.map((value) => value.replace(/[{]{2}contact[}]{2}/, replacement)); +} + +export function matchAndReplaceAccountDataTag(data: string[], replacement: string): string[] { + return data.map((value) => value.replace(/[{]{2}account[}]{2}/, replacement)); +} diff --git a/playwright-tests/impl/utils/ThrowUtils.ts b/playwright-tests/impl/utils/ThrowUtils.ts new file mode 100644 index 00000000..2debfca3 --- /dev/null +++ b/playwright-tests/impl/utils/ThrowUtils.ts @@ -0,0 +1,12 @@ +import { error } from "console"; + + +export function throwIf(conditionFunction: () => boolean, errorMessage: string): void { + if (conditionFunction()) + throw new Error(errorMessage); +} + + +export function throwNotYetImplemented(nameOfThingNotImplemented: string) { + throw new Error(`${nameOfThingNotImplemented} has not yet been implemented.`) +} \ No newline at end of file diff --git a/playwright-tests/impl/utils/TimingUtils.ts b/playwright-tests/impl/utils/TimingUtils.ts new file mode 100644 index 00000000..ab0dbafe --- /dev/null +++ b/playwright-tests/impl/utils/TimingUtils.ts @@ -0,0 +1,273 @@ +import { expect, Locator, Page } from "@playwright/test"; +import LoggingUtils from "./LoggingUtils"; +import { DateTime } from "luxon"; + + +export type TimeoutOpts = { + /** + * @description timeout Commonly referred to for an entire method; exists to allow developers to specify their own timeout whout overriding defaults + */ + + timeout: number + /** + * @description timeout_tiny use for exceedingly small waits, as in, waiting for label to contain the test you just typed into it. + */ + timeoutTiny: number + + /** + * @description timeout_short for relatively quick opperations, such as waiting for a dropdown to render + */ + timeoutShort: number + + /** + * @description timeout_medium for moderately slow operations, such as a new modal rendering, or a calculated field value being updated, or an API Call + */ + timeoutMedium: number + + /** + * @description timeout_long for high-risk, slow operations. Waiting for the minicart to load, waiting for login, or waiting for screen-to-screen navigation. + */ + timeoutLong: number + + /** + * @description for when things are really, really bad. + */ + timeoutConga: number +} + +export const timeoutOptDefaults: TimeoutOpts = { + timeout: 60_000, + timeoutTiny: +(process.env.TIMEOUT_TINY ?? 500), + timeoutShort: +(process.env.TIMEOUT_SHORT ?? 5000), + timeoutMedium: +(process.env.TIMEOUT_MEDIUM ?? 30_000), + timeoutLong: +(process.env.TIMEOUT_LONG ?? 180_000), + timeoutConga: +(process.env.TIMEOUT_CONGA ?? 500_000), +} + +export type WaitUntilOpts = { + delayBetweenChecks: number, + continueOnTimeoutError: boolean, + anticipatedConditionResult: boolean, + conditionName: string, + beginWaitingMessage: string, + delayBetweenChecksMessage: string, + timeoutErrorMessage: string, + successMessage: string, + ignoreErrorsFromConditionFunction: boolean, + +} +export const waitUntilOptDefaults: WaitUntilOpts = { + delayBetweenChecks: 3000, + continueOnTimeoutError: false, + anticipatedConditionResult: true, + conditionName: "", + beginWaitingMessage: "", + delayBetweenChecksMessage: "", + timeoutErrorMessage: "Timed Out", + successMessage: "", + ignoreErrorsFromConditionFunction: true, +} + +/** + * @description repeatedly execute an asynchronous conditional lambda until a given outcome occurs, or the method times-out. Useful for hedgning against GUI race conditions. + * @param conditionFunction the condition lambda. Example: ()=>{await return myPage.someButton.isVisible()} + * @param options standard Timeout and WaitUntil Options. + * @returns true or false - the outcome of the waituntil. + */ + +export async function waitUntil(conditionFunction: (...args: any[]) => Promise, options: Partial = {}): Promise { + const opts = { ...waitUntilOptDefaults, ...timeoutOptDefaults, ...options } + + let timeoutAt = Date.now() + opts.timeout; + let waitUntilHasTimedOut = false; + let conditionHasBeenMet = false; + do { + try { + const conditionResult = await conditionFunction() + conditionHasBeenMet = conditionResult == opts.anticipatedConditionResult + } + catch (e) { + if (!opts.ignoreErrorsFromConditionFunction) { + throw e + } + } + waitUntilHasTimedOut = Date.now() > timeoutAt + if (!waitUntilHasTimedOut && !conditionHasBeenMet) { + await delay(opts.delayBetweenChecks) + } + else if (waitUntilHasTimedOut && !opts.continueOnTimeoutError) { + throw new Error(opts.timeoutErrorMessage) + } + } + while (!conditionHasBeenMet || waitUntilHasTimedOut) + return conditionHasBeenMet; +} + +/** + * @description Order Matters; WaitUntil each condition passes before moving to the next. All conditions must pass in the expected order. + * @param sequentialConditionFunctions an array of async, boolean lambdas to be executed in sqeuance until all have passed. + * @param options standard Timeout and WaitUntil options + */ +export async function waitUntilValueStopsChanging(mercurialValueFunction: (...args: any[]) => Promise, options: Partial = {}): Promise { + const opts = { ...waitUntilOptDefaults, ...timeoutOptDefaults, ...options } + + let lastFoundValue: any = undefined; + const valueHasStoppedChanging = async () => { + const newFoundValue = await mercurialValueFunction(); + if (opts.delayBetweenChecksMessage.length > 0) console.log(`${opts.delayBetweenChecksMessage} - Last Value: ${lastFoundValue}`) + const valueIsStable = (newFoundValue != undefined) && (newFoundValue == lastFoundValue); + lastFoundValue = newFoundValue; + return valueIsStable; + } + await waitUntil(valueHasStoppedChanging, opts) + return lastFoundValue; +} + +/** + * @todo EXPIRIMENTAL! NO UNIT TESTS YET! TODO, add - DF, 5/23 + * @description Order Matters; WaitUntil each condition passes before moving to the next. All conditions must pass in the expected order. + * @param sequentialConditionFunctions an array of async, boolean lambdas to be executed in sqeuance until all have passed. + * @param options standard Timeout and WaitUntil options + */ +export async function waitUntilEach(sequentialConditionFunctions: ((...args: any[]) => Promise)[], options: Partial = {}): Promise { + for (const conditionFunction of sequentialConditionFunctions) { + await waitUntil(conditionFunction, options); + } +} + +/** + * @todo EXPIRIMENTAL! NO UNIT TESTS YET! TODO, add - DF, 5/23 + * @description Order Matters; WaitUntil each condition passes before moving to the next. All conditions must pass in the expected order. + * @param sequentialConditionFunctions an array of async, boolean lambdas to be executed in sequence until all have passed. + * @param options standard Timeout and WaitUntil options + */ +export async function waitUntilAll(sequentialConditionFunctions: ((...args: any[]) => Promise)[], options: Partial = {}): Promise { + for (const conditionFunction of sequentialConditionFunctions) { + await waitUntil(conditionFunction, options); + } +} + +/** + * @todo EXPIRIMENTAL! NO UNIT TESTS YET! TODO, add - DF, 5/23 + * @description Order DOES NOT Matter; WaitUntil ANY condition passes before completing. Use when multiple conditions can give confidence that sufficient waiting has occured. + * @param sequentialConditionFunctions an array of async, boolean lambdas to be executed in psudo-parallel until at least one has passed. + * @param options standard Timeout and WaitUntil options + */ +export async function waitUntilAny(multipleRequiredConditionFunctions: ((...args: any[]) => Promise)[], options: Partial = {}): Promise { + const anyConditionMet = async (): Promise => { + return multipleRequiredConditionFunctions.filter(async (fun: (...args: any[]) => Promise): Promise => await Function.call(fun)).length > 0; + }; + waitUntil(anyConditionMet, options) +} + +/** + * @param milliseconds delay duration + * @param logDelay defaults to false; if true, logs a waiting message. + */ +export async function delay(milliseconds: number, logDelay = false): Promise { + if (logDelay) { console.log(`delaying ${milliseconds} milliseconds before continuing...`) } + return new Promise((resolve) => setTimeout(resolve, milliseconds)); +} + +/** + * Waits for the page url + * @param partialUrl The string we are looking for in the url, to know we have transitioned to the correct page. + * @param timeout the amount of milliseconds to wait before giving up. + */ +export async function waitForUrlPartialMatch(page: Page, firstPartialUrl: string, timeout = 120_000) { + const startTime = Date.now(); + while (Date.now() - startTime < timeout) { + if (page.url().includes(firstPartialUrl)) { + return; // URL matches the partial string, exit the function + } + await page.waitForTimeout(100); // Wait for 100 milliseconds before checking again + } + throw new Error(`Timed out waiting for URL to match '${firstPartialUrl}'`); +} + +/** + * Run a function repeatedly until it returns true or the timeout is reached. + * + * This function executes the provided asynchronous block function in a loop until it returns true or the specified + * timeout duration has elapsed. Between each attempt, it waits for a specified delay. + * + * @param block - An asynchronous function that returns a boolean value. This function will be executed repeatedly until it returns true. + * @param timeout - The maximum duration to keep attempting to run the block function, in milliseconds. Default is 150000 (150 seconds). + * @param delayMs - The delay duration between each attempt, in milliseconds. Default is 3000 (3 seconds). + * @returns A promise that resolves to a boolean value indicating whether the block function eventually returned true. + * + * @example + * // Example usage: + * const blockFunction = async () => { + * // Some asynchronous condition check + * return await someConditionCheck(); + * }; + * const result = await runUntilTrue(blockFunction, 10000, 1000); + * console.log(result); // Outputs true if blockFunction returned true within the timeout, otherwise false. + */ +export async function runUntilTrue(block: () => Promise, timeout: number = 150000, delayMs: number = 3000){ + const startTime = DateTime.now(); + let attempts = 0; + let evaluatesToTrue = false; + + do { + // If timeout duration has elapsed, stop making attempts + if (DateTime.now().diff(startTime).as('milliseconds') > timeout) { + break; + } + + attempts++; + // If retrying, wait delay duration + if (attempts > 1) await delay(delayMs); + + evaluatesToTrue = await block(); + + } while (!evaluatesToTrue); + + return evaluatesToTrue; +} + +// TODO move this to impl/utils/WaitingUtils when that related pr is available in devleop branch - T.S. 5/20/24 +export async function waitForEither(block1: () => Promise, block2: () => Promise, timeOut: number = 180_000): Promise { + const startTime = Date.now(); + + while (true) { + try { + const result1 = await block1(); + const result2 = await block2(); + + // Check if either result is truthy (i.e., not falsy or undefined) + if (result1 || result2) { + // At least one block returned a truthy value, resolve the promise + return; + } + } catch (error) { + // Handle errors thrown by either block + console.error("An error occurred:", error); + } + + // Check if the timeout has been reached + if (Date.now() - startTime >= timeOut) { + throw new Error(`Timeout of ${timeOut} ms exceeded`); + } + + // Add some delay before checking again + await new Promise(resolve => setTimeout(resolve, 1000)); // Adjust delay as needed + } +} + +/** + * Waits for a specific locator to show up on screen, then disappear. Typically used for things like progress bars. + * @param locator The locator we want to become visible and then become hidden + */ +export async function waitToAppearAndDisappear(locator: Locator): Promise { + try { + await expect(locator).toBeVisible({ timeout: 60000 }); + await expect(locator).toBeHidden({ timeout: 60000 }); + } catch (err) { + if (err instanceof Error) + console.log(LoggingUtils.logFunc(waitToAppearAndDisappear.name, err.message, false)); + else + console.log(LoggingUtils.logFunc(waitToAppearAndDisappear.name, null, false)); + } +} \ No newline at end of file diff --git a/playwright-tests/impl/utils/TokenUtils.ts b/playwright-tests/impl/utils/TokenUtils.ts new file mode 100644 index 00000000..39ceb125 --- /dev/null +++ b/playwright-tests/impl/utils/TokenUtils.ts @@ -0,0 +1,34 @@ +import { IClientAuthentication } from "@business-logic/types/Authentication"; +import { Authentication } from "@business-logic/types/Enums"; + +export function buildToken(clientAuth: IClientAuthentication, formData: Map): string { + if(clientAuth.authentication === undefined + || clientAuth.authentication === Authentication.None + || clientAuth.authentication === Authentication.Unknown + ) { + return ''; + } + + let token = (formData.get('Token') ?? formData.get("Timestamp") ?? ''); + if(clientAuth.authentication === Authentication.RSAToken) { + return token; + } + + for(const [id, value] of formData.entries()) { + token += `|${id.toLowerCase()}=${value}` + } + + return token; +} + +export function getTimestamp(): string { + const time = new Date(Date.now()); + const formatOptions :Intl.DateTimeFormatOptions = { + hour12: false, + dateStyle: "short", + timeStyle: "medium" + } + const string = time.toLocaleString('en-US', formatOptions); + let timestamp = string.replaceAll("/","").replaceAll(", ","").replaceAll(":",""); + return timestamp; +} \ No newline at end of file diff --git a/playwright-tests/impl/utils/TryUtils.ts b/playwright-tests/impl/utils/TryUtils.ts new file mode 100644 index 00000000..de30a069 --- /dev/null +++ b/playwright-tests/impl/utils/TryUtils.ts @@ -0,0 +1,132 @@ +import LoggingUtils from "./LoggingUtils"; +import { delay, timeoutOptDefaults } from "./TimingUtils"; + +/** + * @description For situations where a Test Framework Exception (i.e., a locator timeout) could be incorrectly thrown based on the state of the Target-Application (i.e., a missing tail number). Use this to throw clearer 'Application Exception' errors under such circumstances. + * @param actionVerb What are you trying to do? Could be 'gotoCatalog', 'addAnEnhancement', etc. Logged as `Attempting to ${actionVerb}` + * @param failureExplenation Be descriptive. What is the context of the failure? If someone unfamiliar with the code base were to see this, how would they know if the error was caused by their code, or by an underlying problem with the Target Application? + * @param actionToAttempt a lambda for the flaky action. + * @returns + */ +export async function tryBusinessAction(actionVerb: string, failureExplenation: string, actionToAttempt: (...args: any[]) => Promise): Promise { + let actionResult: any; + console.log(`Attempting to ${actionVerb}...`) + try { + actionResult = await actionToAttempt(); + } catch (e) { + if (e instanceof Error) { + e.message = e.message + ">>Failure Explenation>> " + failureExplenation; + throw (e); + } else { + throw (new Error("Unknown 'AttemptBusinessAction' State...")) + } + } + console.log(`Successfully executed ${actionVerb}`) + return actionResult; +} + +/** + * @description Brute-Force Flaky GUI activities by reseting and retrying. + * @param actionToTry lambda for whatever flaky action you're trying to take + * @param resetAction lambda for backing out of the problem and returning to a known state. Often, refreshing a browser, or closing a popup. + * @param maxRetries number of times to retry the action + * @param delayBetweenRetries milliseconds between retries + */ +export async function tryResetAndRetry( + actionVerb: string, + actionToTry: (...args: any[]) => Promise, + resetAction: (...args: any[]) => Promise, + maxRetries = 2, + delayBetweenRetries = timeoutOptDefaults.timeoutMedium): Promise { + + for (let i = 1; i <= maxRetries; i++) { + try { + await actionToTry() + } catch { + await delay(delayBetweenRetries); + resetAction(); + } + } +} + +/** + * @description For situations where a Test Framework Exception (i.e., a locator timeout) could be incorrectly thrown based on the state of the Target-Application (i.e., a missing tail number). Use this to throw clearer 'Application Exception' errors under such circumstances. + * @param actionVerb What are you trying to do? Could be 'gotoCatalog', 'addAnEnhancement', etc. Logged as `Attempting to ${actionVerb}` + * @param failureExplenation Be descriptive. What is the context of the failure? If someone unfamiliar with the code base were to see this, how would they know if the error was caused by their code, or by an underlying problem with the Target Application? + * @param actionToAttempt a lambda for the flaky action. + * @returns + */ +export async function tryBusinessActionWithRetries(actionVerb: string, failureExplenation: string, actionToAttempt: (...args: any[]) => Promise, attempts = 3): Promise { + let actionResult: any; + let isSuccessful: boolean = false; + //actionVerb is already a logFunc string + console.log(actionVerb); + + for (let i = 0; i < attempts; i++) { + try { + actionResult = await actionToAttempt(); + isSuccessful = true; + break; // Break loop if actionToAttempt is successful + } catch (e) { + if (e instanceof Error) { + e.message = e.message + ">>Failure Explanation>> " + failureExplenation; + } else { + throw (new Error("Unknown 'AttemptBusinessAction' State")) + } + } + } + + if (!isSuccessful) { + throw new Error(`Failed to ${actionVerb}`); + } + + //actionVerb is already a logFunc string + console.log(actionVerb); + return actionResult; +} + +/** + * Tries to execute a block of code with chances to retry. + * @param {Function} block The block of code to be executed. + * @param {string} [blockDescription=''] A text description of the block (optional). + * @param {number} [maxRetries=3] The maximum number of retry attempts (optional). + * @param {number} [delayMs=1000] The delay between retry attempts in milliseconds (optional). + */ +export async function retry(block: () => Promise, blockDescription: string = '', maxRetries: number = 3, delayMs: number = 1000): Promise { + let retries: number = 0; + if (!blockDescription.length) { + blockDescription = block.toString(); + } + + while (retries < maxRetries) { + console.log(LoggingUtils.logFunc(retry.name, blockDescription)); + try { + return await block(); + } catch (error) { + if (retries === maxRetries - 1) { + throw new Error(`Max retries (${maxRetries}) exceeded. Last error: ${error}`); + } + // wait between retries + await new Promise(resolve => setTimeout(resolve, delayMs)); + retries++; + } + } + // This should not be reached, but just in case + throw new Error(`Unexpected code execution. Max retries (${maxRetries}) exceeded.`); +} + +export async function tryWithRetries(actionBlock: Function, attempts = 3, waitInterval = 1000) { + for (let attempt = 1; attempt <= attempts; attempt++) { + try { + await actionBlock(); + if (attempt > 1) + console.warn(`Had to retry but attempt ${attempt} succeeded!`); + break; // Exit the loop if the action is successful + } catch (error) { + console.error(`Attempt ${attempt} failed!`); + if (attempt < attempts) { + await new Promise(resolve => setTimeout(resolve, waitInterval)); + } + } + } +} \ No newline at end of file diff --git a/playwright-tests/pages/AfterpayPage.ts b/playwright-tests/pages/AfterpayPage.ts new file mode 100644 index 00000000..4691163f --- /dev/null +++ b/playwright-tests/pages/AfterpayPage.ts @@ -0,0 +1,56 @@ +import { Locator, Page } from "@playwright/test"; +import { BasePage } from "./BasePage"; +import { IPaymentDetails } from "@business-logic/types/CustomerDetails"; + +export class AfterpayPage extends BasePage { + readonly page: Page; + readonly submitButton: Locator; + + // Login + readonly passwordTextBox: Locator; + + // Card details + readonly cardholderNameTextBox: Locator; + readonly cardNumberTextBox: Locator; + readonly expirationDateTextBox: Locator; + readonly cvvTextBox: Locator; + + readonly confirmButton: Locator; + + constructor(page: Page) { + super(page); + this.page = page; + this.passwordTextBox = page.getByRole('textbox', { name: 'Please enter your password' }); + this.submitButton = page.getByRole('button', { name: 'Continue' }); + + this.cardholderNameTextBox = page.getByTestId('payment-method-cardHolderName-input'); + this.cardNumberTextBox = page.getByTestId('payment-method-cardNumber-input'); + this.expirationDateTextBox = page.getByTestId('payment-method-cardExpiry-input'); + this.cvvTextBox = page.getByTestId('payment-method-cardCvv-input'); + + this.confirmButton = page.getByRole('button', { name: 'Confirm' }); + } + + async login(password: string) { + await this.passwordTextBox.fill(password); + await this.submitButton.click(); + } + + async populateCardDetails(paymentDetails: IPaymentDetails) { + await this.cardholderNameTextBox.fill('Roberts'); // TODO: Add cardholder name field + await this.cardNumberTextBox.fill(paymentDetails.cardNumber!); + await this.expirationDateTextBox.fill(`${paymentDetails.expirationMonth}/${paymentDetails.expirationYear}`); + await this.cvvTextBox.fill(paymentDetails.cvv!); + await this.submitButton.click(); + } + + async executeAfterpayPayment(paymentDetails: IPaymentDetails) { + await this.login(paymentDetails.password!); + + await this.populateCardDetails(paymentDetails); + + await this.confirmButton.click(); + } + + +} \ No newline at end of file diff --git a/playwright-tests/pages/BailoutPage.ts b/playwright-tests/pages/BailoutPage.ts new file mode 100644 index 00000000..dbbfcf67 --- /dev/null +++ b/playwright-tests/pages/BailoutPage.ts @@ -0,0 +1,49 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { ICustomerDetails } from '@business-logic/types/CustomerDetails'; + +export class BailoutPage extends BasePage { + readonly page: Page; + readonly firstNameTextBox: Locator; + readonly lastNameTextBox: Locator; + readonly phoneNumberTextBox: Locator; + readonly emailAddressTextBox: Locator; + + url = process.env['BASE_URL']! + '/?issPage=bailout-page'; + + constructor(page: Page) { + super(page); + this.page = page; + this.firstNameTextBox = this.page.locator('#firstNameField'); + this.lastNameTextBox = this.page.locator('#lastNameField'); + this.phoneNumberTextBox = this.page.locator('#phoneNumberField'); + this.emailAddressTextBox = this.page.locator('#emailAddressField'); + // this.page.waitForLoadState(); + // this.validateURL(this.url); + } + + async validateBailoutDetails(customerDetails: ICustomerDetails, bailoutCode: number) { + + await this.firstNameTextBox.waitFor({state:'visible'}); + await expect.soft(this.firstNameTextBox).toHaveValue(customerDetails.firstName); + await expect.soft(this.lastNameTextBox).toHaveValue(customerDetails.lastName); + await expect.soft(this.phoneNumberTextBox).toHaveValue(customerDetails.phoneNumber); + await expect.soft(this.emailAddressTextBox).toHaveValue(customerDetails.email); + await expect.soft(await this.getBailoutCode()).toEqual(bailoutCode); + } + + async validateBailoutDetailsNotNull(){ + await this.firstNameTextBox.waitFor({state:'visible'}) + expect(this.firstNameTextBox.inputValue()).not.toBe(''); + expect(this.lastNameTextBox.inputValue()).not.toBe(''); + expect(this.phoneNumberTextBox.inputValue()).not.toBe(''); + expect(this.emailAddressTextBox.inputValue()).not.toBe(''); + } + + async getBailoutCode() { + const mainLocalStorage = JSON.parse(await this.page.evaluate('localStorage.getItem(\'main\')')); + const bailoutCode = mainLocalStorage.applicationUser.pageData['bailout-page'].bailoutCode as number; + return bailoutCode; + } + +} \ No newline at end of file diff --git a/playwright-tests/pages/BasePage.ts b/playwright-tests/pages/BasePage.ts new file mode 100644 index 00000000..99c7a584 --- /dev/null +++ b/playwright-tests/pages/BasePage.ts @@ -0,0 +1,41 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +export class BasePage { + readonly page: Page; + readonly continueButton: Locator; + readonly pageSpinner: Locator; + readonly buttonLoadSpin: Locator; + + constructor(page: Page){ + this.page = page; + this.continueButton = page.locator('[id="infoBox"]').getByRole('button'); + this.pageSpinner = page.getByRole('status'); + this.buttonLoadSpin = page.getByRole('alert'); + } + + async nextPage() { + const startingUrl = this.page.url(); + await expect(async () => { + const currentUrl = this.page.url(); + if (currentUrl === startingUrl) { + await this.continueButton.click({ timeout: 1000 }); + } + //this causes the schedule page to fail + //await expect(this.buttonLoadSpin).toHaveCount(0, {timeout: 180000}); + expect(currentUrl).not.toEqual(startingUrl); + }).toPass({ timeout: 240_000 }); + } + + async validateURL(url:string){ + await expect(this.pageSpinner).toHaveCount(0, {timeout: 60000}); + await this.page.waitForURL(url); + } + + async fillAndValidate(element: Locator, value: string){ + await expect(async () => { + await element.clear(); + await element.fill(value); + await expect(element).toHaveValue(value); + }).toPass(); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/CapabilityQuestionsPage.ts b/playwright-tests/pages/CapabilityQuestionsPage.ts new file mode 100644 index 00000000..57ec7745 --- /dev/null +++ b/playwright-tests/pages/CapabilityQuestionsPage.ts @@ -0,0 +1,10 @@ +import { Page } from "@playwright/test"; +import { PartQuestionsPage } from "./PartQuestionsPage"; + +export default class CapabilityQuestionsPage extends PartQuestionsPage { + url = process.env['BASE_URL']! + '/?issPage=capability-questions'; + + constructor(page: Page) { + super(page); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/ContactConfirmationPage.ts b/playwright-tests/pages/ContactConfirmationPage.ts new file mode 100644 index 00000000..c3b7bfc9 --- /dev/null +++ b/playwright-tests/pages/ContactConfirmationPage.ts @@ -0,0 +1,17 @@ +import { type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { ICustomerDetails } from '@business-logic/types/CustomerDetails'; + +export class ContactConfirmationPage extends BasePage { + readonly page: Page; + readonly ConfirmMessageLabel: Locator; + + url = process.env['BASE_URL']! + '/?issPage=contact-confirmation'; + + constructor(page: Page) { + super(page); + this.page = page; + this.ConfirmMessageLabel = this.page.getByText("Your callback request has been sent!"); + } + +} \ No newline at end of file diff --git a/playwright-tests/pages/ContactDetailsPage.ts b/playwright-tests/pages/ContactDetailsPage.ts new file mode 100644 index 00000000..731ccf66 --- /dev/null +++ b/playwright-tests/pages/ContactDetailsPage.ts @@ -0,0 +1,45 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { ICustomerDetails } from '@business-logic/types/CustomerDetails'; + +export class ContactDetailsPage extends BasePage { + readonly page: Page; + url = process.env['BASE_URL']! + '/?issPage=contact-details'; + + // Contact details form + // TODO: Check if we can consolidate + readonly firstNameTextBox: Locator; + readonly lastNameTextBox: Locator; + readonly emailAddressTextBox: Locator; + readonly phoneNumberTextBox: Locator; + readonly notesTextBox: Locator; + + constructor(page: Page) { + super(page); + this.page = page; + + // Contact details form + this.firstNameTextBox = this.page.getByRole('textbox', { name: 'First name' }); + this.lastNameTextBox = this.page.getByRole('textbox', { name: 'Last name' }); + this.emailAddressTextBox = this.page.getByRole('textbox', { name: 'Email address' }); + this.phoneNumberTextBox = this.page.getByRole('textbox', { name: 'Phone number' }); + this.notesTextBox = this.page.getByRole('textbox', { name: 'Notes' }); + } + + + async getContactDetails() { + const customerDetails: Partial = {}; + customerDetails.firstName = await this.firstNameTextBox.inputValue(); + customerDetails.lastName = await this.lastNameTextBox.inputValue(); + customerDetails.email = await this.emailAddressTextBox.inputValue(); + customerDetails.phoneNumber = await this.phoneNumberTextBox.inputValue(); + + return customerDetails; + } + + async fillNotes(notes?: string) { + if (notes) { + await this.notesTextBox.fill(notes); + } + } +} \ No newline at end of file diff --git a/playwright-tests/pages/CoverageStatementPage.ts b/playwright-tests/pages/CoverageStatementPage.ts new file mode 100644 index 00000000..147bb19f --- /dev/null +++ b/playwright-tests/pages/CoverageStatementPage.ts @@ -0,0 +1,39 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; + +export class CoverageStatementPage extends BasePage { + readonly page: Page; + readonly scheduleOnlineButton: Locator; + readonly cancelMyClaimButton: Locator; + readonly deductibleAmount: Locator; + readonly verfiyingCoverageText: Locator; + readonly continueToScheduleButton: Locator; // For ITAC/NoComp + url = process.env['BASE_URL']! + '/?issPage=coverage-statement'; + + constructor(page: Page) { + super(page); + this.page = page; + this.scheduleOnlineButton = this.page.getByText('Continue to schedule online'); + this.cancelMyClaimButton = this.page.getByText('Cancel my claim'); + this.deductibleAmount = this.page.getByText('$'); + this.verfiyingCoverageText = this.page.getByRole('heading', { name: 'We’re verifying your coverage' }); + this.continueToScheduleButton = page.locator('div[class*="button-content"]', {hasText:'Continue to schedule online'}); + // this.validateURL(this.url); + } + + async scheduleOnline(){ + await this.scheduleOnlineButton.click(); + } + + async cancelMyClaim(){ + await this.cancelMyClaimButton.click(); + } + + async validateDeductibleAmount(customer){ + await expect(this.deductibleAmount). toContainText(`$${customer.deductibleAmount}`, {timeout: 60000}); + } + + async validateUnverifiedText(){ + await expect(this.verfiyingCoverageText).toBeEnabled(); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/DuplicateCheckPage.ts b/playwright-tests/pages/DuplicateCheckPage.ts new file mode 100644 index 00000000..10518b3f --- /dev/null +++ b/playwright-tests/pages/DuplicateCheckPage.ts @@ -0,0 +1,20 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; + +export class DuplicateCheckPage extends BasePage { + readonly page: Page; + readonly newClaimButton: Locator; + url = process.env['BASE_URL']! + '/?issPage=duplicate-check'; + + constructor(page: Page) { + super(page); + this.page = page; + this.newClaimButton = page.locator('label').filter({ hasText: 'Start a new claim' }).locator('div'); + // this.validateURL(this.url); + } + + async startNewClaim(){ + await this.newClaimButton.click(); + await this.page.waitForLoadState(); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/EndorsementsPage.ts b/playwright-tests/pages/EndorsementsPage.ts new file mode 100644 index 00000000..1e86773f --- /dev/null +++ b/playwright-tests/pages/EndorsementsPage.ts @@ -0,0 +1,54 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IEndorsementDetails } from '@business-logic/types/CustomerDetails'; +import { EndorsementType } from '@business-logic/types/Enums'; + +export class EndorsementsPage extends BasePage { + readonly page: Page; + readonly educatorYesButton: Locator; + readonly educatorNoButton: Locator; + url = process.env['BASE_URL']! + '/?issPage=policy-endorsements'; + + constructor(page: Page) { + super(page); + this.page = page; + this.educatorYesButton = this.page.locator('label[for="schoolProperty-Yes"]'); + this.educatorNoButton = this.page.locator('label[for="schoolProperty-No"]'); + } + + async verifyEndorsements(endorsements: IEndorsementDetails[]) { + for (const endorsement of endorsements) { + switch(endorsement.endorsementType) { + case EndorsementType.Educator: + if (endorsement.isOnPolicy) { + await expect.soft(this.educatorYesButton).toBeAttached(); + } else { + await expect.soft(this.educatorYesButton).not.toBeAttached(); + } + break; + case EndorsementType.EmployeeParking: + // TODO: Implement + break; + } + } + } + + async selectEndorsements(endorsements: IEndorsementDetails[]) { + for (const endorsement of endorsements) { + if (endorsement.isOnPolicy) { + switch (endorsement.endorsementType) { + case EndorsementType.Educator: + if (endorsement.isClickYes) { + await this.educatorYesButton.click(); + } else { + await this.educatorNoButton.click(); + } + break; + case EndorsementType.EmployeeParking: + // TODO: Implement + break; + } + } + } + } +} \ No newline at end of file diff --git a/playwright-tests/pages/OrderConfirmationPage.ts b/playwright-tests/pages/OrderConfirmationPage.ts new file mode 100644 index 00000000..7f809d44 --- /dev/null +++ b/playwright-tests/pages/OrderConfirmationPage.ts @@ -0,0 +1,115 @@ +import test, { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { ICustomerDetails, IVehicleDetails } from '@business-logic/types/CustomerDetails'; +import { PaymentType, ServicePackage } from '@business-logic/types/Enums'; +import { ITestData } from '@business-logic/types/ITestData'; + +export class OrderConfirmationPage extends BasePage { + readonly page: Page; + readonly serviceText: Locator; + readonly emailText: Locator; + readonly apptDateText: Locator; + readonly amountDueText: Locator; + readonly viewCartButton: Locator; + readonly deductibleText: Locator; + readonly subtotalText: Locator; + readonly finalAmountDue: Locator; + readonly cartServicePackageText: Locator; + url = process.env['BASE_URL']! + '/?issPage=order-confirmation'; + + constructor(page: Page) { + super(page); + this.page = page; + this.serviceText = this.page.locator('[class="appointment-text text-center lh-base"]'); + this.emailText = this.page.locator('[class="email-confirmation-text"]'); + this.apptDateText = this.page.locator('[class="appointment-date-time text-center mt-4"]'); + this.amountDueText = this.page.getByLabel('expand cart details'); + this.viewCartButton = this.page.locator('#cart-dropdown-head'); + this.deductibleText = this.page.locator('#deductible-value'); + this.subtotalText = this.page.locator("#subtotal-value"); + this.finalAmountDue = this.page.locator('#bottom-amount-due-value'); + this.cartServicePackageText = this.page.locator('#cart-service-package'); + // this.validateURL(this.url); + } + + async validateOrderConfirmationPage(testData: Partial) { + // Destructure data we use + const { vehicleDetails, customerDetails, servicePackage, isItac, + isNoComp, isPolicyFound, claimDetails, paymentDetails } = testData; + await this.serviceText.waitFor({ state: "visible" }); + await this.logOrderNumber(); + + // 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(); + const deductibleTextValue = (isItac || isNoComp)? null: await this.deductibleText.textContent(); + const subtotalTextValue = await this.subtotalText.textContent(); + const finalAmountDueValue = await this.finalAmountDue.textContent(); + + // Extract service package price + 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 + await expect.soft(this.cartServicePackageText).toContainText(`${servicePackage}`) + if (servicePackage === ServicePackage.Premium || servicePackage === ServicePackage.Standard) { + expect.soft(servicePackageValue).toContain('New wiper blades'); + } + if (servicePackage === ServicePackage.Premium) { + expect.soft(servicePackageValue).toContain('Rain Defense™ treatment'); + } + + // Price validations + if (servicePackage === ServicePackage.GlassOnly) { + expect.soft(servicePackageAmt).toEqual(0); + } else { + expect.soft(servicePackageAmt).toBeGreaterThan(0); + } + + if (isPolicyFound) { + // Extract numbers + const amountDueAmt = Number.parseFloat(amountDueValue!.split('$')[1].replaceAll(',', '')); + const deductibleAmt = deductibleTextValue? Number.parseFloat(deductibleTextValue.split('$')[1].replaceAll(',', '')): 0; + const subtotalAmt = Number.parseFloat(subtotalTextValue!.split('$')[1].replaceAll(',', '')); + subtotalTextValue?.replaceAll(',', '') + const finalAmountDueAmt = Number.parseFloat(finalAmountDueValue!.split('$')[1].replaceAll(',', '')); + + if (!(isItac || isNoComp)) { + expect.soft(subtotalAmt).toEqual(claimDetails!.policyDeductible + servicePackageAmt); + expect.soft(deductibleAmt).toEqual(claimDetails!.policyDeductible); + } else { + expect.soft(subtotalAmt).toBeGreaterThan(0); + expect.soft(deductibleAmt).toEqual(0); + } + + if (paymentDetails!.paymentType === PaymentType.PayAtService && (claimDetails!.policyDeductible > 0 || servicePackageAmt > 0)) { + // Verify amount due > 0 + expect.soft(amountDueAmt).toBeGreaterThan(0); + expect.soft(finalAmountDueAmt).toBeGreaterThan(0); + } else { + // Verify amount due 0 + expect.soft(amountDueAmt).toEqual(0); + expect.soft(finalAmountDueAmt).toEqual(0); + } + } else { + expect.soft(amountDueValue).toContain('Verifying coverage'); + expect.soft(subtotalTextValue).toEqual('Verifying coverage'); + expect.soft(finalAmountDueValue).toEqual('Verifying coverage'); + } + } + + async logOrderNumber() { + const sessionStorage = JSON.parse(await this.page.evaluate('sessionStorage.getItem(\'submittedOrder\')')); + const workOrderNumber = sessionStorage.workOrderNumber; + await test.step(`SessionStorage Work Order Number:${workOrderNumber}`, async () => { + console.log(`SessionStorage Work Order Number:${workOrderNumber}`); + }); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/PartQuestionsPage.ts b/playwright-tests/pages/PartQuestionsPage.ts new file mode 100644 index 00000000..46249387 --- /dev/null +++ b/playwright-tests/pages/PartQuestionsPage.ts @@ -0,0 +1,35 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IPartQuestion } from '@business-logic/types/CustomerDetails'; + +export class PartQuestionsPage extends BasePage { + readonly page: Page; + url = process.env['BASE_URL']! + '/?issPage=part-questions'; + + constructor(page: Page) { + super(page); + this.page = page; + } + + async validatePartQuestions(partQuestions: IPartQuestion[]) { + for (const pq of partQuestions) { + const partQuestionOptions = this.page.locator(`fieldset[aria-labelledby="${pq.partQuestionType}"]`); + if (pq.isOnPage) { + await expect(partQuestionOptions).toBeAttached(); + } else { + await expect(partQuestionOptions).not.toBeAttached(); + } + } + } + + async selectPartQuestionResponses(partQuestions: IPartQuestion[]) { + for (const pq of partQuestions) { + const partQuestionOptionButton = this.page.locator(`fieldset[aria-labelledby="${pq.partQuestionType}"]`).locator(`[buttonlabel="${pq.optionToSelect}"]`); + await partQuestionOptionButton.click(); + if (pq.secondaryQuestionOptionToSelect != null) { + const secondaryQuestionButton = this.page.getByText(`${pq.secondaryQuestionOptionToSelect}`); + secondaryQuestionButton.click(); + } + } + } +} \ No newline at end of file diff --git a/playwright-tests/pages/PaymentMethodPage.ts b/playwright-tests/pages/PaymentMethodPage.ts new file mode 100644 index 00000000..57b67370 --- /dev/null +++ b/playwright-tests/pages/PaymentMethodPage.ts @@ -0,0 +1,82 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IPaymentDetails } from '@business-logic/types/CustomerDetails'; +import { PaymentType } from '@business-logic/types/Enums'; +import { PaymentPage } from './PaymentPage'; +import { AfterpayPage } from './AfterpayPage'; +import { PaypalPage } from './PaypalPage'; + +export class PaymentMethodPage extends BasePage { + readonly page: Page; + readonly payAtServiceButton: Locator; + readonly payNowButton: Locator; + readonly payInFourButton: Locator; + readonly paypalButton: Locator; + readonly paymentPage: PaymentPage; + readonly paypalPage: PaypalPage; + url = process.env['BASE_URL']! + '/?issPage=payment-method'; + + constructor(page: Page) { + super(page); + this.page = page; + this.payAtServiceButton = this.page.locator('[buttonlabel="Pay at my appointment"]'); //this.page.getByText('Pay at time of service'); + this.payNowButton = this.page.locator('[buttonlabel="Pay now"]'); + this.payInFourButton = this.page.locator('[buttonlabel="Pay in 4 installments"]'); + this.paypalButton = this.page.frameLocator('iframe[name="card-frame"]').locator('div[id="paypalParentDiv"]'); + this.paymentPage = new PaymentPage(page); + this.paypalPage = new PaypalPage(page); + } + + async executePayment(paymentDetails: IPaymentDetails) { + const browserContext = this.page.context(); + + switch (paymentDetails.paymentType) { + case PaymentType.Credit: + await this.selectCreditCard(); + await this.paymentPage.populateCreditCardDetails(paymentDetails); + break; + case PaymentType.Paypal: + await this.selectPaypal(); + await this.paypalPage.completePaypalPurchase(paymentDetails); + break; + case PaymentType.AfterPay: + await this.payInFourButton.click(); + await this.nextPage(); + + // Capture popup + const afterpayPopup = await browserContext.waitForEvent('page'); + const afterpayPage = new AfterpayPage(afterpayPopup); + + // Execute payment + await afterpayPage.executeAfterpayPayment(paymentDetails); + break; + + case PaymentType.PayAtService: + await this.selectPayAtService(); + break; + default: + console.error('PaymentMethodPage >> Logic for this payment method unimplemented'); + break; + } + } + + async selectPaypal() { + await this.payNowButton.click(); + await this.nextPage(); + await this.paypalButton.click(); + } + + async selectCreditCard() { + await this.payNowButton.click(); + await this.nextPage(); + } + + async selectPayAtService() { + await this.payAtServiceButton.click(); + } + + // async validateAmountDue(customer){ + // await this.amountDueDropDown.click(); + // await expect(this.deductibleAmountTextField).toContainText(`${customer.deductibleAmount}`); + // } +} \ No newline at end of file diff --git a/playwright-tests/pages/PaymentPage.ts b/playwright-tests/pages/PaymentPage.ts new file mode 100644 index 00000000..1c2334c9 --- /dev/null +++ b/playwright-tests/pages/PaymentPage.ts @@ -0,0 +1,45 @@ +import { type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IPaymentDetails } from '@business-logic/types/CustomerDetails'; + +export class PaymentPage extends BasePage { + readonly page: Page; + readonly cardNumberTextField: Locator; + readonly expirationMonthDropDown: Locator; + readonly expirationYearDropDown: Locator; + readonly cvvTextField: Locator; + readonly billingAddressTextField: Locator; + readonly cityTextField: Locator; + readonly stateDropDown: Locator; + readonly billingZipTextField: Locator; + readonly submitPaymentButton: Locator; + url = process.env['BASE_URL']! + '/?issPage=payment-page'; + + constructor(page: Page) { + super(page); + this.page = page; + this.cardNumberTextField = page.frameLocator('iframe[name="card-frame"]').getByLabel('Card number*'); + this.expirationMonthDropDown = page.frameLocator('iframe[name="card-frame"]').getByRole('combobox', { name: 'Expiration month' }); + this.expirationYearDropDown = page.frameLocator('iframe[name="card-frame"]').getByRole('combobox', { name: 'Expiration year' }); + this.cvvTextField = page.frameLocator('iframe[name="card-frame"]').getByRole('textbox', { name: 'CVV' }); + this.billingAddressTextField = page.frameLocator('iframe[name="card-frame"]').getByRole('textbox', { name: 'Billing address' }); + this.cityTextField = page.frameLocator('iframe[name="card-frame"]').getByRole('textbox', { name: 'City' }); + this.stateDropDown = page.frameLocator('iframe[name="card-frame"]').getByRole('combobox', { name: 'State' }); + this.billingZipTextField = page.frameLocator('iframe[name="card-frame"]').getByRole('textbox', { name: 'Billing ZIP code' });; + this.submitPaymentButton = page.frameLocator('iframe[name="card-frame"]').locator('#buttonContainer'); + // this.validateURL(this.url); + } + + async populateCreditCardDetails(paymentDetails: IPaymentDetails){ + await this.cardNumberTextField.fill(paymentDetails.cardNumber || ''); + await this.expirationMonthDropDown.selectOption(paymentDetails.expirationMonth!); + await this.expirationYearDropDown.selectOption(paymentDetails.expirationYear!); + await this.cvvTextField.fill(paymentDetails.cvv!); + await this.billingAddressTextField.fill(paymentDetails.billingAddress!.street); + await this.cityTextField.fill(paymentDetails.billingAddress!.city); + await this.stateDropDown.selectOption(paymentDetails.billingAddress!.state); + await this.billingZipTextField.fill(paymentDetails.billingAddress!.postalCode); + await this.submitPaymentButton.click(); + } + +} \ No newline at end of file diff --git a/playwright-tests/pages/PaypalPage.ts b/playwright-tests/pages/PaypalPage.ts new file mode 100644 index 00000000..862a97ed --- /dev/null +++ b/playwright-tests/pages/PaypalPage.ts @@ -0,0 +1,27 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IPaymentDetails } from '@business-logic/types/CustomerDetails'; + +export class PaypalPage extends BasePage { + readonly page: Page; + readonly loginWithPasswordButton: Locator; + readonly passwordTextBox: Locator; + readonly paypalLoginButton: Locator; + readonly completePurchaseButton: Locator; + + constructor(page: Page) { + super(page); + this.page = page; + this.loginWithPasswordButton = page.getByRole('link', { name: 'Log in with a password instead' }); + this.passwordTextBox = page.getByPlaceholder('Password'); + this.paypalLoginButton = page.getByRole('button', { name: 'Log In', exact: true }); + this.completePurchaseButton = page.getByTestId('submit-button-initial'); + } + + async completePaypalPurchase(paymentDetails: IPaymentDetails){ + await this.loginWithPasswordButton.click(); + await this.passwordTextBox.fill(paymentDetails.password!); + await this.paypalLoginButton.click(); + await this.completePurchaseButton.click(); + } +} diff --git a/playwright-tests/pages/PolicyHolderDetailsPage.ts b/playwright-tests/pages/PolicyHolderDetailsPage.ts new file mode 100644 index 00000000..f9f8d9a7 --- /dev/null +++ b/playwright-tests/pages/PolicyHolderDetailsPage.ts @@ -0,0 +1,20 @@ +import { type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { ICustomerDetails } from '@business-logic/types/CustomerDetails'; +import { AddressForm } from './forms/AddressForm'; + +export class PolicyHolderDetailsPage extends BasePage { + readonly page: Page; + readonly addressForm: AddressForm; + readonly url = process.env['BASE_URL']! + '/?issPage=policy-holder-details'; + + constructor(page: Page) { + super(page); + this.page = page; + this.addressForm = new AddressForm(page); + } + + async fillCustomerDetails(customerDetails: ICustomerDetails){ + await this.addressForm.populateAddress(customerDetails); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/PolicyVehiclesPage.ts b/playwright-tests/pages/PolicyVehiclesPage.ts new file mode 100644 index 00000000..0cd05b41 --- /dev/null +++ b/playwright-tests/pages/PolicyVehiclesPage.ts @@ -0,0 +1,27 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IVehicleDetails } from '@business-logic/types/CustomerDetails'; + +export class PolicyVehiclesPage extends BasePage { + readonly page: Page; + url = process.env['BASE_URL']! + '/?issPage=policy-vehicles'; + + constructor(page: Page) { + super(page); + this.page = page; + // this.validateURL(this.url); + } + + async validateVehicleIsOnPolicy(vehicleDetails: IVehicleDetails) { + const vehicleRegExp = new RegExp(`${vehicleDetails.year} .+ ${vehicleDetails.model}`, 'i'); + await expect.soft(this.page.getByRole('radio', {name: vehicleRegExp})).toBeAttached(); + } + + async selectVehicle(vehicleDetails: IVehicleDetails){ + const vehicleRegExp = new RegExp(`${vehicleDetails.year} .+ ${vehicleDetails.model}`, 'i'); + await this.page.locator('label').filter({hasText: vehicleRegExp}).locator('div').click(); + } + async selectVehicleNotListed(){ + await this.page.getByText('Vehicle not listed').click(); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/ProviderPreferencePage.ts b/playwright-tests/pages/ProviderPreferencePage.ts new file mode 100644 index 00000000..4bcfef51 --- /dev/null +++ b/playwright-tests/pages/ProviderPreferencePage.ts @@ -0,0 +1,50 @@ +import test, { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; + +export class ProviderPreferencePage extends BasePage { + readonly page: Page; + readonly scheduleWithSafelite: Locator; + readonly scheduleWithOther: Locator; + readonly acknowledgeAdasButton: Locator; + readonly gotItButton: Locator; + readonly acknowledgeCheckbox: Locator; + readonly stateLawModalHeading: Locator; + url = process.env['BASE_URL']! + '/?issPage=provider-preference'; + + constructor(page: Page) { + super(page); + this.page = page; + // this.scheduleWithSafelite = this.page.locator('div').filter({ hasText: /Schedule online with |Safelite AutoGlass/ }).first(); + this.scheduleWithSafelite = this.page.getByText(/Schedule online with|Safelite AutoGlass/).first(); + this.scheduleWithOther = this.page.getByText(/Find another shop|Choose my own shop/).first(); + this.acknowledgeAdasButton = this.page.getByLabel('I acknowledge that my vehicle'); + this.gotItButton = this.page.getByRole('button', { name: 'Got it' }); + this.acknowledgeCheckbox = this.page.locator('#tpaAcknowledgement'); + this.stateLawModalHeading = this.page.getByRole('heading').filter({ hasText: /.* State Law/}); + } + + async selectProvider(isSafelite = true) { + if (isSafelite) { + await this.scheduleWithSafelite.click(); + await this.nextPage(); + } + else { + await this.scheduleWithOther.waitFor({ state: 'visible' }); + await this.scheduleWithOther.click(); + await this.continueButton.click(); + //if(await this.acknowledgeAdasButton.isEnabled({timeout: 2500})){ + // await this.acknowledgeAdasButton.click(); + // await this.gotItButton.click(); + // await this.page.waitForTimeout(1000); + //} + } + } + async acknowledgeRecalNotificaiton() { + await this.acknowledgeAdasButton.click(); + await this.gotItButton.click(); + } + + async validateStateLawModalIsVisible() { + await expect.soft(this.stateLawModalHeading).toBeVisible(); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/SchedulePage.ts b/playwright-tests/pages/SchedulePage.ts new file mode 100644 index 00000000..4003bcb9 --- /dev/null +++ b/playwright-tests/pages/SchedulePage.ts @@ -0,0 +1,58 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IAppointmentDetails } from '@business-logic/types/CustomerDetails'; +import { formatDate, formatTime } from '@impl/utils/DateUtils'; +import { ServiceLocation } from '@business-logic/types/Enums'; + +export class SchedulePage extends BasePage { + readonly page: Page; + url = process.env['BASE_URL']! + '/?issPage=schedule-page'; + readonly firstAvailableDate: Locator; + readonly firstAvailableTime: Locator; + readonly modalContinueButton: Locator; + readonly dropOffButton: Locator; + readonly dateText: Locator; + readonly viewMoreDatesLink: Locator; + + constructor(page: Page) { + super(page); + this.page = page; + this.firstAvailableDate = this.page.locator('.selectable-day').locator('nth=0'); + this.firstAvailableTime = this.page.locator('label').filter({ hasText: /AM|PM/ }).locator('div').locator('nth=0'); + this.modalContinueButton = this.page.locator('#modalbtn'); + this.dropOffButton = this.page.getByText('Drop off your vehicle', { exact: true }); + this.dateText = this.page.locator('[class="modal-header mb-2 mt-2"]'); + this.viewMoreDatesLink = this.page.getByText(/View more dates/).first(); + } + + async scheduleAppointment(appointmentDetails: IAppointmentDetails) { + const formattedDate = formatDate(appointmentDetails.appointmentDate!); + const formattedTime = formatTime(appointmentDetails.appointmentDate!); + const dateInput = this.page.locator(`div[id="${formattedDate}"]`); + const timeButton = this.page.locator(`div[aria-label="${formattedTime}"]`); + if (await dateInput.isVisible()) { + await dateInput.click(); + } else { + await this.viewMoreDatesLink.click(); + await dateInput.click(); + } + await timeButton.click(); + await this.modalContinueButton.click(); + } + + async scheduleFirstAppointment(serviceLocation: ServiceLocation) { + 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(); + } + + serviceLocation === ServiceLocation.DropOff ? await this.dropOffButton.click() : await this.firstAvailableTime.click(); + await this.modalContinueButton.click(); + return (`${await this.dateText.allInnerTexts()}`); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/ServiceLocationPage.ts b/playwright-tests/pages/ServiceLocationPage.ts new file mode 100644 index 00000000..0d777587 --- /dev/null +++ b/playwright-tests/pages/ServiceLocationPage.ts @@ -0,0 +1,135 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IAppointmentDetails } from '@business-logic/types/CustomerDetails'; +import { ServiceLocation } from '@business-logic/types/Enums'; +import { AddressForm } from './forms/AddressForm'; +import { faker } from '@faker-js/faker'; + +export class ServiceLocationPage extends BasePage { + readonly page: Page; + + readonly addressForm: AddressForm; + + // Initial selection + readonly inShopButton: Locator; + readonly mobileButton: Locator; + readonly dropOffButton: Locator; + readonly RecalWarningMessage1: Locator; + readonly RecalWarningMessage2: Locator; + readonly militaryWarningMessage: Locator; + + // For in-shop and drop off + readonly selectAShopOptions: Locator; + readonly firstAppointmentButton: Locator; + readonly changeZipButton: Locator; + readonly updateZipTextBox: Locator; + readonly saveZipButton: Locator; + + // For mobile + readonly enterServiceAddressButton: Locator; + readonly serviceAddressTextBox: Locator; + readonly aptNumberTextBox: Locator; + readonly cityTextBox: Locator; + readonly stateDropDown: Locator; + readonly zipCodeTextBox: Locator; + readonly vehicleProtectedYesButton: Locator; + readonly vehicleProtectedNoButton: Locator; + readonly saveAddressButton: Locator; + + url = process.env['BASE_URL']! + '/?issPage=service-location'; + + constructor(page: Page) { + super(page); + this.page = page; + + this.addressForm = new AddressForm(page); + + // Initial selection + this.inShopButton = this.page.locator('[buttonlabel="In-shop"]'); + this.mobileButton = this.page.locator('[buttonlabel="Mobile"]') + this.dropOffButton = this.page.locator('[buttonlabel="Drop-off"]'); + this.RecalWarningMessage1 = this.page.getByText(/We're not able to provide mobile service/); + this.RecalWarningMessage2 = this.page.getByText(/advanced safety system recalibration needs to be done in our shop./); + this.militaryWarningMessage = this.page.locator('[class*="widget-name-AlertMilitaryBaseZipWidget"]'); + + // For in-shop and drop off + this.selectAShopOptions = this.page.locator('[class="shop-question"]'); + this.firstAppointmentButton = this.page.locator('div').filter({ hasText: /Appts/}).first(); + this.changeZipButton = this.page.locator('[id="serviceZipLinkPromptId"]'); + this.updateZipTextBox = this.page.getByRole('textbox', { name: 'Update your service ZIP code'}); + this.saveZipButton = this.page.getByRole('button', { name: 'Save ZIP code' }); + + + // For mobile + this.enterServiceAddressButton = this.page.getByRole('link', { name: 'Enter your service address' }); + this.serviceAddressTextBox = this.page.getByRole('textbox', { name: 'Street Address' }); + this.aptNumberTextBox = this.page.getByRole('textbox', { name: 'Apt. number'}); + this.cityTextBox = this.page.getByRole('textbox', { name: 'City' }); + this.stateDropDown = this.page.getByRole('combobox', { name: 'State' }); + this.zipCodeTextBox = this.page.getByRole('textbox', { name: 'Zip code' }); + 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: 'Save Address' }); + } + + async selectLocation(appointmentDetails: IAppointmentDetails){ + if (appointmentDetails.alternateServiceZip) { + await this.changeZipButton.click(); + await this.fillAndValidate(this.updateZipTextBox, appointmentDetails.alternateServiceZip); + await this.saveZipButton.click(); + } + + switch(appointmentDetails.serviceLocation) { + case ServiceLocation.Mobile: + await this.scheduleMobile(appointmentDetails); + break; + case ServiceLocation.InShop: + await this.scheduleInShop(appointmentDetails); + break; + case ServiceLocation.DropOff: + await this.scheduleDropOff(appointmentDetails); + break; + } + } + + + async scheduleInShop(appointmentDetails?: IAppointmentDetails) { + await this.inShopButton.click(); + if (appointmentDetails && appointmentDetails.shopAddress) { + await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).check(); + } else { + await this.firstAppointmentButton.click(); + } + } + + async scheduleMobile(appointmentDetails: IAppointmentDetails){ + if (appointmentDetails.serviceAddress) { + await this.mobileButton.click(); + await this.enterServiceAddressButton.click(); + await this.addressForm.populateAddress({ address: appointmentDetails.serviceAddress! }); + if (faker.datatype.boolean()) { + await this.vehicleProtectedYesButton.check(); + } else { + await this.vehicleProtectedNoButton.check(); + } + await this.saveAddressButton.click(); + } else { + console.error('ServiceLocationPage >> Please supply an address') + } + } + + async scheduleDropOff(appointmentDetails?: IAppointmentDetails){ + await this.dropOffButton.click(); + if (appointmentDetails && appointmentDetails.shopAddress) { + await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).check(); + } else { + await this.firstAppointmentButton.click(); + } + } + + async validateRecalWarning(){ + await expect(this.RecalWarningMessage1).toBeVisible(); + await expect(this.RecalWarningMessage2).toBeVisible(); + + } +} \ No newline at end of file diff --git a/playwright-tests/pages/ServicePackagesPage.ts b/playwright-tests/pages/ServicePackagesPage.ts new file mode 100644 index 00000000..e2b8ba67 --- /dev/null +++ b/playwright-tests/pages/ServicePackagesPage.ts @@ -0,0 +1,26 @@ +import { type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { ServicePackage } from '@business-logic/types/Enums'; + +export class ServicePackagesPage extends BasePage { + readonly page: Page; + readonly standardPackageButton: Locator; + readonly premiumPackageButton: Locator; + readonly glassOnlyButton: Locator; + url = process.env['BASE_URL']! + '/?issPage=service-packages'; + + constructor(page: Page) { + super(page); + this.page = page; + this.standardPackageButton = this.page.locator('li').filter({ hasText: 'Standard' }); + this.premiumPackageButton = this.page.locator('li').filter({ hasText: 'Premium' }); + this.glassOnlyButton = this.page.locator('li').filter({ hasText: 'Glass service' }); + // this.validateURL(this.url); + } + + async selectServicePackage(servicePackage: ServicePackage){ + await this.page.getByText(servicePackage).click(); + return (`${await this.page.locator('li').filter({ hasText: 'Premium' }).locator('[class="pricing-info"]').allInnerTexts()}`); + //return (`${await this.page.getByText(servicePackage).locator('[class="pricing-info"]').allInnerTexts()}`); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/TpaConfirmationPage.ts b/playwright-tests/pages/TpaConfirmationPage.ts new file mode 100644 index 00000000..2dbb8b19 --- /dev/null +++ b/playwright-tests/pages/TpaConfirmationPage.ts @@ -0,0 +1,19 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; + +export class TpaConfirmationPage extends BasePage { + readonly page: Page; + readonly successMessage: Locator; + readonly url = process.env['BASE_URL']! + '/?issPage=tpa-confirmation'; + + constructor(page: Page) { + super(page); + this.page = page; + this.successMessage = page.getByText('Success'); + } + + async validateSuccessMessage() { + await expect(this.successMessage).toBeVisible(); + } + +} \ No newline at end of file diff --git a/playwright-tests/pages/TpaSearchPage.ts b/playwright-tests/pages/TpaSearchPage.ts new file mode 100644 index 00000000..67704c4c --- /dev/null +++ b/playwright-tests/pages/TpaSearchPage.ts @@ -0,0 +1,24 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; + +export class TpaSearchPage extends BasePage { + readonly page: Page; + readonly firstLocationButton: Locator; + readonly doNotSeeMyShopButton: Locator; + url = process.env['BASE_URL']! + '/?issPage=tpa-search'; + + constructor(page: Page) { + super(page); + this.page = page; + this.firstLocationButton = page.locator("fieldset[aria-labelledby='chooseShop']").first(); + this.doNotSeeMyShopButton = page.getByRole('link', { name: 'I don\'t see my shop' }); + } + + async selectDoNotSeeMyShop() { + await this.doNotSeeMyShopButton.click() + } + + async selectFirstLocation() { + await this.firstLocationButton.click(); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/TpaSubmitPage.ts b/playwright-tests/pages/TpaSubmitPage.ts new file mode 100644 index 00000000..bece6703 --- /dev/null +++ b/playwright-tests/pages/TpaSubmitPage.ts @@ -0,0 +1,18 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; + +export class TpaSubmitPage extends BasePage { + readonly page: Page; + readonly deductible: Locator; + url = process.env['BASE_URL']! + '/?issPage=tpa-submit'; + + constructor(page: Page) { + super(page); + this.page = page; + this.deductible = page.getByText('Deductible $'); + } + + async validateDeductible(expDeductible) { + await expect(this.deductible).toContainText(expDeductible); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/VehicleDamagePage.ts b/playwright-tests/pages/VehicleDamagePage.ts new file mode 100644 index 00000000..58ec7515 --- /dev/null +++ b/playwright-tests/pages/VehicleDamagePage.ts @@ -0,0 +1,150 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { SideDoorDamage, VehicleDamage, WindshieldDamage } from '@business-logic/types/Enums'; + +export class VehicleDamagePage extends BasePage { + readonly page: Page; + readonly windshieldChkBox: Locator; + readonly crackButton: Locator; + readonly chipButton: Locator; + readonly sideDoorButton: Locator; + readonly driverSideButton: Locator; + readonly passengerSideButton: Locator; + readonly driverQuarterPanelChkBox: Locator; + readonly driverFrontDoorChkBox: Locator; + readonly driverBackDoorChkBox: Locator; + readonly driverSlidingDoorChkBox: Locator; + readonly driverVentGlassChkBox: Locator; + readonly passengerQuarterPanelChkBox: Locator; + readonly passengerFrontDoorChkBox: Locator; + readonly passengerBackDoorChkBox: Locator; + readonly passengerVentGlassChkBox: Locator; + readonly rearWindowChkBox: Locator; + readonly separateApptsWarning: Locator; + readonly editVehicleButton: Locator; + url = process.env['BASE_URL']! + '/?issPage=vehicle-damage'; + + constructor(page: Page) { + super(page); + this.page = page; + this.windshieldChkBox = this.page.locator('[buttonlabel="Windshield"]'); + this.crackButton = this.page.locator('[buttonlabel="Crack"]'); + this.chipButton = this.page.locator('[buttonlabel="Chip(s)"]'); + this.sideDoorButton = this.page.locator('[buttonlabel="Side door"]'); + this.driverSideButton = this.page.locator('[buttonlabel="Driver side"]'); + this.passengerSideButton = this.page.locator('[buttonlabel="Passenger side"]'); + this.driverQuarterPanelChkBox = this.page.locator('[aria-labelledby="driverSideOptions"]').locator('[buttonlabel="Quarter panel"]'); + this.driverFrontDoorChkBox = this.page.locator('[aria-labelledby="driverSideOptions"]').locator('[buttonlabel="Front door"]'); + this.driverBackDoorChkBox = this.page.locator('[aria-labelledby="driverSideOptions"]').locator('[buttonlabel="Back door"]'); + this.driverVentGlassChkBox = this.page.locator('[aria-labelledby="driverSideOptions"]').locator('[buttonlabel="Vent glass"]'); + this.driverSlidingDoorChkBox = this.page.locator('[aria-labelledby="driverSideOptions"]').locator('[buttonlabel="Sliding door"]'); + this.passengerQuarterPanelChkBox = this.page.locator('[aria-labelledby="passengerSideOptions"]').locator('[buttonlabel="Quarter panel"]'); + this.passengerFrontDoorChkBox = this.page.locator('[aria-labelledby="passengerSideOptions"]').locator('[buttonlabel="Front door"]'); + this.passengerVentGlassChkBox = this.page.locator('[aria-labelledby="passengerSideOptions"]').locator('[buttonlabel="Vent glass"]'); + this.passengerBackDoorChkBox = this.page.locator('[aria-labelledby="passengerSideOptions"]').locator('[buttonlabel="Back door"]'); + this.rearWindowChkBox = this.page.locator('[buttonlabel="Rear window"]'); + this.separateApptsWarning = this.page.locator('[class*="widget-name-HasReplacementConflict"]'); + this.editVehicleButton = this.page.getByRole('link', { name: 'Edit vehicle' }); + } + + async checkSeparateApptsWarning() { + // Select conflict + await this.windshieldChkBox.check(); + await this.chipButton.check(); + await this.rearWindowChkBox.check(); + + // Expect warning + await expect.soft(this.separateApptsWarning).toBeAttached(); + + // Undo changes + await this.crackButton.check(); + await this.windshieldChkBox.uncheck(); + await expect.soft(this.crackButton).not.toBeVisible(); + await this.rearWindowChkBox.uncheck(); + } + + async selectDamage(vehicleDamage: VehicleDamage[]) { + for (const damage of vehicleDamage) { + switch(damage) { + case VehicleDamage.WindshieldOneChip: + await this.windshieldChkBox.check(); + await this.selectChips('1'); + break; + case VehicleDamage.WindshieldTwoChips: + await this.windshieldChkBox.check(); + await this.selectChips('2'); + break; + case VehicleDamage.WindshieldThreeChips: + await this.windshieldChkBox.check(); + await this.selectChips('3'); + break; + case VehicleDamage.WindshieldCrack: + await this.windshieldChkBox.check(); + await this.selectCrack(); + break; + case VehicleDamage.DriverFrontDoor: + await this.sideDoorButton.check(); + await this.driverSideButton.check(); + await this.driverFrontDoorChkBox.check(); + break; + case VehicleDamage.DriverRearDoor: + await this.sideDoorButton.check(); + await this.driverSideButton.check(); + await this.driverBackDoorChkBox.check(); + break; + case VehicleDamage.DriverQuarterPanel: + await this.sideDoorButton.check(); + await this.driverSideButton.check(); + await this.driverQuarterPanelChkBox.check(); + break; + case VehicleDamage.DriverVentGlass: + await this.sideDoorButton.check(); + await this.driverSideButton.check(); + await this.driverVentGlassChkBox.check(); + break; + case VehicleDamage.DriverSlidingDoor: + await this.sideDoorButton.check(); + await this.driverSideButton.check(); + await this.driverSlidingDoorChkBox.check(); + break; + case VehicleDamage.PassengerFrontDoor: + await this.sideDoorButton.check(); + await this.passengerSideButton.check(); + await this.passengerFrontDoorChkBox.check(); + break; + case VehicleDamage.PassengerRearDoor: + await this.sideDoorButton.check(); + await this.passengerSideButton.check(); + await this.passengerBackDoorChkBox.check(); + break; + case VehicleDamage.PassengerQuarterPanel: + await this.sideDoorButton.check(); + await this.passengerSideButton.check(); + await this.passengerQuarterPanelChkBox.click(); + break; + case VehicleDamage.PassengerVentGlass: + await this.sideDoorButton.check(); + await this.passengerSideButton.check(); + await this.passengerVentGlassChkBox.check(); + break; + case VehicleDamage.RearWindow: + await this.selectRearWindowDamage(); + break; + } + } + } + + async selectCrack(){ + await this.crackButton.check(); + } + + async selectChips(numChips: string){ + const numChipsButton = this.page.getByText(numChips, {exact: true}); + await this.chipButton.check(); + await numChipsButton.check(); + } + + async selectRearWindowDamage(){ + await this.rearWindowChkBox.check(); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/VehicleLookupAddressPage.ts b/playwright-tests/pages/VehicleLookupAddressPage.ts new file mode 100644 index 00000000..ca2cd6f0 --- /dev/null +++ b/playwright-tests/pages/VehicleLookupAddressPage.ts @@ -0,0 +1,25 @@ +import { type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { AddressForm } from './forms/AddressForm'; +import { VehicleSelectionForm } from './forms/VehicleSelectionForm'; +import { ICustomerDetails, IVehicleDetails } from '@business-logic/types/CustomerDetails'; + +export class VehicleLookupAddressPage extends BasePage { + readonly page: Page; + readonly addressForm: AddressForm; + readonly vehicleSelectionForm: VehicleSelectionForm; + url = process.env['BASE_URL']! + '/?issPage=address-vehicles'; + + constructor(page: Page) { + super(page); + this.page = page; + this.addressForm = new AddressForm(page); + this.vehicleSelectionForm = new VehicleSelectionForm(page); + } + + async lookupVehicleByAddress(customerDetails: ICustomerDetails, vehicleDetails: IVehicleDetails) { + await this.addressForm.populateAddress(customerDetails); + //await this.nextPage(); + //await this.vehicleSelectionForm.selectVehicle(vehicleDetails); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/VehicleLookupLicensePage.ts b/playwright-tests/pages/VehicleLookupLicensePage.ts new file mode 100644 index 00000000..1b2b3140 --- /dev/null +++ b/playwright-tests/pages/VehicleLookupLicensePage.ts @@ -0,0 +1,22 @@ +import { type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IVehicleDetails } from '@business-logic/types/CustomerDetails'; + +export class VehicleLookupLicensePage extends BasePage { + readonly page: Page; + readonly licensePlateNumTextBox: Locator; + readonly licensePlateStateDrpDwn: Locator; + url = process.env['BASE_URL']! + '/?issPage='; // TODO: Input correct URL + + constructor(page: Page) { + super(page); + this.page = page; + this.licensePlateNumTextBox = page.getByRole('textbox', { name: 'License plate number'}); + this.licensePlateStateDrpDwn = page.getByRole('combobox', { name: 'License plate state'}); + } + + async enterPlateDetails(vehicleDetails: IVehicleDetails){ + await this.licensePlateNumTextBox.fill(vehicleDetails.licensePlateNumber || ''); + await this.licensePlateStateDrpDwn.selectOption(vehicleDetails.licensePlateState!); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/VehicleLookupPage.ts b/playwright-tests/pages/VehicleLookupPage.ts new file mode 100644 index 00000000..c2bd2046 --- /dev/null +++ b/playwright-tests/pages/VehicleLookupPage.ts @@ -0,0 +1,61 @@ +import { type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { VehicleLookupType } from '@business-logic/types/Enums'; +import { IVehicleDetails } from '@business-logic/types/CustomerDetails'; +import { VinLookupPage } from './VinLookupPage'; +import { VehicleLookupAddressPage } from './VehicleLookupAddressPage'; +import { VehicleLookupLicensePage } from './VehicleLookupLicensePage'; + +export class VehicleLookupPage extends BasePage { + readonly page: Page; + readonly vinLookupButton: Locator; + readonly addressLookupButton: Locator; + readonly licenseLookupButton: Locator; + readonly vinLookupPage: VinLookupPage; + readonly vehicleLookupAddressPage: VehicleLookupAddressPage; + readonly vehicleLookupLicensePage: VehicleLookupLicensePage; + url = process.env['BASE_URL']! + '/?issPage=vehicle-lookup'; + + constructor(page: Page) { + super(page); + this.page = page; + this.vinLookupButton = page.getByLabel('Provide my VIN manually', { exact: true }); + this.addressLookupButton = page.getByLabel('Provide my home address', { exact: true }); + this.licenseLookupButton = page.getByLabel('Provide my license plate #', { exact: true }); + this.vinLookupPage = new VinLookupPage(page); + + // this.validateURL(this.url); + } + + async vehicleLookup(vehicleDetails: IVehicleDetails) { + switch (vehicleDetails.vehicleLookupType) { + case VehicleLookupType.Address: + await this.selectAddressLookup(); + await this.nextPage(); + break; + case VehicleLookupType.LicensePlateNumber: + await this.selectLicenseLookup(); + await this.nextPage(); + break; + case VehicleLookupType.Vin: + await this.selectVinLookup(); + await this.nextPage(); + break; + default: + console.error('VehicleLookupPage >> DATA ISSUE: VehicleLookupType not provided'); + break; + } + } + + async selectVinLookup(){ + await this.vinLookupButton.click(); + } + + async selectAddressLookup(){ + await this.addressLookupButton.click(); + } + + async selectLicenseLookup(){ + await this.licenseLookupButton.click(); + } +} diff --git a/playwright-tests/pages/VehiclePartsPage.ts b/playwright-tests/pages/VehiclePartsPage.ts new file mode 100644 index 00000000..908a2542 --- /dev/null +++ b/playwright-tests/pages/VehiclePartsPage.ts @@ -0,0 +1,10 @@ +import { Page } from "@playwright/test"; +import { PartQuestionsPage } from "./PartQuestionsPage"; + +export default class VehiclePartQuestionsPage extends PartQuestionsPage{ + url = process.env['BASE_URL']! + '/?issPage=vehicle-parts'; + + constructor(page: Page) { + super(page); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/VehicleSelectionPage.ts b/playwright-tests/pages/VehicleSelectionPage.ts new file mode 100644 index 00000000..abb08fa3 --- /dev/null +++ b/playwright-tests/pages/VehicleSelectionPage.ts @@ -0,0 +1,35 @@ +import { type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IVehicleDetails } from '@business-logic/types/CustomerDetails'; + +export class VehicleSelectionPage extends BasePage { + readonly page: Page; + readonly yearDropdown: Locator; + readonly makeDropdown: Locator; + readonly modelDropdown: Locator; + readonly styleDropdown: Locator; + + url = process.env['BASE_URL']! + '/?issPage=vehicle-selection'; + + constructor(page: Page) { + super(page); + this.page = page; + this.yearDropdown = this.page.locator('#yearQuestionField'); + this.makeDropdown = this.page.locator('#makeQuestionField'); + this.modelDropdown = this.page.locator('#modelQuestionField'); + this.styleDropdown = this.page.locator('#styleQuestionField'); + // this.validateURL(this.url); + } + + async selectVehicle(vehicleDetails: IVehicleDetails) { + await this.yearDropdown.selectOption(vehicleDetails.year); + await this.yearDropdown.press('Tab'); + await this.makeDropdown.selectOption(vehicleDetails.make); + await this.yearDropdown.press('Tab'); + await this.modelDropdown.selectOption(vehicleDetails.model); + await this.yearDropdown.press('Tab'); + if (vehicleDetails.style != undefined) { + await this.styleDropdown.selectOption(vehicleDetails.style); + } + } +} \ No newline at end of file diff --git a/playwright-tests/pages/VinLookupPage.ts b/playwright-tests/pages/VinLookupPage.ts new file mode 100644 index 00000000..864ecb15 --- /dev/null +++ b/playwright-tests/pages/VinLookupPage.ts @@ -0,0 +1,26 @@ +import { type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; + +export class VinLookupPage extends BasePage { + readonly page: Page; + readonly vinLookupTextBox: Locator; + readonly lookupVinForMe: Locator; + url = process.env['BASE_URL']! + '/?issPage=vin-lookup'; // TODO: Input correct URL + + constructor(page: Page) { + super(page); + this.page = page; + this.vinLookupTextBox = page.getByRole('textbox', { name: 'Enter your VIN' }); + this.lookupVinForMe = page.getByRole('link', { name: 'look up your VIN' }); + // this.validateURL(this.url); + } + + async enterVin(vin: string) { + await this.vinLookupTextBox.fill(vin); + } + + async triggerBailout() { + await this.continueButton.click(); + await this.lookupVinForMe.click(); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/WelcomePage.ts b/playwright-tests/pages/WelcomePage.ts new file mode 100644 index 00000000..80dcbadb --- /dev/null +++ b/playwright-tests/pages/WelcomePage.ts @@ -0,0 +1,105 @@ +import test, { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from './BasePage'; +import { IClaimDetails, ICustomerDetails } from '@business-logic/types/CustomerDetails'; +import { getClientAuthByClientTag, getClientSignature } from '@impl/api/AdminServiceApiUtil'; +import { ICertificateInfo, IClientSignatureRequest } from '@business-logic/types/Authentication'; +import { buildToken, getTimestamp } from '@impl/utils/TokenUtils'; + +export class WelcomePage extends BasePage { + readonly page: Page; + readonly policyNumber: Locator; + readonly policyZip: Locator; + readonly damageDate: Locator; + readonly damageCause: Locator; + readonly phoneNumber: Locator; + readonly emailAddress: Locator; + readonly city: Locator; + readonly state: Locator; + readonly cookieCloseButton: Locator; + url = process.env['BASE_URL']! + '/?issPage=welcome-page'; + + constructor(page: Page) { + super(page); + this.page = page; + this.policyNumber = page.getByRole('textbox', { name: 'Policy number' }); + this.policyZip = page.getByRole('textbox', { name: 'Policy ZIP' }); + this.damageDate = page.getByRole('textbox', { name: 'When did the damage occur?<' }); + this.damageCause = page.locator('#damageCauseQuestionField'); + this.phoneNumber = page.getByRole('textbox', { name: 'Best number to reach you' }); + this.emailAddress = page.getByRole('textbox', { name: 'Email address' }); + this.city = page.getByRole('textbox', { name: 'In which city did the damage' }); + this.state = page.locator('select[name="\\38 fdf9dc2e13e430eb57529499dceb3eb"]'); + this.cookieCloseButton = page.getByRole('button', { name: 'Close' }); + + } + + async goto(clientTag: string) { + await this.page.goto(process.env['BASE_URL']! + `/?issPage=entry-page&ClientTag=${clientTag}`); + await this.validateURL(this.url); + await this.cookieCloseButton.click(); + } + + async gotoWithAuthentication(clientTag: string) { + const clientAuth = await getClientAuthByClientTag(clientTag); + const certificate = clientAuth.certificateInfo[0] as ICertificateInfo; + let formData: Map = new Map(); + formData.set("Timestamp", getTimestamp()) + const request: IClientSignatureRequest = { + clientTag: clientTag, + token: buildToken(clientAuth, formData), + certificateFileName: certificate.name, + certificateKey: certificate.key, + certificateAlgorithm: certificate.algorithm, + certificateType: certificate.type + } + const result = await getClientSignature(request); + await this.page.goto(process.env['BASE_URL']! + `/?issPage=entry-page&ClientTag=${clientTag}&token=${request.token}&signature=${result.signature}`); + await this.validateURL(this.url); + await this.logReferralNumber(); + await this.cookieCloseButton.click(); + } + + async hasCityInfo() { + await expect.soft(this.damageCause).toBeVisible(); + return this.city.isVisible(); + } + + async populatePage(customerDetails: ICustomerDetails, claimDetails: IClaimDetails, isFillCityInfo: boolean) { + await this.policyNumber.fill(claimDetails.policyNumber); + await this.policyZip.fill(customerDetails.address.postalCode); + await this.damageDate.click(); + await this.damageDate.fill(claimDetails.damageDate); + await this.damageCause.selectOption(claimDetails.damageCause); + await this.damageCause.press('Tab'); + await this.phoneNumber.fill(customerDetails.phoneNumber); + // Phone number fixed 12/5. Can't start with 1 or 0 + await this.emailAddress.fill(customerDetails.email); + + if (isFillCityInfo) { + await this.city.fill(customerDetails.address.city); + await this.state.selectOption(customerDetails.address.state); + } + await this.logReferralNumber(); + } + + async logReferralNumber() { + let mainLocalStorage = JSON.parse(await this.page.evaluate('localStorage.getItem(\'main\')')); + let referralNumber = mainLocalStorage.order.referralNumber as number; + let referralSequenceNumber = mainLocalStorage.order.referralSequenceNumber as number; + if (referralNumber == null) { + for (let i = 1; i <= 20; i++) { + if (!referralNumber == null) break; + await this.page.waitForTimeout(500); + mainLocalStorage = JSON.parse(await this.page.evaluate('localStorage.getItem(\'main\')')); + referralNumber = mainLocalStorage.order.referralNumber as number; + referralSequenceNumber = mainLocalStorage.order.referralSequenceNumber as number; + } + } + + await test.step(`Referral Number:${referralNumber} Referral Sequence Number:${referralSequenceNumber}`, async () => { + console.log(`Referral Number:${referralNumber}`); + console.log(`Referral Sequence Number:${referralSequenceNumber}`); + }); + + } +} \ No newline at end of file diff --git a/playwright-tests/pages/forms/AddressForm.ts b/playwright-tests/pages/forms/AddressForm.ts new file mode 100644 index 00000000..acbdadec --- /dev/null +++ b/playwright-tests/pages/forms/AddressForm.ts @@ -0,0 +1,58 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { BasePage } from '../BasePage'; +import { ICustomerDetails } from '@business-logic/types/CustomerDetails'; +import { faker } from '@faker-js/faker/locale/en'; + +export class AddressForm extends BasePage { + readonly page: Page; + readonly streetAddressTextBox: Locator; + readonly cityTextBox: Locator; + readonly stateDrpDwn: Locator; + readonly zipCodeTextBox: Locator; + readonly firstNameTextBox: Locator; + readonly lastNameTextBox: Locator; + readonly addressNotFoundMsg: Locator; + + constructor(page: Page) { + super(page); + this.page = page; + this.streetAddressTextBox = page.getByRole('textbox', { name: /Street (a|A)ddress$/ }); + this.cityTextBox = page.getByRole('textbox', { name: 'City' }); + this.stateDrpDwn = page.getByRole('combobox', { name: 'State' }); + this.zipCodeTextBox = page.getByRole('textbox', { name: 'ZIP code' }); + this.firstNameTextBox = page.getByRole('textbox', { name: 'First name' }); + this.lastNameTextBox = page.getByRole('textbox', { name: 'Last name' }); + this.addressNotFoundMsg = page.getByText('Address not found.'); + } + + async forceAddressFormToAppear() { + await expect(async () => { + await this.streetAddressTextBox.click(); + await this.streetAddressTextBox.pressSequentially('7400 Safelite Way'); + await this.streetAddressTextBox.press('Tab'); + await expect(this.zipCodeTextBox).toBeVisible({ timeout: 100 }); + }).toPass(); + } + + async populateAddress(customerDetails: Partial) { + + if (customerDetails.address) { + // Force address form to appear + await this.forceAddressFormToAppear(); + + // Fill address + await this.fillAndValidate(this.streetAddressTextBox, customerDetails.address.street); + await this.fillAndValidate(this.zipCodeTextBox, customerDetails.address.postalCode) + await this.fillAndValidate(this.cityTextBox, customerDetails.address.city); + await this.stateDrpDwn.selectOption(customerDetails.address.state); + } + + if (customerDetails.firstName) { + await this.fillAndValidate(this.firstNameTextBox, customerDetails.firstName); + } + + if (customerDetails.lastName) { + await this.fillAndValidate(this.lastNameTextBox, customerDetails.lastName); + } + } +} \ No newline at end of file diff --git a/playwright-tests/pages/forms/VehicleSelectionForm.ts b/playwright-tests/pages/forms/VehicleSelectionForm.ts new file mode 100644 index 00000000..e9125cfd --- /dev/null +++ b/playwright-tests/pages/forms/VehicleSelectionForm.ts @@ -0,0 +1,16 @@ +import { type Page } from '@playwright/test'; +import { BasePage } from '../BasePage'; +import { IVehicleDetails } from '@business-logic/types/CustomerDetails'; + +export class VehicleSelectionForm extends BasePage { + readonly page: Page; + + constructor(page: Page) { + super(page); + this.page = page; + } + + async selectVehicle(vehicleDetails: IVehicleDetails){ + await this.page.locator('label').filter({hasText: vehicleDetails.model}).locator('div').first().click(); + } +} \ No newline at end of file diff --git a/playwright-tests/playwright.config.ts b/playwright-tests/playwright.config.ts new file mode 100644 index 00000000..bc960078 --- /dev/null +++ b/playwright-tests/playwright.config.ts @@ -0,0 +1,112 @@ +import { defineConfig, devices } from '@playwright/test'; +import dotenv from 'dotenv-safe'; +import { OrtoniReportConfig } from "ortoni-report"; + +if (!process.env.CI) { + // Environment variables are present in CI environment, no need to read from file + if (process.env.NODE_ENV == 'undefined' || process.env.NODE_ENV == null) { + dotenv.config({ path: `playwright-tests/.env.dev`, example: 'playwright-tests/.env.example' }); + } + else { + dotenv.config({ path: `playwright-tests/.env.${process.env.NODE_ENV}`, example: 'playwright-tests/.env.example' }); + } +} + + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const reportConfig: OrtoniReportConfig = { + port: 1994, + open: "never", + folderPath: "test-results", + filename: "index.html", + logo: "../data/logo.png", + title: "Test Report", + showProject: false, + projectName: "ISS-Nextgen-Playwright-Report", + testType: `E2E- Environment: ${process.env.NODE_ENV} `, + preferredTheme: "light", + base64Image: true, +}; + +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 1 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 2 : 5, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: [ + ['ortoni-report', reportConfig], + ['list'] + ], + timeout: 120_000, + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + headless: process.env.CI ? true : false, + screenshot: "only-on-failure", + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, + + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/playwright-tests/tests/0000__M.test.ts b/playwright-tests/tests/0000__M.test.ts new file mode 100644 index 00000000..2a6126f3 --- /dev/null +++ b/playwright-tests/tests/0000__M.test.ts @@ -0,0 +1,628 @@ +import TestCase from "@business-logic/types/TestCase"; +import { expect, Page } from "@playwright/test"; +import { addSmokeTagToRandomTest, prepareTest, test, TestInfo } from "@business-logic/types/Test"; +import { RuleEngine, ValidationOptions } from "@business-logic/types/RuleEngine"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import essentialHpTestCases from "./0016_EssentialRepairInShop"; +import essentialReplaceTestCases from "./0013_EssentialReplace"; +import { BailoutCode, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import essentialTpaNotEnabledTestCases from "./0003_EssentialTpaNotEnabledBailout"; +import essentialDoNotSeeShopTestCases from "./0011_EssentialDoNotSeeShopBailout"; +import essentialVehicleNotFoundTestCases from "./0010_EssentialVehicleNotFoundBailout"; +import essentialPartsServiceErrorBailout_0009 from "./0009_EssentialPartsServiceErrorBailout"; +import essentialHeavyVehicleBailoutTestCases from "./0008_EssentialHeavyVehicleBailout"; +import essentialRepairMobileTests from "./0014_EssentialRepairMobile"; +import essentialReplacePartsQuestionsDropoff_0015 from "./0015_EssentialReplacePartsQuestionsDropoff"; +import essentialUniqueGlassTests from "./0012_EssentialUniqueGlass"; +import essentialRepairInShopAcuraTests from "./0002_EssentialRepairInShopAcura"; +import essentialTpaEnabled_0004 from "./0004_EssentialTpaEnabled"; +import essentialRepairMobileHyundaiTests from "./0007_EssentialRepairHyundaiMobile"; +import essentialTpaNotEnabledReplace_0017 from "./0017_EssentialTpaNotEnabledBailoutReplace"; +import essentialTpaEnabledReplace_0018 from "./0018_EssentialTpaEnabledReplace"; +import essentialTpaEnabledReplaceRecal_0019 from "./0019_EssentialTpaEnabledReplaceRecal"; +import advancedScenario0001TestCases from "./advanced/0001a_ReplaceInShopCredit"; +import advancedScenario0003TestCases from "./advanced/0003a_MobileAfterpay"; +import essentialReplaceDynamicAdasTests from "./0005_EssentialReplaceDynamicAdas"; +import essentialReplaceStaticAdasTests from "./0001_EssentialReplaceStatisAdas"; +import essentialVehicleLookupBailoutTests from "./0020_EssentialVehicleLookupBailout"; +import essentialPriceServiceErrorBailoutTests from "./0021_EssentialPriceServiceErrorBailout"; +import { forceAPIError, mockApiResponse } from "@impl/utils/HttpUtils"; +import advancedScenario0002TestCases from "./advanced/0002a_ReplaceOemEndorsement"; +import ApiResponseInterceptUtil from "@impl/api/ApiResponseInterceptUtil"; +import advancedScenario0004aTestCases from "./advanced/0004a_NoDeductibleAdas"; +import advancedScenario0006TestCases from "./advanced/0006a_RepairNoDeductibleMobile"; +import advancedScenario0007TestCases from "./advanced/0007a_RepairStateLanguage"; +import advancedScenario0008TestCases from "./advanced/0008a_RepairTpa"; +import advancedScenario0011TestCases from "./advanced/0011a_ItacNoAdas"; +import advancedScenario0012TestCases from "./advanced/0012a_ItacDropOff"; +import advancedScenario0014TestCases from "./advanced/0014a_NoCompAdas"; +import advancedScenario0013TestCases from "./advanced/0013a_ItacMobile"; +import advancedScenario0015TestCases from "./advanced/0015a_NoCompPartQuestions"; +import advancedScenario0016TestCases from "./advanced/0016a_NoCompAllGlass"; +import advancedScenario0017TestCases from "./advanced/0017a_NoCompPremium"; +import advancedScenario0018TestCases from "./advanced/0018a_NoCompGlassOnly"; +import advancedScenario0019TestCases from "./advanced/0019a_NoCompEditVehicle"; +import advancedScenario0020TestCases from "./advanced/0020a_NoCompChangeLoc"; +import advancedScenario0005TestCases from "./advanced/0005a_CapabilityQuestions"; +import advancedScenario0009TestCases from "./advanced/0009a_NoDeductibleFlorida"; +import advancedScenario0010TestCases from "./advanced/0010a_RearGlass"; + + +test.describe.parallel('ISS QA Automation Regression', () => { + const ruleEngine = new RuleEngine(); + const options = new ValidationOptions(); + addSmokeTagToRandomTest(essentialReplaceStaticAdasTests); + addSmokeTagToRandomTest(essentialRepairInShopAcuraTests); + addSmokeTagToRandomTest(essentialTpaNotEnabledTestCases); + addSmokeTagToRandomTest(essentialTpaEnabled_0004); + addSmokeTagToRandomTest(essentialReplaceDynamicAdasTests); + addSmokeTagToRandomTest(essentialRepairMobileHyundaiTests); + addSmokeTagToRandomTest(essentialVehicleNotFoundTestCases); + addSmokeTagToRandomTest(essentialHeavyVehicleBailoutTestCases); + addSmokeTagToRandomTest(essentialPartsServiceErrorBailout_0009); + addSmokeTagToRandomTest(essentialDoNotSeeShopTestCases); + addSmokeTagToRandomTest(essentialUniqueGlassTests); + addSmokeTagToRandomTest(essentialReplaceTestCases); + addSmokeTagToRandomTest(essentialRepairMobileTests); + addSmokeTagToRandomTest(essentialReplacePartsQuestionsDropoff_0015); + addSmokeTagToRandomTest(essentialHpTestCases); + addSmokeTagToRandomTest(essentialTpaNotEnabledReplace_0017); + addSmokeTagToRandomTest(essentialTpaEnabledReplace_0018); + addSmokeTagToRandomTest(essentialTpaEnabledReplaceRecal_0019); + addSmokeTagToRandomTest(essentialVehicleLookupBailoutTests); + addSmokeTagToRandomTest(essentialPriceServiceErrorBailoutTests); + + //Scenario 1 + for (const testCase of essentialReplaceStaticAdasTests) { + + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 2 + for (const testCase of essentialRepairInShopAcuraTests) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 3 + for (const testCase of essentialTpaNotEnabledTestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 4 + for (const testCase of essentialTpaEnabled_0004) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 5 + for (const testCase of essentialReplaceDynamicAdasTests) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 7 + for (const testCase of essentialRepairMobileHyundaiTests) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 8 + for (const testCase of essentialHeavyVehicleBailoutTestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 9 + for (const testCase of essentialPartsServiceErrorBailout_0009) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 10 + for (const testCase of essentialVehicleNotFoundTestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 11 + for (const testCase of essentialDoNotSeeShopTestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 12 + for (const testCase of essentialUniqueGlassTests) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 13 //defect# SSR-2009 + for (const testCase of essentialReplaceTestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 14 + for (const testCase of essentialRepairMobileTests) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 15 + for (const testCase of essentialReplacePartsQuestionsDropoff_0015) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 16 + for (const testCase of essentialHpTestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 17 + for (const testCase of essentialTpaNotEnabledReplace_0017) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 18 + for (const testCase of essentialTpaEnabledReplace_0018) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 19 + for (const testCase of essentialTpaEnabledReplaceRecal_0019) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + //Scenario 20 + for (const testCase of essentialVehicleLookupBailoutTests) { + test(...prepareTest(testCase, run, options, ruleEngine)); //skipping this until SSR-2004 is fixed + } + //Scenario 21 + for (const testCase of essentialPriceServiceErrorBailoutTests) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Advanced Scenarios + + // Scenario 0001a + for (const testCase of advancedScenario0001TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0002a + for (const testCase of advancedScenario0002TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0003a + // Note: payment will fail in dev. Payment (PIA) works fine in SYS + for (const testCase of advancedScenario0003TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0004a + for (const testCase of advancedScenario0004aTestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0005a + for (const testCase of advancedScenario0005TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0006a + for (const testCase of advancedScenario0006TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0007a + for (const testCase of advancedScenario0007TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0008a + for (const testCase of advancedScenario0008TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0009a + for (const testCase of advancedScenario0009TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + // Scenario 0010a + for (const testCase of advancedScenario0010TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0011a + for (const testCase of advancedScenario0011TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0012a + for (const testCase of advancedScenario0012TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0013a + for (const testCase of advancedScenario0013TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0014a + for (const testCase of advancedScenario0014TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0015a + for (const testCase of advancedScenario0015TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0016a + // FIXME: + for (const testCase of advancedScenario0016TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0017a + for (const testCase of advancedScenario0017TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0018a + for (const testCase of advancedScenario0018TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0019a + for (const testCase of advancedScenario0019TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } + + // Scenario 0020a + for (const testCase of advancedScenario0020TestCases) { + test(...prepareTest(testCase, run, options, ruleEngine)); + } +}); + + +test.afterEach(async ({ page, testInfo }) => { + await TestCase.afterEachMethod(page, testInfo); +}); + +async function run(page: Page, testInfo: TestInfo): Promise { + + await testInfo.testCase.setup(); + testInfo.testCase.setupPages(page); + if (testInfo.testCase.testData.isAuthenticationRequired) { + await testInfo.testCase.pages.welcomePage.gotoWithAuthentication(testInfo.testCase.testData!.clientTag!); + } + else { + await testInfo.testCase.pages.welcomePage.goto(testInfo.testCase.testData!.clientTag!); + } + await runWorkflow(page, testInfo.testCase); +} + +async function runWorkflow(page: Page, testCase: TestCase) { + // Intercept API Responses + const apiResponseInterceptUtil = new ApiResponseInterceptUtil(testCase.testData); + page.on('response', apiResponseInterceptUtil.handleInterceptResponse); + + + // Destructure data for easy access + const { customerDetails, claimDetails, vehicleDetails, vehicleDamage, + appointmentDetails, isSafelite, endorsements, + partQuestions, paymentDetails, isNoComp, isItac, isRecalNotification, + isRecalWarning, servicePackage, hasStateLawPopup, otherVehiclesOnPolicy, + isSeparateApptsWarning, vehiclePartQuestions, editVehicleDetails, + hasMilitaryWarning, capabilityQuestions } = testCase.testData; + + let { isPolicyFound } = testCase.testData; // Allow isPolicyFound to be re-assigned + + // Destructure pages for easy access + const { welcomePage, duplicateCheckPage, policyHolderDetailsPage, + vehicleSelectionPage, vehicleDamagePage, coverageStatementPage, + providerPreferencePage, serviceLocationPage, servicePackagesPage, + schedulePage, contactDetailsPage, orderConfirmationPage, policyVehiclesPage, + endorsementsPage, vehicleLookupPage, partQuestionsPage, paymentMethodPage, + vehicleLookupAddressPage, vehicleLookupLicensePage, vinLookupPage, + bailoutPage, tpaSearchPage, tpaSubmitPage, tpaConfirmationPage, + vehiclePartQuestionsPage, capabilityQuestionsPage } = testCase.pages; + + // Destructure bailout flags + const { isVehicleSelectBailout, isDoNotSeeMyShopBailout, isTpaNotEnabledBailout, + isRequestCallbackBailout, isHeavyTruckVehicleBailout, isPartsServiceErrorBailout, + isSafeliteNotTheProviderBailout, isVehicleLookupBailout, isPriceServiceErrorBailout } = testCase.testData.bailoutFlags || {}; + + const repairTypes: VehicleDamage[] = [ + VehicleDamage.WindshieldOneChip, + VehicleDamage.WindshieldTwoChips, + VehicleDamage.WindshieldThreeChips + ] + + // Are we replacing or repairing? + const isReplace = !repairTypes.some(damageType => { + return vehicleDamage!.includes(damageType); + }); + + const hasEndorsements = endorsements && endorsements.length > 0; + + await test.step('WelcomePage >> Populate Customer Details', async () => { + await welcomePage.populatePage(customerDetails!, claimDetails!, await welcomePage.hasCityInfo()); + + // mockApiResponse(page, 'location/api/v1/location/zip/43016', 'common', testCase.testData!.mockTesting || false); + mockApiResponse(page, 'location/api/v1/location/zip/36116', 'common',testCase.testData!.isMockTesting || false); + mockApiResponse(page, 'coverage/api/v1/coverage/policy-information', 'scenario1', testCase.testData!.isMockTesting || false); + await welcomePage.nextPage(); + }); + + if (testCase.testData.isDuplicateClaim) { + // TODO: Scenarios where we want to resume from duplicate + // TODO: Dynamic for when we don't care if duplicate page appears + + await test.step('DuplicateCheckPage >> Start New Claim', async () => { + await duplicateCheckPage.startNewClaim(); + await duplicateCheckPage.nextPage(); + }); + } + + if (isPolicyFound) { + await test.step('PolicyVehiclesPage >> Select vehicle', async () => { + // Validate other vehicles on policy + if (otherVehiclesOnPolicy && otherVehiclesOnPolicy.length > 0) { + for (const vehicle of otherVehiclesOnPolicy) { + await policyVehiclesPage.validateVehicleIsOnPolicy(vehicle); + } + } + + // Select vehicle + await policyVehiclesPage.selectVehicle(vehicleDetails!); + await policyVehiclesPage.nextPage(); + }); + + if (isHeavyTruckVehicleBailout) { + await test.step('BailoutPage >> Heavy Vehicle Bailout', async () => { + await bailoutPage.validateBailoutDetails(customerDetails!, BailoutCode.HeavyTruckVehicle); + }); + return; + } + + if (hasEndorsements) { + await test.step('EndorsementsPage >> Select Endorsements', async () => { + await endorsementsPage.verifyEndorsements(endorsements); + await endorsementsPage.selectEndorsements(endorsements); + await endorsementsPage.nextPage(); + }); + } + + } else { + await test.step('PolicyHolderDetailsPage >> Enter customer data', async () => { + await policyHolderDetailsPage.fillCustomerDetails(customerDetails!); + await policyHolderDetailsPage.nextPage(); + }); + + if (isVehicleLookupBailout){ + forceAPIError(page, '/vehicle/api/v1/vehicle/lookup') + } + + await test.step('VehicleDetailsPage >> Select Vehicle', async () => { + await vehicleSelectionPage.selectVehicle(vehicleDetails!); + await vehicleSelectionPage.nextPage(); + }); + + if (isVehicleLookupBailout) { + await test.step('BailoutPage >> Vehicle Lookup Bailout', async () => { + await bailoutPage.validateBailoutDetails(customerDetails!, BailoutCode.VehicleLookupError); + }); + return; + } + + if (isHeavyTruckVehicleBailout) { + await test.step('BailoutPage >> Heavy Vehicle Bailout', async () => { + await bailoutPage.validateBailoutDetails(customerDetails!, BailoutCode.HeavyTruckVehicle); + }); + return; + } + } + + if (editVehicleDetails) { + isPolicyFound = false; // Flow proceeds as unverified + testCase.testData.isPolicyFound = false; + await test.step('VehicleDamagePage >> Click Edit Vehicle', async () => { + await vehicleDamagePage.editVehicleButton.click(); + }); + + await test.step('VehicleDetailsPage >> Select edited vehicle', async () => { + await test.step('VehicleDetailsPage >> Select Vehicle', async () => { + await vehicleSelectionPage.selectVehicle(editVehicleDetails); + await vehicleSelectionPage.nextPage(); + }); + }); + } + + await test.step('VehicleDamagePage >> Select Damage', async () => { + if (isSeparateApptsWarning) { + await vehicleDamagePage.checkSeparateApptsWarning(); + } + await vehicleDamagePage.selectDamage(vehicleDamage!); + await vehicleDamagePage.nextPage(); + }); + + if (isPartsServiceErrorBailout) { + await test.step('VehicleLookupPage >> Select Lookup Type', async () => { + await vehicleLookupPage.vehicleLookup(vehicleDetails!); + }); + await test.step('VinLookupPage >> Lookup by VIN', async () => { + await vinLookupPage.enterVin(vehicleDetails!.vin!); + forceAPIError(page, '/parts/api/v1/parts') + await vinLookupPage.nextPage(); + }); + await test.step('BailoutPage >> Parts Service Error Bailout', async () => { + await bailoutPage.validateBailoutDetails(customerDetails!, BailoutCode.PartsServiceError); + return; + }); + return; + } + + if (isReplace && !(isPolicyFound)) { + await test.step('VehicleLookupPage >> Select Lookup Type' + vehicleDetails?.vehicleLookupType, async () => { + await vehicleLookupPage.vehicleLookup(vehicleDetails!); + }); + + if (isVehicleSelectBailout) { + await test.step('BailoutPage >> Validate Bailout', async () => { + await vinLookupPage.enterVin(vehicleDetails!.vin!); + await vinLookupPage.triggerBailout(); + await bailoutPage.validateBailoutDetails(customerDetails!, BailoutCode.VehicleNotFound); + }); + return; + } + switch (vehicleDetails!.vehicleLookupType!) { + case VehicleLookupType.Address: + await test.step('VehicleLookupAddressPage >> Lookup by address: ' + customerDetails!.address.street, async () => { + await vehicleLookupAddressPage.lookupVehicleByAddress(customerDetails!, vehicleDetails!); + await vehicleLookupAddressPage.nextPage(); + }); + break; + case VehicleLookupType.LicensePlateNumber: + await test.step('VehicleLookupLicensePage >> Lookup by license plate: ' + vehicleDetails!.licensePlateNumber, async () => { + await vehicleLookupLicensePage.enterPlateDetails(vehicleDetails!); + await vehicleLookupLicensePage.nextPage(); + }); + break; + case VehicleLookupType.Vin: + await test.step('VinLookupPage >> Lookup by VIN: ' + vehicleDetails!.vin!, async () => { + await vinLookupPage.enterVin(vehicleDetails!.vin!); + await vinLookupPage.nextPage(); + }); + break; + } + } + + if (capabilityQuestions && capabilityQuestions.length > 0) { + await capabilityQuestionsPage.validatePartQuestions(capabilityQuestions); + await capabilityQuestionsPage.selectPartQuestionResponses(capabilityQuestions); + await capabilityQuestionsPage.nextPage(); + } + + if (partQuestions && partQuestions.length > 0) { + await partQuestionsPage.validatePartQuestions(partQuestions); + await partQuestionsPage.selectPartQuestionResponses(partQuestions); + await partQuestionsPage.nextPage(); + } + + if (vehiclePartQuestions && vehiclePartQuestions.length > 0) { + await vehiclePartQuestionsPage.validatePartQuestions(vehiclePartQuestions); + await vehiclePartQuestionsPage.selectPartQuestionResponses(vehiclePartQuestions); + await vehiclePartQuestionsPage.nextPage(); + } + + await test.step('CoverageStatementPage >> Next page', async () => { + // Confirm no coverage + if (isPolicyFound && (isItac || isNoComp)) { + await coverageStatementPage.continueToScheduleButton.click(); + } + + await coverageStatementPage.nextPage(); + }); + + if (hasStateLawPopup) { + await test.step('ProviderPreferencePage >> Dismiss state law popup', async () => { + await providerPreferencePage.validateStateLawModalIsVisible(); + await providerPreferencePage.gotItButton.click(); + }); + } + + if (!isPolicyFound || !(isItac || isNoComp)) { + await test.step('ProviderPreferencePage >> Select Provider ' + isSafelite ? "Safelite" : "Other shops(Non-Safelite)", async () => { + await providerPreferencePage.selectProvider(isSafelite); + }); + } + + // validations for Recal warning mesage + if (isRecalWarning) { + await serviceLocationPage.validateRecalWarning(); + } + + // if isRecalNotifidation flag true additional step to acknowledge Recal notification. + if (isRecalNotification) { + await providerPreferencePage.acknowledgeRecalNotificaiton(); + } + // If No-Comp or ITAC, ProviderPreferencePage does not appear + if (!isPolicyFound || !(isItac || isNoComp)) { + + + if (!isSafelite) { + if (testCase.testData!.clientTag == '05CC1609-3631-4044-B45A-E78E13343B9A') { // Avoiding TPA flow for Federated Insureance due to Defect# SSR-1984 //Temporary fix until Defect# SSR-1984 is addressed. + await test.step('***** Performing Safelite flow for Federal Insurance due to defec# SSR-1984 *****', async () => { }); + await test.step('TpaSearchPage >> TPA Search', async () => { + await tpaSearchPage.selectFirstLocation(); + // await tpaSearchPage.nextPage(); + }); + return; + } + if (isTpaNotEnabledBailout) { + await test.step('validateBailoutDetails >> Bailout code : ' + BailoutCode.TPANotEnabled, async () => { + await bailoutPage.validateBailoutDetails(customerDetails!, BailoutCode.TPANotEnabled); + return; + }); + return; + } + + if (isDoNotSeeMyShopBailout) { + await test.step('TpaSearchPage >> Select "Do Not See My Shop"', async () => { + await tpaSearchPage.selectDoNotSeeMyShop(); + }); + await bailoutPage.validateBailoutDetails(customerDetails!, BailoutCode.DoNotSeeMyShop); + return; + } + + await test.step('TpaSearchPage >> TPA Search', async () => { + await tpaSearchPage.selectFirstLocation(); + await tpaSearchPage.nextPage(); + }); + + await test.step('TpaSubmitPage >> TPA Submit', async () => { + // TODO: Validations + await tpaSubmitPage.nextPage(); + }); + + await test.step('TpaConfirmationPage >> TPA Confirmation', async () => { + await tpaConfirmationPage.validateSuccessMessage(); + return; + }); + return; + } + } + + await test.step('ServiceLocationPage >> Select service location', async () => { + await serviceLocationPage.selectLocation(appointmentDetails!); + if (hasMilitaryWarning) { + await expect.soft(serviceLocationPage.militaryWarningMessage).toBeVisible(); + } + await serviceLocationPage.nextPage(); + }); + + await test.step('SchedulePage >> Select day and time', async () => { + customerDetails!.apptDate = await schedulePage.scheduleFirstAppointment(appointmentDetails!.serviceLocation); + }); + + await test.step('ContactDetailsPage >> Validate contact details', async () => { + const expectedContactDetails: Partial = { + firstName: customerDetails!.firstName, + lastName: customerDetails!.lastName, + email: customerDetails!.email, + phoneNumber: customerDetails!.phoneNumber + }; + const actualContactDetails = await contactDetailsPage.getContactDetails(); + expect.soft(actualContactDetails).toEqual(expectedContactDetails); + + await contactDetailsPage.fillNotes(customerDetails!.notes); + + if (isPriceServiceErrorBailout) { + forceAPIError(page, '/price/api/v1/price/combined-quote'); + } + + await contactDetailsPage.nextPage(); + }); + + if (isPriceServiceErrorBailout) { + await test.step('BailoutPage >> Price Service Error Bailout', async () => { + await bailoutPage.validateBailoutDetails(customerDetails!, BailoutCode.PricingResponseError); + }); + return; + } + + await test.step('ServicePackagesPage >> Choose service package', async () => { + customerDetails!.packagePrice = await servicePackagesPage.selectServicePackage(servicePackage!); + await servicePackagesPage.nextPage(); + }); + + if (isPolicyFound && claimDetails!.policyDeductible > 0) { + await test.step('PaymentMethodPage >> Execute Payment', async () => { + await paymentMethodPage.executePayment(paymentDetails!); + await paymentMethodPage.nextPage(); + }); + } else { + await test.step('PaymentMethodPage >> Skip to Order Confirmation', async () => { + await paymentMethodPage.nextPage(); + }); + } + + await test.step('OrderConfirmationPage >> Validate order', async () => { + await orderConfirmationPage.validateOrderConfirmationPage(testCase.testData); + }); +} \ No newline at end of file diff --git a/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts b/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts new file mode 100644 index 00000000..cc3da886 --- /dev/null +++ b/playwright-tests/tests/0001_EssentialReplaceStatisAdas.ts @@ -0,0 +1,70 @@ +import ClientData from "@business-logic/data/ClientData"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import TestCase from "@business-logic/types/TestCase"; +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialReplaceStaticAdasData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: faker.internet.email(), + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Knoxville', + state: 'Tennessee', + postalCode: '37996', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2019', + make: 'Toyota', + model: 'C-HR', + style: '4 door hatchback', + vin: 'NMTKHMBX5KR086519', + vehicleLookupType: VehicleLookupType.Vin, + + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.DropOff, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialReplaceStaticAdasTests: TestCase[] = []; +for (const client of essentialClients) { + const data = { ...essentialReplaceStaticAdasData }; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0001 Essential Replace Statis ADAS Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Essentials','@0001', '@test_report'], + testData: data + }, undefined, '0001'); + essentialReplaceStaticAdasTests.push(tc); +} + +export default essentialReplaceStaticAdasTests; \ No newline at end of file diff --git a/playwright-tests/tests/0002_EssentialRepairInShopAcura.ts b/playwright-tests/tests/0002_EssentialRepairInShopAcura.ts new file mode 100644 index 00000000..f05482a4 --- /dev/null +++ b/playwright-tests/tests/0002_EssentialRepairInShopAcura.ts @@ -0,0 +1,70 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialRepairInShopAcuraData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + servicePackage: ServicePackage.Premium, + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Fort Collins', + state: 'Colorado', + postalCode: '80526', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2020', + make: 'Acura', + model: 'ILX', + style: '4 door sedan', + vin: '19UDE2F38LA001705' + }, + vehicleDamage: [ + VehicleDamage.WindshieldOneChip, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialRepairInShopAcuraTests: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialRepairInShopAcuraData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0002 Essential Repair Mobile Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`], + testData: data + }, undefined, '0002'); + essentialRepairInShopAcuraTests.push(tc); +} + +export default essentialRepairInShopAcuraTests; \ No newline at end of file diff --git a/playwright-tests/tests/0003_EssentialTpaNotEnabledBailout.ts b/playwright-tests/tests/0003_EssentialTpaNotEnabledBailout.ts new file mode 100644 index 00000000..65f27111 --- /dev/null +++ b/playwright-tests/tests/0003_EssentialTpaNotEnabledBailout.ts @@ -0,0 +1,81 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialTpaNotEnabledData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: false, + bailoutFlags: { + isTpaNotEnabledBailout: true + }, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Dublin', + state: 'Ohio', + postalCode: '43016', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2022', + make: 'Honda', + model: 'Civic', + style: '4 door hatchback' + }, + vehicleDamage: [ + VehicleDamage.WindshieldThreeChips, + // VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '6826 Sawmill Rd, Columbus, OH 43235', + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClientsWithTpaDisabled(); +const essentialTpaNotEnabledTestCases: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialTpaNotEnabledData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0003 Essential TPA Not Enabled Bailout Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Bailout', '@TpaNotEnabled', '@Essentials'], + testData: data + }, undefined, '0003'); + essentialTpaNotEnabledTestCases.push(tc); +} + +export default essentialTpaNotEnabledTestCases; \ No newline at end of file diff --git a/playwright-tests/tests/0004_EssentialTpaEnabled.ts b/playwright-tests/tests/0004_EssentialTpaEnabled.ts new file mode 100644 index 00000000..4aed4e19 --- /dev/null +++ b/playwright-tests/tests/0004_EssentialTpaEnabled.ts @@ -0,0 +1,83 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialTpaEnabledData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: false, + bailoutFlags: { + isTpaNotEnabledBailout: false + }, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + // phoneNumber: faker.phone.toString(), + notes: 'Automated Test', + address: { + // street: faker.location.streetAddress(), + street: '134 Woodlands Place', + city: 'Dublin', + state: 'Florida', + postalCode: '32040', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2022', + make: 'Honda', + model: 'Civic', + style: '4 door hatchback' + }, + vehicleDamage: [ + VehicleDamage.WindshieldThreeChips, + // VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '6826 Sawmill Rd, Columbus, OH 43235', + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClientsWithTpaEnabled(); +const essentialTpaEnabled_0004: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialTpaEnabledData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0004 Essential TPA Enabled Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@TpaEnabled', '@Essentials','@0004'], + testData: data + }, undefined, '0004'); + essentialTpaEnabled_0004.push(tc); +} + +export default essentialTpaEnabled_0004; \ No newline at end of file diff --git a/playwright-tests/tests/0005_EssentialReplaceDynamicAdas.ts b/playwright-tests/tests/0005_EssentialReplaceDynamicAdas.ts new file mode 100644 index 00000000..9dded5fc --- /dev/null +++ b/playwright-tests/tests/0005_EssentialReplaceDynamicAdas.ts @@ -0,0 +1,78 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialReplaceDynamicAdasData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + servicePackage: ServicePackage.Premium, + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Chicago', + state: 'Illinois', + postalCode: '60645', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2021', + make: 'BMW', + model: '740', + style: '4 door sedan', + vin: 'WBA7T2C01LGL17632', + vehicleLookupType: VehicleLookupType.Vin, + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + serviceAddress: { + street: "2088 Haviland Road, Columbus, OH, USA", + city:"Vermillion", + state: "Ohio", + postalCode: "44089", + country: "undefined" + }, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialReplaceDynamicAdasTests: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialReplaceDynamicAdasData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0005 Essential Replace Dynamic ADAS Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`], + testData: data + }, undefined, '0005'); + essentialReplaceDynamicAdasTests.push(tc); +} + +export default essentialReplaceDynamicAdasTests; \ No newline at end of file diff --git a/playwright-tests/tests/0007_EssentialRepairHyundaiMobile.ts b/playwright-tests/tests/0007_EssentialRepairHyundaiMobile.ts new file mode 100644 index 00000000..5a1d4c70 --- /dev/null +++ b/playwright-tests/tests/0007_EssentialRepairHyundaiMobile.ts @@ -0,0 +1,77 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialRepairMobileHyundaiData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + servicePackage: ServicePackage.Premium, + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Raleigh', + state: 'North Carolina', + postalCode: '27615', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2013', + make: 'Hyundai', + model: 'Sonata', + style: '4 door sedan', + vin: '5NPEC4AB6DH791034' + }, + vehicleDamage: [ + VehicleDamage.WindshieldTwoChips, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + serviceAddress: { + street: "2088 Haviland Road, Columbus, OH, USA", + city:"Vermillion", + state: "Ohio", + postalCode: "44089", + country: "undefined" + }, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialRepairMobileHyundaiTests: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialRepairMobileHyundaiData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0007 Essential Repair Hyundai Mobile Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`], + testData: data + }, undefined, '0007'); + essentialRepairMobileHyundaiTests.push(tc); +} + +export default essentialRepairMobileHyundaiTests; \ No newline at end of file diff --git a/playwright-tests/tests/0008_EssentialHeavyVehicleBailout.ts b/playwright-tests/tests/0008_EssentialHeavyVehicleBailout.ts new file mode 100644 index 00000000..f53c2548 --- /dev/null +++ b/playwright-tests/tests/0008_EssentialHeavyVehicleBailout.ts @@ -0,0 +1,81 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialHeavyVehicleBailoutData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + bailoutFlags: { + isHeavyTruckVehicleBailout: true + }, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Dublin', + state: 'Ohio', + postalCode: '43016', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2017', + make: 'Freightliner', + model: '114sd', + style: 'conventional cab' // TODO: Check correctness of vehicle style + }, + vehicleDamage: [ + // VehicleDamage.WindshieldThreeChips, + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '6826 Sawmill Rd, Columbus, OH 43235', + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialHeavyVehicleBailoutTestCases: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialHeavyVehicleBailoutData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0008 Essential Heavy Vehicle Bailout Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Bailout', '@HeavyVehicle', '@Essentials'], + testData: data + }, undefined, '0008'); + essentialHeavyVehicleBailoutTestCases.push(tc); +} + +export default essentialHeavyVehicleBailoutTestCases; \ No newline at end of file diff --git a/playwright-tests/tests/0009_EssentialPartsServiceErrorBailout.ts b/playwright-tests/tests/0009_EssentialPartsServiceErrorBailout.ts new file mode 100644 index 00000000..d4f9a9b5 --- /dev/null +++ b/playwright-tests/tests/0009_EssentialPartsServiceErrorBailout.ts @@ -0,0 +1,83 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialPartsServiceErrorData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + bailoutFlags: { + isPartsServiceErrorBailout: true + }, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Tulare', + state: 'California', + postalCode: '93247', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2012', + make: 'Dodge', + model: 'Charger', + style: '4 door sedan', + vin: '2C3CDXBG6CH260654', + vehicleLookupType: VehicleLookupType.Vin + }, + vehicleDamage: [ + // VehicleDamage.WindshieldThreeChips, + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '6826 Sawmill Rd, Columbus, OH 43235', + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialPartsServiceErrorBailout_0009: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialPartsServiceErrorData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0009 Essential Parts Service Error Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Bailout', '@PartsServiceError', '@Essentials','@0009'], + testData: data + }, undefined, '0009'); + essentialPartsServiceErrorBailout_0009.push(tc); +} + +export default essentialPartsServiceErrorBailout_0009; \ No newline at end of file diff --git a/playwright-tests/tests/0010_EssentialVehicleNotFoundBailout.ts b/playwright-tests/tests/0010_EssentialVehicleNotFoundBailout.ts new file mode 100644 index 00000000..a2515e6b --- /dev/null +++ b/playwright-tests/tests/0010_EssentialVehicleNotFoundBailout.ts @@ -0,0 +1,83 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialVehicleNotFoundData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + bailoutFlags: { + isVehicleSelectBailout: true + }, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Dublin', + state: 'Ohio', + postalCode: '43016', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2022', + make: 'Honda', + model: 'Civic', + style: '4 door hatchback', + vin: '0HGCR2E30FA099831', + vehicleLookupType: VehicleLookupType.Vin + }, + vehicleDamage: [ + // VehicleDamage.WindshieldThreeChips, + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '6826 Sawmill Rd, Columbus, OH 43235', + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialVehicleNotFoundTestCases: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialVehicleNotFoundData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0010 Essential Vehicle Not Found Bailout Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Bailout', '@VehicleNotFound'], + testData: data + }, undefined, '0001'); + essentialVehicleNotFoundTestCases.push(tc); +} + +export default essentialVehicleNotFoundTestCases; \ No newline at end of file diff --git a/playwright-tests/tests/0011_EssentialDoNotSeeShopBailout.ts b/playwright-tests/tests/0011_EssentialDoNotSeeShopBailout.ts new file mode 100644 index 00000000..40bdd106 --- /dev/null +++ b/playwright-tests/tests/0011_EssentialDoNotSeeShopBailout.ts @@ -0,0 +1,81 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialDoNotSeeShopData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: false, + bailoutFlags: { + isDoNotSeeMyShopBailout: true + }, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Dublin', + state: 'Ohio', + postalCode: '43016', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2022', + make: 'Honda', + model: 'Civic', + style: '4 door hatchback' + }, + vehicleDamage: [ + VehicleDamage.WindshieldThreeChips, + // VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '6826 Sawmill Rd, Columbus, OH 43235', + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClientsWithTpaEnabled(); +const essentialDoNotSeeShopTestCases: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialDoNotSeeShopData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0011 Essential DoNotSeeShop Bailout Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Bailout', '@DoNotSeeShop', '@Essentials'], + testData: data + }, undefined, '0011'); + essentialDoNotSeeShopTestCases.push(tc); +} + +export default essentialDoNotSeeShopTestCases; \ No newline at end of file diff --git a/playwright-tests/tests/0012_EssentialUniqueGlass.ts b/playwright-tests/tests/0012_EssentialUniqueGlass.ts new file mode 100644 index 00000000..2cce1cef --- /dev/null +++ b/playwright-tests/tests/0012_EssentialUniqueGlass.ts @@ -0,0 +1,84 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialUnqiqueGlassData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Tacoma', + state: 'Washington', + postalCode: '98409', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2019', + make: 'Ram', + model: 'Promaster', + style: 'cargo van', + vin: '3C6TRVBG8KE566001', + vehicleLookupType: VehicleLookupType.Vin + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + VehicleDamage.DriverSlidingDoor + ], + vehiclePartQuestions: [ + { + partQuestionType: PartQuestionType.WindshieldColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.DriverSideColor, + isOnPage: true, + optionToSelect: 'Green Tint', + secondaryQuestionOptionToSelect: 'solar, driver side, rear' + }, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.DropOff, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialUniqueGlassTests: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialUnqiqueGlassData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0012 Essential Unique Glass Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`], + testData: data + }, undefined, '0012'); + essentialUniqueGlassTests.push(tc); +} + +export default essentialUniqueGlassTests; \ No newline at end of file diff --git a/playwright-tests/tests/0013_EssentialReplace.ts b/playwright-tests/tests/0013_EssentialReplace.ts new file mode 100644 index 00000000..4e139432 --- /dev/null +++ b/playwright-tests/tests/0013_EssentialReplace.ts @@ -0,0 +1,102 @@ +import ClientData from "@business-logic/data/ClientData"; +import { DamageType, PartQuestionType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import TestCase from "@business-logic/types/TestCase"; +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialReplaceData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + vehiclePartQuestions: [ + { + partQuestionType: PartQuestionType.WindshieldColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.DriverFrontColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.DriverRearColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.PassengerFrontColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.PassengerRearColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: 'Reed', + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: '10212 JEWEL CT',// DO NOT use fake address here as this scenario search vehicle by address //faker.location.streetAddress(), + city: 'CONROE', + state: 'Texas', + postalCode: '77385', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2015', + make: 'Ford', + model: 'F Series F150', + style: '2 door super cab', + vin: '5N1AN0NW9BC524974', + vehicleLookupType: VehicleLookupType.Address, + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + VehicleDamage.DriverFrontDoor, + VehicleDamage.DriverRearDoor, + VehicleDamage.PassengerFrontDoor, + VehicleDamage.PassengerRearDoor, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialReplaceTestCases: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialReplaceData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0013 Essential Replace Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Essentials'], + testData: data + }, undefined, '0013'); + essentialReplaceTestCases.push(tc); +} + +export default essentialReplaceTestCases; \ No newline at end of file diff --git a/playwright-tests/tests/0014_EssentialRepairMobile.ts b/playwright-tests/tests/0014_EssentialRepairMobile.ts new file mode 100644 index 00000000..085b9fea --- /dev/null +++ b/playwright-tests/tests/0014_EssentialRepairMobile.ts @@ -0,0 +1,77 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialRepairMobileData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Dublin', + state: 'Ohio', + postalCode: '43016', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2021', + make: 'Subaru', + model: 'Outback', + style: '4 door station wagon', + vin: '4S4BTAFC7M3163249' + }, + vehicleDamage: [ + VehicleDamage.WindshieldThreeChips, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + serviceAddress: { + street: "2088 Haviland Road, Columbus, OH, USA", + city:"Vermillion", + state: "Ohio", + postalCode: "44089", + country: "undefined" + }, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialRepairMobileTests: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialRepairMobileData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0014 Essential Repair Mobile Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Essentials'], + testData: data + }, undefined, '0014'); + essentialRepairMobileTests.push(tc); +} + +export default essentialRepairMobileTests; \ No newline at end of file diff --git a/playwright-tests/tests/0015_EssentialReplacePartsQuestionsDropoff.ts b/playwright-tests/tests/0015_EssentialReplacePartsQuestionsDropoff.ts new file mode 100644 index 00000000..de719a2f --- /dev/null +++ b/playwright-tests/tests/0015_EssentialReplacePartsQuestionsDropoff.ts @@ -0,0 +1,85 @@ +import ClientData from "@business-logic/data/ClientData"; +import { DamageType, PartQuestionType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import TestCase from "@business-logic/types/TestCase"; +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialReplaceData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + vehiclePartQuestions: [ + { + partQuestionType: PartQuestionType.WindshieldColor, + isOnPage: true, + optionToSelect: 'Green Tint, Blue Shade' + }, + // { + // partQuestionType: PartQuestionType.DriverFrontColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + ], + + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: faker.internet.email(), + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + // street: faker.location.streetAddress(), + street: '456 Columbus Pike', + city: 'Dublin', + state: 'Ohio', + postalCode: '43223', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2011', + make: 'Nissan', + model: 'XTerra', + style: '4 door utility', + vin: '5N1AN0NW9BC524974', + vehicleLookupType: VehicleLookupType.Vin, + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.DropOff, + shopAddress: undefined, + appointmentDate: nextWeekday + } +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialReplacePartsQuestionsDropoff_0015: TestCase[] = []; +for (const client of essentialClients) { + const data = { ...essentialReplaceData }; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0015 Essential Replace Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Essentials','@0015'], + testData: data + }, undefined, '0015'); + essentialReplacePartsQuestionsDropoff_0015.push(tc); +} + +export default essentialReplacePartsQuestionsDropoff_0015; \ No newline at end of file diff --git a/playwright-tests/tests/0016_EssentialRepairInShop.ts b/playwright-tests/tests/0016_EssentialRepairInShop.ts new file mode 100644 index 00000000..735f275a --- /dev/null +++ b/playwright-tests/tests/0016_EssentialRepairInShop.ts @@ -0,0 +1,78 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialHappyPathData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Dublin', + state: 'Ohio', + postalCode: '43016', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2022', + make: 'Honda', + model: 'Civic', + style: '4 door hatchback' + }, + vehicleDamage: [ + VehicleDamage.WindshieldThreeChips, + // VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '6826 Sawmill Rd, Columbus, OH 43235', + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialHpTestCases: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialHappyPathData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0016 Essential Repair Inshop Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Essentials'], + testData: data + }, undefined, '0016'); + essentialHpTestCases.push(tc); +} + +export default essentialHpTestCases; \ No newline at end of file diff --git a/playwright-tests/tests/0017_EssentialTpaNotEnabledBailoutReplace.ts b/playwright-tests/tests/0017_EssentialTpaNotEnabledBailoutReplace.ts new file mode 100644 index 00000000..0009d72f --- /dev/null +++ b/playwright-tests/tests/0017_EssentialTpaNotEnabledBailoutReplace.ts @@ -0,0 +1,86 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialTpaNotEnabledData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: true, + partQuestions: undefined, + isSafelite: false, + bailoutFlags: { + isTpaNotEnabledBailout: true + }, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + // street: faker.location.streetAddress(), + street: '7913 Station Street', + city: 'Dublin', + state: 'Ohio', + postalCode: '43016', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2015', + make: 'Ford', + model: 'F Series F150', + style: '2 door super cab', + vin: '1FTEX1C82FFB42543', + licensePlateNumber: '15377DV', + licensePlateState: 'Texas', + vehicleLookupType: VehicleLookupType.LicensePlateNumber, + }, + vehicleDamage: [ + // VehicleDamage.WindshieldThreeChips, + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '6826 Sawmill Rd, Columbus, OH 43235', + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClientsWithTpaDisabled(); +const essentialTpaNotEnabledReplace_0017: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialTpaNotEnabledData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0017 Essential TPA Not Enabled Bailout Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Bailout', '@TpaNotEnabled', '@Essentials','@0017'], + testData: data + }, undefined, '0017'); + essentialTpaNotEnabledReplace_0017.push(tc); +} + +export default essentialTpaNotEnabledReplace_0017; \ No newline at end of file diff --git a/playwright-tests/tests/0018_EssentialTpaEnabledReplace.ts b/playwright-tests/tests/0018_EssentialTpaEnabledReplace.ts new file mode 100644 index 00000000..73f29276 --- /dev/null +++ b/playwright-tests/tests/0018_EssentialTpaEnabledReplace.ts @@ -0,0 +1,87 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialTpaEnabledData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: true, + partQuestions: undefined, + isSafelite: false, + isRecalNotification: false, + bailoutFlags: { + isTpaNotEnabledBailout: false + }, + + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + // phoneNumber: faker.phone.toString(), + notes: 'Automated Test', + address: { + // street: faker.location.streetAddress(), + street: '134 Woodlands Place', + city: 'Dublin', + state: 'Ohio', + postalCode: '43232', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2013', + make: 'Hyundai', + model: 'Sonata', + style: '4 door sedan', + vin: '5NPEC4AB6DH791034', + vehicleLookupType: VehicleLookupType.Vin, + }, + vehicleDamage: [ + // VehicleDamage.WindshieldThreeChips, + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClientsWithTpaEnabled(); +const essentialTpaEnabledReplace_0018: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialTpaEnabledData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0018 Essential TPA Enabled Client-TPA happy path: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@TpaEnabled', '@Essentials','@0018'], + testData: data + }, undefined, '0018'); + essentialTpaEnabledReplace_0018.push(tc); +} + +export default essentialTpaEnabledReplace_0018; \ No newline at end of file diff --git a/playwright-tests/tests/0019_EssentialTpaEnabledReplaceRecal.ts b/playwright-tests/tests/0019_EssentialTpaEnabledReplaceRecal.ts new file mode 100644 index 00000000..839dbad1 --- /dev/null +++ b/playwright-tests/tests/0019_EssentialTpaEnabledReplaceRecal.ts @@ -0,0 +1,88 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialTpaEnabledData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: true, + partQuestions: undefined, + isSafelite: true, + isRecalNotification: false, + isRecalWarning: true, + bailoutFlags: { + isTpaNotEnabledBailout: false + }, + + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + // phoneNumber: faker.phone.toString(), + notes: 'Automated Test', + address: { + // street: faker.location.streetAddress(), + street: '134 Woodlands Place', + city: 'Jacksonville Beach', + state: 'Florida', + postalCode: '32250', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2020', + make: 'Acura', + model: 'ILX', + style: '4 door sedan', + vin: '19UDE2F38LA001705', + vehicleLookupType: VehicleLookupType.Vin, + }, + vehicleDamage: [ + // VehicleDamage.WindshieldThreeChips, + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.DropOff, + shopAddress: undefined, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialTpaEnabledReplaceRecal_0019: TestCase[] = []; +for (const client of essentialClients) { + const data = { ...essentialTpaEnabledData }; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0019 Essential TPA Enabled Client with Replace and ReCal vehicle: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Recal', '@Essentials', '@0019'], + testData: data + }, undefined, '0019'); + essentialTpaEnabledReplaceRecal_0019.push(tc); +} + +export default essentialTpaEnabledReplaceRecal_0019; \ No newline at end of file diff --git a/playwright-tests/tests/0020_EssentialVehicleLookupBailout.ts b/playwright-tests/tests/0020_EssentialVehicleLookupBailout.ts new file mode 100644 index 00000000..77322774 --- /dev/null +++ b/playwright-tests/tests/0020_EssentialVehicleLookupBailout.ts @@ -0,0 +1,81 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialVehicleLookupBailoutData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + bailoutFlags: { + isVehicleLookupBailout: true + }, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Dublin', + state: 'Ohio', + postalCode: '43016', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2020', + make: 'BMW', + model: '740', + style: '4 door sedan' // TODO: Check correctness of vehicle style + }, + vehicleDamage: [ + // VehicleDamage.WindshieldThreeChips, + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '6826 Sawmill Rd, Columbus, OH 43235', + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialVehicleLookupBailoutTests: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialVehicleLookupBailoutData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0020 Essential Vehicle Lookup Bailout Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Bailout', '@VehicleLookup', '@Essentials'], + testData: data + }, undefined, '0020'); + essentialVehicleLookupBailoutTests.push(tc); +} + +export default essentialVehicleLookupBailoutTests; \ No newline at end of file diff --git a/playwright-tests/tests/0021_EssentialPriceServiceErrorBailout.ts b/playwright-tests/tests/0021_EssentialPriceServiceErrorBailout.ts new file mode 100644 index 00000000..e7d5f69c --- /dev/null +++ b/playwright-tests/tests/0021_EssentialPriceServiceErrorBailout.ts @@ -0,0 +1,82 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; + +const nextWeekday = getNextWeekday(); + +const essentialPriceServiceErrorBailoutData: Partial = { + clientTag: 'ALL_ESSENTIAL', + isDuplicateClaim: false, + isPolicyFound: false, + endorsements: [], + isReplace: false, + partQuestions: undefined, + isSafelite: true, + bailoutFlags: { + isPriceServiceErrorBailout: true + }, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: "itqatest@safelite.com", + phoneNumber: faker.helpers.fromRegExp(/[2-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/), + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'Dublin', + state: 'Ohio', + postalCode: '43016', + country: 'United States' + } + }, + claimDetails: { + policyNumber: faker.string.alphanumeric(5), + policyDeductible: -1, // Not advanced, so we don't care about deductible. + damageDate: '2024-10-10', + damageCause: DamageType.Other + }, + vehicleDetails: { + year: '2015', + make: 'Honda', + model: 'Accord', + style: '4 door sedan', // TODO: Check correctness of vehicle style + vehicleLookupType: VehicleLookupType.Vin, + vin: '1HGCR2E30FA099831' + }, + vehicleDamage: [ + // VehicleDamage.WindshieldThreeChips, + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + // VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + appointmentDate: nextWeekday + } + +} + +const essentialClients = ClientData.getEssentialClients(); +const essentialPriceServiceErrorBailoutTests: TestCase[] = []; +for (const client of essentialClients) { + const data = {...essentialPriceServiceErrorBailoutData}; + data.clientTag = client.clientTag; + data.isAuthenticationRequired = client.clientFlags.isAuthenticationEnabled ?? false + const tc = new TestCase({ + name: `0021 Essential Price Service Error Bailout Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Bailout', '@PriceService', '@Essentials'], + testData: data + }, undefined, '0021'); + essentialPriceServiceErrorBailoutTests.push(tc); +} + +export default essentialPriceServiceErrorBailoutTests; \ No newline at end of file diff --git a/playwright-tests/tests/README.md b/playwright-tests/tests/README.md new file mode 100644 index 00000000..8dbe2dd6 --- /dev/null +++ b/playwright-tests/tests/README.md @@ -0,0 +1,101 @@ +# ISS QA Automation Framework +An automated testing framework for Insurance Self-Service (ISS) application using Playwright with TypeScript. + +# Technology Stack +• Playwright - Core testing framework +• TypeScript - Programming language +• Node.js - Runtime environment +• SauceLabs/Dockers - Cross-browser testing platform + +# Key Features +• Page Object Model implementation +• Data-driven test approach +• Cross-browser testing support +• Parallel test execution +• HTML report generation +• Environment-specific configurations +• SauceLabs integration + +# Test Scenarios +The framework includes various test scenarios covering essential flows like: + +1. Vehicle Not Found Tests (Scenario 10) +2. Do Not See Shop Tests (Scenario 11) +3. Unique Glass Tests (Scenario 12) +4. Replace Tests (Scenario 13) +5. Repair Mobile Tests (Scenario 14) +6. Replace Parts Questions Dropoff Tests (Scenario 15) +7. HP Tests (Scenario 16) + +# Installation +# Install dependencies +npm install + +# Install SauceLabs CLI +npm install saucectl + +# Configuration +• Environment configurations in .env files +• SauceLabs configuration in config.yml +• Playwright configuration in playwright.config.ts + +# Project Structure +. +├── tests/ # Test scenarios and cases +├── pages/ # Page Object Models +├── business-logic/ # Business logic and data models +├── impl/ # Implementation utilities +└── artifacts/ # Test artifacts and results + +# Running Tests +# Run all tests +npx playwright test + +# Run a specific test file +npx playwright test tests/0000__M.test.ts + +# Run tests with specific tag (cognitive approach) +npx playwright test --grep "@smoke" + +# Test Reports +Test results are available in: + +• ortoni report (summerized HTML) +• HTML format (playwright-report) +• JUnit format +• SauceLabs dashboard + +# Key Features +Page Object Model implementation +Data-driven test approach +Cross-browser testing support +Integration with SauceLabs +Parallel test execution +HTML report generation +Environment-specific configurations + +# Common Test Flows +• Vehicle lookup validation +• Shop selection +• Damage assessment +• Appointment scheduling +• Coverage verification +• Payment processing + +# Contributing +1. Follow the established page object pattern +2. Add proper test documentation +3. Include appropriate test tags +4. Ensure tests are isolated and repeatable + +# Environment Variables +BASE_URL= +CCIS_API_URL= +ADMIN_SERVICE_API_URL= +SAUCE_USERNAME= +SAUCE_ACCESS_KEY= + + +# CI/CD Integration +The project uses Azure Pipelines for continuous integration with configurations defined in azure-pipelines.yml. + diff --git a/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts b/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts new file mode 100644 index 00000000..8f138abb --- /dev/null +++ b/playwright-tests/tests/advanced/0001a_ReplaceInShopCredit.ts @@ -0,0 +1,80 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; + +const nextWeekday = getNextWeekday(); +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'PORTLAND', + state: 'OR', + postalCode: '97230-6373', + country: 'United States' + } +} + +const policyNumber = `~AutomatedScenario0001a${faker.string.uuid().substring(0, 6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0001a', customerDetails, policyNumber); + +const advancedScenario0001Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: true, + endorsements: undefined, + vehiclePartQuestions: [ + + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 250, + damageDate: '2023-08-01', + damageCause: DamageType.Vandalism + }, + policySoap: policySoap, + vehicleDetails: { + year: '2020', + make: 'BMW', + model: '740', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultPaypalDetails()//ClientData.getDefaultCreditCardDetails() +} + +// TODO: Add validation for deductible/covered amount +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0001TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = { ...advancedScenario0001Data }; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0001a Advanced Replace Deductible Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0001a'); + advancedScenario0001TestCases.push(tc); +} + +export default advancedScenario0001TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts b/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts new file mode 100644 index 00000000..6ba70ae6 --- /dev/null +++ b/playwright-tests/tests/advanced/0002a_ReplaceOemEndorsement.ts @@ -0,0 +1,115 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; + +const nextWeekday = getNextWeekday(); +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'PLANO', + state: 'TX', + postalCode: '75023', + country: 'United States' + } +} + +const policyNumber = `~AutomatedScenario0002a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0002a', customerDetails, policyNumber); + +const advancedScenario0002Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: false, + hasOemEndorsement: true, + endorsements: undefined, + vehiclePartQuestions: [ + // { + // partQuestionType: PartQuestionType.WindshieldColor, + // isOnPage: true, + // optionToSelect: 'Green Tint, Blue Shade' + // }, + // { + // partQuestionType: PartQuestionType.DriverFrontColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.DriverQuarterColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.DriverRearColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // } + // { + // partQuestionType: PartQuestionType.LeatherSeats, + // isOnPage: true, + // optionToSelect: 'Yes' + // } + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 50, + damageDate: '2023-08-01', + damageCause: DamageType.Vandalism + }, + policySoap: policySoap, + vehicleDetails: { + year: '2006', + make: 'Chrysler', + model: '300', + style: '' + }, + vehicleDamage: [ + // VehicleDamage.WindshieldThreeChips, + VehicleDamage.WindshieldCrack, + // VehicleDamage.DriverFrontDoor, + // VehicleDamage.DriverQuarterPanel, + // VehicleDamage.DriverRearDoor, + // VehicleDamage.PassengerFrontDoor, + // VehicleDamage.PassengerQuarterPanel, + // VehicleDamage.PassengerRearDoor, + //VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.DropOff, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultPaypalDetails() + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0002TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0002Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0002a Advanced Replace Deductible Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0002a'); + advancedScenario0002TestCases.push(tc); +} + +export default advancedScenario0002TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts b/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts new file mode 100644 index 00000000..6e494225 --- /dev/null +++ b/playwright-tests/tests/advanced/0003a_MobileAfterpay.ts @@ -0,0 +1,95 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'PLANO', + state: 'TX', + postalCode: '75023', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0003a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0003a', customerDetails, policyNumber); + +const advancedScenario0003Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: false, + endorsements: undefined, + vehiclePartQuestions: [ + { + partQuestionType: PartQuestionType.WindshieldColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.PassengerRearColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 50, + damageDate: '2016-01-02', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2006', + make: 'Chrysler', + model: '300', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + VehicleDamage.PassengerRearDoor + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + shopAddress: undefined, + serviceAddress: customerAddress, + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultAfterpayDetails() + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0003TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0003Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0003a Advanced Replace Deductible Mobile Afterpay Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0003a'); + advancedScenario0003TestCases.push(tc); +} + +export default advancedScenario0003TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts b/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts new file mode 100644 index 00000000..b64aaac5 --- /dev/null +++ b/playwright-tests/tests/advanced/0004a_NoDeductibleAdas.ts @@ -0,0 +1,83 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; + +const nextWeekday = getNextWeekday(); +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: { + street: faker.location.streetAddress(), + city: 'PALM COAST', + state: 'FL', + postalCode: '32137-8523', + country: 'United States' + } +} + +const policyNumber = `~AutomatedScenario0004a${faker.string.uuid().substring(0, 6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0004a', customerDetails, policyNumber); + +const advancedScenario0004aData: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: false, + endorsements: undefined, + vehiclePartQuestions: [ + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 0, + damageDate: '2022-07-26', + damageCause: faker.helpers.enumValue(DamageType), + }, + policySoap: policySoap, + vehicleDetails: { + year: '2021', + make: 'Subaru', + model: 'Outback', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: { + paymentType: PaymentType.PayAtService + } + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0004aTestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = { ...advancedScenario0004aData }; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0004a Advanced Replace Deductible Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0004a'); + advancedScenario0004aTestCases.push(tc); +} + +export default advancedScenario0004aTestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0005a_CapabilityQuestions.ts b/playwright-tests/tests/advanced/0005a_CapabilityQuestions.ts new file mode 100644 index 00000000..64a1d1c6 --- /dev/null +++ b/playwright-tests/tests/advanced/0005a_CapabilityQuestions.ts @@ -0,0 +1,91 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Somersworth', + state: 'NH', + postalCode: '03878', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0005a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0005a', customerDetails, policyNumber); + +const advancedScenario0005Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: false, + endorsements: undefined, + capabilityQuestions: [ + { + partQuestionType: PartQuestionType.LaneKeepAssist, + isOnPage: true, + optionToSelect: 'Yes' + } + ], + vehiclePartQuestions: undefined, + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 0, + damageDate: '2023-01-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2014', + make: 'Lincoln', + model: 'MKS', + style: '4-door sedan' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + serviceAddress: customerAddress, + appointmentDate: nextWeekday + }, + paymentDetails: { + paymentType: PaymentType.PayAtService + } + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0005TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0005Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0005a Advanced Replace Capability Questions Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0005a'); + advancedScenario0005TestCases.push(tc); +} + +export default advancedScenario0005TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0006a_RepairNoDeductibleMobile.ts b/playwright-tests/tests/advanced/0006a_RepairNoDeductibleMobile.ts new file mode 100644 index 00000000..7491c063 --- /dev/null +++ b/playwright-tests/tests/advanced/0006a_RepairNoDeductibleMobile.ts @@ -0,0 +1,95 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Frankfort', + state: 'KY', + postalCode: '40601', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0006a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0006a', customerDetails, policyNumber); + +const advancedScenario0006Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: false, + endorsements: undefined, + vehiclePartQuestions: [ + // { + // partQuestionType: PartQuestionType.WindshieldColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.PassengerRearColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 0, + damageDate: '2023-08-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2011', + make: 'Toyota', + model: 'Camry', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldOneChip + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + serviceAddress: customerAddress, + appointmentDate: nextWeekday + }, + paymentDetails: { + paymentType: PaymentType.PayAtService + } + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0006TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0006Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0006a Advanced Repair No Deductible Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0006a'); + advancedScenario0006TestCases.push(tc); +} + +export default advancedScenario0006TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0007a_RepairStateLanguage.ts b/playwright-tests/tests/advanced/0007a_RepairStateLanguage.ts new file mode 100644 index 00000000..6abec815 --- /dev/null +++ b/playwright-tests/tests/advanced/0007a_RepairStateLanguage.ts @@ -0,0 +1,95 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Columbia', + state: 'SC', + postalCode: '29205', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0007a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0007a', customerDetails, policyNumber); + +const advancedScenario0007Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: true, + endorsements: undefined, + vehiclePartQuestions: [ + // { + // partQuestionType: PartQuestionType.WindshieldColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.PassengerRearColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 0, + damageDate: '2023-08-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2011', + make: 'Toyota', + model: 'Camry', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldTwoChips + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: { + paymentType: PaymentType.PayAtService + } + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0007TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0007Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0007a Advanced Repair SC State Language Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0007a'); + advancedScenario0007TestCases.push(tc); +} + +export default advancedScenario0007TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0008a_RepairTpa.ts b/playwright-tests/tests/advanced/0008a_RepairTpa.ts new file mode 100644 index 00000000..c5b23efa --- /dev/null +++ b/playwright-tests/tests/advanced/0008a_RepairTpa.ts @@ -0,0 +1,93 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Hamden', + state: 'CT', + postalCode: '06517', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0008a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0008a', customerDetails, policyNumber); + +const advancedScenario0008Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: true, + endorsements: undefined, + vehiclePartQuestions: [ + // { + // partQuestionType: PartQuestionType.WindshieldColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.PassengerRearColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + ], + isSafelite: false, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 0, + damageDate: '2023-08-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2019', + make: 'Volkswagen', + model: 'Golf', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldThreeChips + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: undefined + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0008TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0008Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0008a Advanced Repair TPA Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0008a'); + advancedScenario0008TestCases.push(tc); +} + +export default advancedScenario0008TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0009a_NoDeductibleFlorida.ts b/playwright-tests/tests/advanced/0009a_NoDeductibleFlorida.ts new file mode 100644 index 00000000..103f769c --- /dev/null +++ b/playwright-tests/tests/advanced/0009a_NoDeductibleFlorida.ts @@ -0,0 +1,101 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, EndorsementType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'North Miami Beach', + state: 'FL', + postalCode: '33179', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0009a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0009a', customerDetails, policyNumber); + +const advancedScenario0009Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: false, + endorsements: [ + { + endorsementType: EndorsementType.Educator, + isOnPolicy: true, + isClickYes: false + } + ], + vehiclePartQuestions: [ + // { + // partQuestionType: PartQuestionType.WindshieldColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.PassengerRearColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 0, + damageDate: '2023-09-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2019', + make: 'Toyota', + model: 'C-HR', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack + ], + appointmentDetails: { + serviceLocation: ServiceLocation.DropOff, + serviceAddress: customerAddress, + appointmentDate: nextWeekday + }, + paymentDetails: { + paymentType: PaymentType.PayAtService + } + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0009TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0009Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0009a Advanced Replace No Deductible FL Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0009a'); + advancedScenario0009TestCases.push(tc); +} + +export default advancedScenario0009TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0010a_RearGlass.ts b/playwright-tests/tests/advanced/0010a_RearGlass.ts new file mode 100644 index 00000000..de2fc541 --- /dev/null +++ b/playwright-tests/tests/advanced/0010a_RearGlass.ts @@ -0,0 +1,102 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, EndorsementType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'North Miami Beach', + state: 'FL', + postalCode: '33179', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0010a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0010a', customerDetails, policyNumber); + +const advancedScenario0010Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + hasStateLawPopup: false, + endorsements: [ + { + endorsementType: EndorsementType.Educator, + isOnPolicy: true, + isClickYes: false + } + ], + vehiclePartQuestions: [ + // { + // partQuestionType: PartQuestionType.WindshieldColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.PassengerRearColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 1000, + damageDate: '2023-09-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2019', + make: 'Toyota', + model: 'C-HR', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + VehicleDamage.RearWindow + ], + appointmentDetails: { + serviceLocation: ServiceLocation.DropOff, + serviceAddress: customerAddress, + appointmentDate: nextWeekday + }, + paymentDetails: { + paymentType: PaymentType.PayAtService + } + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0010TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0010Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0010a Advanced Repair No Deductible FL Rear Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0010a'); + advancedScenario0010TestCases.push(tc); +} + +export default advancedScenario0010TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0011a_ItacNoAdas.ts b/playwright-tests/tests/advanced/0011a_ItacNoAdas.ts new file mode 100644 index 00000000..bf008707 --- /dev/null +++ b/playwright-tests/tests/advanced/0011a_ItacNoAdas.ts @@ -0,0 +1,115 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, EndorsementType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Birmingham', + state: 'AL', + postalCode: '35118', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0011a${faker.string.uuid().substring(0, 6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0011a', customerDetails, policyNumber); + +const advancedScenario0011Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + isItac: true, + hasStateLawPopup: false, + endorsements: [ + { + endorsementType: EndorsementType.Educator, + isOnPolicy: true, + isClickYes: false + } + ], + partQuestions: undefined, + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 500, + damageDate: '2023-03-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2006', + make: 'Honda', + model: 'Element', + style: '' + }, + otherVehiclesOnPolicy: [ + { + year: '2006', + make: 'Chevrolet', + model: 'Silverado' + }, + { + year: '2017', + make: 'Jeep', + model: 'Wrangler' + }, + { + year: '2001', + make: 'Toyota', + model: 'Sienna' + }, + { + year: '2005', + make: 'Jeep', + model: 'Wrangler' + }, + { + year: '2022', + make: 'wild', + model: '178bhfkx' + } + ], + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultPaypalDetails() + +} + +// TODO: Add validation for deductible/covered amount +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0011TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = { ...advancedScenario0011Data }; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0011a Advanced ITAC No Adas Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0011a'); + advancedScenario0011TestCases.push(tc); +} + +export default advancedScenario0011TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0012a_ItacDropOff.ts b/playwright-tests/tests/advanced/0012a_ItacDropOff.ts new file mode 100644 index 00000000..3cfe4788 --- /dev/null +++ b/playwright-tests/tests/advanced/0012a_ItacDropOff.ts @@ -0,0 +1,90 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, EndorsementType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Birmingham', + state: 'AL', + postalCode: '35118', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0012a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0012a', customerDetails, policyNumber); + +const advancedScenario0012Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + isItac: true, + hasStateLawPopup: false, + endorsements: [ + { + endorsementType: EndorsementType.Educator, + isOnPolicy: true, + isClickYes: false + } + ], + partQuestions: undefined, + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 500, + damageDate: '2023-09-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2006', + make: 'Honda', + model: 'Element', + style: '' + }, + otherVehiclesOnPolicy: undefined, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + ], + appointmentDetails: { + serviceLocation: ServiceLocation.DropOff, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultAfterpayDetails() + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0012TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0012Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0012a Advanced ITAC Drop-Off Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0012a'); + advancedScenario0012TestCases.push(tc); +} + +export default advancedScenario0012TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0013a_ItacMobile.ts b/playwright-tests/tests/advanced/0013a_ItacMobile.ts new file mode 100644 index 00000000..3e0cf9cc --- /dev/null +++ b/playwright-tests/tests/advanced/0013a_ItacMobile.ts @@ -0,0 +1,90 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, EndorsementType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Birmingham', + state: 'AL', + postalCode: '35118', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0013a${faker.string.uuid().substring(0, 6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0013a', customerDetails, policyNumber); + +const advancedScenario0013Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: false, + isItac: true, + hasStateLawPopup: false, + endorsements: [ + { + endorsementType: EndorsementType.Educator, + isOnPolicy: true, + isClickYes: false + } + ], + vehiclePartQuestions: [ + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 9999, + damageDate: '2023-08-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2006', + make: 'Honda', + model: 'Element', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + serviceAddress: customerAddress, + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultCreditCardDetails() + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0013TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = { ...advancedScenario0013Data }; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0013a Advanced ITAC Mobile Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0013a'); + advancedScenario0013TestCases.push(tc); +} + +export default advancedScenario0013TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0014a_NoCompAdas.ts b/playwright-tests/tests/advanced/0014a_NoCompAdas.ts new file mode 100644 index 00000000..c8a4c414 --- /dev/null +++ b/playwright-tests/tests/advanced/0014a_NoCompAdas.ts @@ -0,0 +1,90 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, EndorsementType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'East York', + state: 'PA', + postalCode: '17402', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0014a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0014a', customerDetails, policyNumber); + +const advancedScenario0014Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: true, + isRecalWarning: true, + hasStateLawPopup: false, + endorsements: [ + { + endorsementType: EndorsementType.Educator, + isOnPolicy: true, + isClickYes: true + } + ], + partQuestions: undefined, + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 9999, + damageDate: '2023-01-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2019', + make: 'Toyota', + model: 'C-HR', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + alternateServiceZip: '43016', + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultAfterpayDetails() + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0014TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0014Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0014a Advanced Replace NoComp Adas Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0014a'); + advancedScenario0014TestCases.push(tc); +} + +export default advancedScenario0014TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0015a_NoCompPartQuestions.ts b/playwright-tests/tests/advanced/0015a_NoCompPartQuestions.ts new file mode 100644 index 00000000..c4108bb4 --- /dev/null +++ b/playwright-tests/tests/advanced/0015a_NoCompPartQuestions.ts @@ -0,0 +1,103 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Montgomery', + state: 'AL', + postalCode: '36116', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0015a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0015a', customerDetails, policyNumber); + +const advancedScenario0015Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: true, + hasStateLawPopup: false, + endorsements: undefined, + partQuestions: [ + { + partQuestionType: PartQuestionType.LeatherSeats, + isOnPage: true, + optionToSelect: 'Yes' + } + ], + vehiclePartQuestions: [ + { + partQuestionType: PartQuestionType.WindshieldColor, + isOnPage: true, + optionToSelect: 'Green Tint, Blue Shade' + }, + { + partQuestionType: PartQuestionType.DriverFrontColor, + isOnPage: true, + optionToSelect: 'Green Tint' + } + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 9999, + damageDate: '2023-08-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2006', + make: 'Subaru', + model: 'Forester', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + VehicleDamage.DriverFrontDoor + ], + appointmentDetails: { + serviceLocation: ServiceLocation.DropOff, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: { + paymentType: PaymentType.PayAtService + } + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0015TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0015Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0015a Advanced Replace NoComp Parts Questions Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0015a'); + advancedScenario0015TestCases.push(tc); +} + +export default advancedScenario0015TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0016a_NoCompAllGlass.ts b/playwright-tests/tests/advanced/0016a_NoCompAllGlass.ts new file mode 100644 index 00000000..8ac8e099 --- /dev/null +++ b/playwright-tests/tests/advanced/0016a_NoCompAllGlass.ts @@ -0,0 +1,120 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Somersworth', + state: 'NH', + postalCode: '03878', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0016a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0016a', customerDetails, policyNumber); + +const advancedScenario0016Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: true, + hasStateLawPopup: false, + endorsements: undefined, + vehiclePartQuestions: [ + { + partQuestionType: PartQuestionType.WindshieldColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.DriverFrontColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.DriverRearColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.DriverVentColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.PassengerFrontColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + { + partQuestionType: PartQuestionType.PassengerRearColor, + isOnPage: true, + optionToSelect: 'Green Tint' + }, + ], + isSafelite: true, + servicePackage: ServicePackage.Standard, + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 9999, + damageDate: '2023-08-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2021', + make: 'Subaru', + model: 'WRX', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldCrack, + VehicleDamage.RearWindow, + VehicleDamage.DriverFrontDoor, + VehicleDamage.DriverRearDoor, + VehicleDamage.DriverVentGlass, + VehicleDamage.PassengerFrontDoor, + VehicleDamage.PassengerRearDoor, + // VehicleDamage.PassengerVentGlass // TODO: Verify if this is intentionally not in UI + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + serviceAddress: customerAddress, + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultPaypalDetails() + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0016TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0016Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0016a Advanced Replace NoComp All Glass Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@Flaky'], + testData: data + }, undefined, '0016a'); + advancedScenario0016TestCases.push(tc); +} + +export default advancedScenario0016TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0017a_NoCompPremium.ts b/playwright-tests/tests/advanced/0017a_NoCompPremium.ts new file mode 100644 index 00000000..6852eb76 --- /dev/null +++ b/playwright-tests/tests/advanced/0017a_NoCompPremium.ts @@ -0,0 +1,96 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Montgomery', + state: 'AL', + postalCode: '36116', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0017a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0017a', customerDetails, policyNumber); + +const advancedScenario0017Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: true, + isSeparateApptsWarning: true, + hasStateLawPopup: false, + endorsements: undefined, + vehiclePartQuestions: [ + // { + // partQuestionType: PartQuestionType.WindshieldColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.PassengerRearColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + ], + isSafelite: true, + servicePackage: ServicePackage.Premium, + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 9999, + damageDate: '2019-11-20', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2006', + make: 'Subaru', + model: 'Forester', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldTwoChips + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: { + paymentType: PaymentType.PayAtService + } + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0017TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0017Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0017a Advanced Repair NoComp Premium Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0017a'); + advancedScenario0017TestCases.push(tc); +} + +export default advancedScenario0017TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0018a_NoCompGlassOnly.ts b/playwright-tests/tests/advanced/0018a_NoCompGlassOnly.ts new file mode 100644 index 00000000..4208b0d9 --- /dev/null +++ b/playwright-tests/tests/advanced/0018a_NoCompGlassOnly.ts @@ -0,0 +1,93 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Hartford', + state: 'CT', + postalCode: '06106', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0018a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0018a', customerDetails, policyNumber); + +const advancedScenario0018Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: true, + hasStateLawPopup: false, + endorsements: undefined, + vehiclePartQuestions: [ + // { + // partQuestionType: PartQuestionType.WindshieldColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.PassengerRearColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + ], + isSafelite: true, + servicePackage: ServicePackage.GlassOnly, + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 9999, + damageDate: '2023-01-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2021', + make: 'Subaru', + model: 'Outback', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldTwoChips + ], + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: undefined, + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultAfterpayDetails() + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0018TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0018Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0018a Advanced Repair No Deductible Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0018a'); + advancedScenario0018TestCases.push(tc); +} + +export default advancedScenario0018TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0019a_NoCompEditVehicle.ts b/playwright-tests/tests/advanced/0019a_NoCompEditVehicle.ts new file mode 100644 index 00000000..5be94a82 --- /dev/null +++ b/playwright-tests/tests/advanced/0019a_NoCompEditVehicle.ts @@ -0,0 +1,95 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails, IVehicleDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Somersworth', + state: 'NH', + postalCode: '03878', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} +const vehicleDetails: IVehicleDetails = { + year: '2021', + make: 'Subaru', + model: 'WRX', + style: '4 door sedan' +}; + +const policyNumber = `~AutomatedScenario0019a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0019a', customerDetails, policyNumber); + +const advancedScenario0019Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: true, + hasStateLawPopup: false, + endorsements: undefined, + vehiclePartQuestions: [ + // { + // partQuestionType: PartQuestionType.WindshieldColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + // { + // partQuestionType: PartQuestionType.PassengerRearColor, + // isOnPage: true, + // optionToSelect: 'Green Tint' + // }, + ], + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 9999, + damageDate: '2023-01-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: vehicleDetails, + editVehicleDetails: vehicleDetails, + vehicleDamage: [ + VehicleDamage.WindshieldThreeChips + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + serviceAddress: customerAddress, + appointmentDate: nextWeekday + }, + paymentDetails: ClientData.getDefaultPaypalDetails() + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0019TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0019Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0019a Advanced Repair No Deductible Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0019a'); + advancedScenario0019TestCases.push(tc); +} + +export default advancedScenario0019TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/advanced/0020a_NoCompChangeLoc.ts b/playwright-tests/tests/advanced/0020a_NoCompChangeLoc.ts new file mode 100644 index 00000000..0d8122e7 --- /dev/null +++ b/playwright-tests/tests/advanced/0020a_NoCompChangeLoc.ts @@ -0,0 +1,92 @@ +import ClientData from "@business-logic/data/ClientData"; +import TestCase from "@business-logic/types/TestCase"; +import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage } from "@business-logic/types/Enums"; +import { ITestData } from "@business-logic/types/ITestData" +import { faker } from "@faker-js/faker"; +import { getNextWeekday } from "@impl/utils/DateUtils"; +import MockPolicyData from "@business-logic/data/MockPolicyData"; +import { ICustomerDetails } from "@business-logic/types/CustomerDetails"; +import { IAddress } from "@business-logic/types/IAddress"; + +const nextWeekday = getNextWeekday(); +const customerAddress: IAddress = { + street: faker.location.streetAddress(), + city: 'Frankfort', + state: 'KY', + postalCode: '40601', + country: 'United States' +} +const customerDetails: ICustomerDetails = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: 'itqatest@safelite.com', + phoneNumber: '614-531-0031', + notes: 'Automated Test', + address: customerAddress +} + +const policyNumber = `~AutomatedScenario0020a${faker.string.uuid().substring(0,6)}`; +const policySoap = MockPolicyData.getPolicySoapByScenario('0020a', customerDetails, policyNumber); + +const advancedScenario0020Data: Partial = { + clientTag: '', + isDuplicateClaim: false, + isPolicyFound: true, + isNoComp: true, + hasStateLawPopup: false, + hasMilitaryWarning: true, + endorsements: undefined, + vehiclePartQuestions: undefined, + isSafelite: true, + servicePackage: faker.helpers.enumValue(ServicePackage), + customerDetails: customerDetails, + claimDetails: { + policyNumber: policyNumber, + policyDeductible: 9999, + damageDate: '2023-08-01', + damageCause: faker.helpers.enumValue(DamageType) + }, + policySoap: policySoap, + vehicleDetails: { + year: '2021', + make: 'Subaru', + model: 'WRX', + style: '' + }, + vehicleDamage: [ + VehicleDamage.WindshieldOneChip + ], + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + serviceAddress: { + street: faker.location.streetAddress(), + city: 'Goose Creek', + state: 'SC', + postalCode: '29445', + country: 'US' + }, + alternateServiceZip: '29445', + appointmentDate: nextWeekday + }, + paymentDetails: { + paymentType: PaymentType.PayAtService + } + +} + +// TODO: Add validation for deductible/covered amount + +const advancedClients = ClientData.getAdvancedClients(); +const advancedScenario0020TestCases: TestCase[] = []; +for (const client of advancedClients) { + const data = {...advancedScenario0020Data}; + data.clientTag = client.clientTag; + const tc = new TestCase({ + name: `0020a Advanced Repair Change Location Client: "${client.accountName}"`, + tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'], + testData: data + }, undefined, '0020a'); + advancedScenario0020TestCases.push(tc); +} + +export default advancedScenario0020TestCases; \ No newline at end of file diff --git a/playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/36116.json b/playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/36116.json new file mode 100644 index 00000000..76da245c --- /dev/null +++ b/playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/36116.json @@ -0,0 +1,8 @@ +{ + "containsMilitaryBase": false, + "isServiceable": true, + "isValid": false, + "state": "AL", + "providerNumber": "00795", + "zipCodeCtu": "01872" +} \ No newline at end of file diff --git a/playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/43016.json b/playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/43016.json new file mode 100644 index 00000000..9ac73a90 --- /dev/null +++ b/playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/43016.json @@ -0,0 +1,8 @@ +{ + "containsMilitaryBase": false, + "isServiceable": true, + "isValid": false, + "state": "OH", + "providerNumber": "03357", + "zipCodeCtu": "01820" +} \ No newline at end of file diff --git a/playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/75023.json b/playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/75023.json new file mode 100644 index 00000000..4ee5134f --- /dev/null +++ b/playwright-tests/tests/mockResponses/common/location/api/v1/location/zip/75023.json @@ -0,0 +1,8 @@ +{ + "containsMilitaryBase": false, + "isServiceable": true, + "isValid": true, + "state": "TX", + "providerNumber": "01813", + "zipCodeCtu": "01813" +} \ No newline at end of file diff --git a/playwright-tests/tests/mockResponses/mockResponsesConfig.json b/playwright-tests/tests/mockResponses/mockResponsesConfig.json new file mode 100644 index 00000000..590e2d88 --- /dev/null +++ b/playwright-tests/tests/mockResponses/mockResponsesConfig.json @@ -0,0 +1,12 @@ +{ + "common": { + "location/api/v1/location/zip/36116": "common/location/api/v1/location/zip/36116.json", + "location/api/v1/location/zip/43016": "common/location/api/v1/location/zip/43016.json" + }, + "scenario1": { + "coverage/api/v1/coverage/policy-information": "scenario1/coverage/api/v1/coverage/policy-information.json" + }, + "scenario2": { + "location/api/v1/location/zip/36116": "scenario2/coverage/api/v1/coverage/policy-information.json" + } +} \ No newline at end of file diff --git a/playwright-tests/tests/mockResponses/scenario1/coverage/api/v1/coverage/policy-information.json b/playwright-tests/tests/mockResponses/scenario1/coverage/api/v1/coverage/policy-information.json new file mode 100644 index 00000000..f9a85a92 --- /dev/null +++ b/playwright-tests/tests/mockResponses/scenario1/coverage/api/v1/coverage/policy-information.json @@ -0,0 +1,57 @@ +{ + "policies": [ + { + "policyEffectiveDate": "0001-01-01T00:00:00", + "expirationDate": "0001-01-01T00:00:00", + "type": null, + "lineOfBusiness": null, + "policyNumber": "AOA23102736040", + "status": null, + "source": null, + "insureds": [ + { + "firstName": "JOHN", + "lastName": "SMITH", + "businessName": null, + "address": "4372 SUNSHINE DR", + "city": "MONTGOMERY", + "state": "AL", + "zipCode": "36116", + "phones": null, + "email": null, + "driverLicenseState": null, + "relationToInsured": null, + "companyCode": "LIBERTY", + "customerId": null + } + ], + "vehicles": [ + { + "id": 0, + "vehicleYear": "2006", + "vehicleMake": "SBRU", + "vehicleModel": "FORESTER", + "vehicleStyle": null, + "licensePlate": "UNKNOWN", + "vin": "JF1SG63616B121212", + "driver": null, + "owner": null, + "coverages": [], + "fleetNumber": null, + "fleetUnitNumber": "1", + "endorsements": null + } + ], + "taxExempt": "FALSE", + "policyData": "G+r99boRzfUExiJdE0IYp5w1bhP8AZ31Qy+HnTxiQ8sf9paDgwprrcJJ8b8Sd3khpFBwgGwA0ZDgZmLNcmlOiSRchEvJfrZ47XoMTYq4cRsYjrMqvGVKl2ihGDY5brfMc11Hj6h/iJyDyUTAF4CgkDGhtRTHRcaDnjNQtC3G5Mge5LnZwF1HZs6AI4iFm43O6ZFC1fviI00LoQ1JAiXRcDBDng9OFTkeHoyAiVbxyaUqT07sEKl7jHKXJGYletP2r+yMKdMPoN2fo48quSvoQA==" + } + ], + "referralCorrelationId": "6a4e2176-b877-481a-ab13-01653ee4f224", + "referralNumber": null, + "accountNumber": "550036", + "isSuccess": true, + "isError": false, + "errorCode": null, + "errorMessage": null, + "successMessage": null +} \ No newline at end of file diff --git a/playwright-tests/tests/mockResponses/scenario2/coverage/api/vi/coverage/policy-information.json b/playwright-tests/tests/mockResponses/scenario2/coverage/api/vi/coverage/policy-information.json new file mode 100644 index 00000000..bd541df0 --- /dev/null +++ b/playwright-tests/tests/mockResponses/scenario2/coverage/api/vi/coverage/policy-information.json @@ -0,0 +1,82 @@ +{ + "policies": [ + { + "policyEffectiveDate": "0001-01-01T00:00:00", + "expirationDate": "0001-01-01T00:00:00", + "type": null, + "lineOfBusiness": null, + "policyNumber": "~550036ENDRSOEM", + "status": null, + "source": null, + "insureds": [ + { + "firstName": "LERNO", + "lastName": "REEB", + "businessName": null, + "address": "2817 BENGAL LN", + "city": "PLANO", + "state": "TX", + "zipCode": "75023", + "phones": null, + "email": null, + "driverLicenseState": null, + "relationToInsured": null, + "companyCode": "LIBERTY", + "customerId": null + }, + { + "firstName": "ISAAC", + "lastName": "ASIMOV", + "businessName": null, + "address": "2817 BENGAL LN", + "city": "PLANO", + "state": "TX", + "zipCode": "75023", + "phones": null, + "email": null, + "driverLicenseState": null, + "relationToInsured": null, + "companyCode": "LIBERTY", + "customerId": null + } + ], + "vehicles": [ + { + "id": 0, + "vehicleYear": "2006", + "vehicleMake": "CHRY", + "vehicleModel": "300", + "vehicleStyle": null, + "licensePlate": "UNKNOWN", + "vin": "2C3KA53G06H407823", + "driver": null, + "owner": null, + "coverages": [ + { + "code": "COMP", + "deductible": 50, + "individualLimit": 0, + "occurrenceLimit": 0, + "dayLimit": 0 + } + ], + "fleetNumber": null, + "fleetUnitNumber": "1", + "endorsements": [ + "OEM Approved" + ] + } + ], + "taxExempt": "FALSE", + "policyData": "G+r99boRzfUExiJdE0IYp/EeRyybEAPInOh4NEX8UesSGLZvquPGXqn9HRn/4zge7aj+HhRN6CkN21JKCMqw4cPrSHhJzQpb83TQMsj71r2fmpx7/2mxZskTaSu0UG48ye+7qQki7a3xTkE9V3aw8+xFLhEm3P6gkExFSRefeCBPH3+n//ypF0ZsawSni4wPyzpZ5KOSyTX4kDPPJUS3Ry2drjHpT/oDKm6Iro0WBaqj8mf7758UlaW1OsxO3JHZc2fi2+vnYB+nVjbDN0DnWZOayLLmjUKyHf09kp2gDCEc39Mb1mweI9OOl8GtW91078A7wnCEWrTITvc1+VLCN9n7mwf6WJ8m47qw5oBxv5s=" + } + ], + "referralCorrelationId": "3fd7b485-569a-4b4b-8d90-7b7bb49dbb01", + "referralNumber": null, + "accountNumber": "550036", + "isSuccess": true, + "isError": false, + "errorCode": null, + "errorMessage": null, + "successMessage": null +} \ No newline at end of file diff --git a/playwright-tests/tsconfig.json b/playwright-tests/tsconfig.json new file mode 100644 index 00000000..069ee928 --- /dev/null +++ b/playwright-tests/tsconfig.json @@ -0,0 +1,62 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "strictNullChecks": true, + "noImplicitAny": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "rootDirs": [ + "./impl", + "./business-logic" + ], + "paths": { + "@impl/*": [ + "./impl/*" + ], + "@api/*": [ + "./impl/api/*" + ], + "@controller/*": [ + "./impl/api/controller/*" + ], + "@model/*": [ + "./impl/api/model/*" + ], + "@gui/*": [ + "./impl/gui/*" + ], + "@lfm/*": [ + "./impl/gui/lfm/*" + ], + "@pom/*": [ + "./impl/gui/pom/*" + ], + "@mixins/*": [ + "./impl/gui/mixins" + ], + "@business-logic/*": [ + "./business-logic/*" + ], + "@validations/*": [ + "./business-logic/validations/*" + ], + "@workflows/*": [ + "./business-logic/workflows/*" + ], + "@helpers/*": [ + "./helpers/*" + ], + "@tests/*": [ + "./tests/*" + ], + "@utils/*": [ + "./impl/utils/*" + ], + } + } +} \ No newline at end of file