diff --git a/package-lock.json b/package-lock.json index 00fc7673a..2b541b385 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "0.1.0", "dependencies": { "@iframe-resizer/child": "^5.3.3", - "axios": "^0.23.0", "bootstrap": "^5.3.3", "core-js": "^3.38.1", "http-status-codes": "^2.3.0", @@ -38,6 +37,9 @@ "@vue/eslint-config-prettier": "^9.0.0", "@vue/test-utils": "^2.4.6", "@vue/vue3-jest": "^27.0.0", + "axios": "^1.8.4", + "axios-mock-adapter": "^2.1.0", + "axios-retry": "^4.5.0", "dotenv-safe": "^9.1.0", "eslint": "8.57", "eslint-plugin-prettier": "^5.2.1", @@ -5412,12 +5414,82 @@ } }, "node_modules/axios": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.23.0.tgz", - "integrity": "sha512-NmvAE4i0YAv5cKq8zlDoPd1VLKAqX5oLuZKs8xkJa4qi6RGn0uhCYFjWtHHC9EM/MwOwYWOs53W+V0aqEXq1sg==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "dev": true, "license": "MIT", "dependencies": { - "follow-redirects": "^1.14.4" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios-mock-adapter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-2.1.0.tgz", + "integrity": "sha512-AZUe4OjECGCNNssH8SOdtneiQELsqTsat3SQQCWLPjN436/H+L9AjWfV7bF+Zg/YL9cgbhrz5671hoh+Tbn98w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "is-buffer": "^2.0.5" + }, + "peerDependencies": { + "axios": ">= 0.17.0" + } + }, + "node_modules/axios-mock-adapter/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "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", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios-retry": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz", + "integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "is-retry-allowed": "^2.2.0" + }, + "peerDependencies": { + "axios": "0.x || 1.x" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, "node_modules/babel-jest": { @@ -8005,6 +8077,22 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -9227,6 +9315,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", @@ -9694,6 +9783,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "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", @@ -10509,6 +10614,19 @@ "dev": true, "license": "MIT" }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -16247,6 +16365,13 @@ "node": ">= 0.10" } }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", diff --git a/package.json b/package.json index c63d8b5e8..fb65152d5 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,8 @@ "prettier": "^3.3.3", "sass": "^1.77.8", "sass-loader": "^8.0.2", + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7", "typescript": "^4.9.5" }, "eslintConfig": { diff --git a/playwright-tests/.env.dev b/playwright-tests/.env.dev index 0330919bf..c89292674 100644 --- a/playwright-tests/.env.dev +++ b/playwright-tests/.env.dev @@ -4,9 +4,13 @@ # Environment type NODE_ENV="qa" +SKIP_CONTENT_SITE=false + # Base URLs by environment (uncomment the one you need) # qa BASE_URL="https://www-qa2.safelite.com/" +# qa with skipToInsurance Turned Off +# BASE_URL="https://fixmyglassqa.safelite.com/?cns=all&experiments=ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_VinAndEmailOptional=true,NextGen_IGQSkipToInsurance=NextGen_IGQSkipToInsurance_V1=NextGen_IGQSkipToInsurance_CONTROL=true" # sys # BASE_URL="https://www-test2.safelite.com/fmg/?fmgPage=vehicle" # dev diff --git a/playwright-tests/azure-pipelines-automated-testing.yml b/playwright-tests/azure-pipelines-automated-testing.yml index b4d7da831..ac332ef44 100644 --- a/playwright-tests/azure-pipelines-automated-testing.yml +++ b/playwright-tests/azure-pipelines-automated-testing.yml @@ -1,11 +1,3 @@ -schedules: -- cron: 0 9 * * MON-FRI - always: true - displayName: Daily Test Automation Run for FMG-NextGen - branches: - include: - - main - pool: 'AmazonLinuxPool' variables: @@ -50,10 +42,10 @@ stages: container_id=$(docker create \ --ipc=host \ -e CCIS_API_AUTH=$(CCIS_API_AUTH) \ - -e BASE_URL=$(BASE_URL) \ + -e BASE_URL="$(BASE_URL)" \ -e CCIS_API_URL=$(CCIS_API_URL) \ -e ADMIN_SERVICE_API_URL=$(ADMIN_SERVICE_API_URL) \ - -e SHARD=$(shardNumber) \ + -e SKIP_CONTENT_SITE=$(SKIP_CONTENT_SITE) \ -e CI=true \ -e NODE_ENV=$(NODE_ENV) \ $(dockerImageName):$(imageTag) \ @@ -131,6 +123,7 @@ stages: -e REPORT_NAME="$report_name" \ -e CURRENT_DAY="$current_day" \ -e NUMERIC_DATE="$numeric_date" \ + -e NODE_ENV=$(NODE_ENV) \ $(dockerImageName):$(imageTag) \ bash -c "chmod +x devops/scripts/jira_writeback.sh echo \"Moving Playwright reports out of subfolders...\" @@ -151,7 +144,7 @@ stages: fi echo \"Writing report to Jira card '$jira_card_number'...\" - /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/\${REPORT_NAME} \"AUTOMATED TEST RUN: $(date) - \${CURRENT_DAY} (\${NUMERIC_DATE})\" ") + /app/devops/scripts/jira_writeback.sh add_comment \"$jira_card_number\" /app/ortoni-report/\${REPORT_NAME} \"AUTOMATED TEST RUN: $(date) - \${CURRENT_DAY} (\${NUMERIC_DATE}) - Environment: \${NODE_ENV}\" ") # Start container and stream logs echo "Starting merge" diff --git a/playwright-tests/business-logic/constants/DefaultTestData.ts b/playwright-tests/business-logic/constants/DefaultTestData.ts index 6efb2c1db..197297b4b 100644 --- a/playwright-tests/business-logic/constants/DefaultTestData.ts +++ b/playwright-tests/business-logic/constants/DefaultTestData.ts @@ -43,7 +43,7 @@ export function getCustomerDetails(): ICustomerDetails { street: faker.location.streetAddress(), city: 'Columbus', state: 'Ohio', - postalCode: '43215', + postalCode: '21237', country: 'United States' } }; diff --git a/playwright-tests/pages/ServicePackagesPage.ts b/playwright-tests/pages/ServicePackagesPage.ts index 89f085d3b..3f8bb0d68 100644 --- a/playwright-tests/pages/ServicePackagesPage.ts +++ b/playwright-tests/pages/ServicePackagesPage.ts @@ -78,6 +78,7 @@ export class ServicePackagesPage extends BasePage { if (await this.repeatedClicksModalCloseButton.isVisible()) { await this.repeatedClicksModalCloseButton.click(); } + await this.page.waitForTimeout(3000); await this.closeButton.click(); } else { diff --git a/playwright-tests/tests/0000__M.test.ts b/playwright-tests/tests/0000__M.test.ts index 0c1abb757..d3022f67c 100644 --- a/playwright-tests/tests/0000__M.test.ts +++ b/playwright-tests/tests/0000__M.test.ts @@ -168,8 +168,9 @@ async function runWorkflow(page: Page, testCase: TestCase) { //============================= TEST WORKFLOW STEPS ============================= - // Execute home page for qa and dev environments (skip for sys) - if (process.env.SKIP_CONTENT_SITE == "false") { + // Use Environment Variable to decide whether or not we want to skip content site aka home page + + if (process.env.SKIP_CONTENT_SITE == "false") { await test.step('HomePage >> Lets Get Started', async () => { let homePage = testCase.pages.homePage; console.log(`Customer for this test: ${customerDetails?.firstName} ${customerDetails?.lastName}`); diff --git a/src/assets/img/icons/calendar-icon-black.svg b/src/assets/img/icons/calendar-icon-black.svg new file mode 100644 index 000000000..9c66862ac --- /dev/null +++ b/src/assets/img/icons/calendar-icon-black.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/img/icons/calendar-icon-grey.svg b/src/assets/img/icons/calendar-icon-grey.svg new file mode 100644 index 000000000..5a67d0eae --- /dev/null +++ b/src/assets/img/icons/calendar-icon-grey.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/img/icons/info-circle.svg b/src/assets/img/icons/info-circle.svg new file mode 100644 index 000000000..7f176afd2 --- /dev/null +++ b/src/assets/img/icons/info-circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/img/icons/payment-icon-green.svg b/src/assets/img/icons/payment-icon-green.svg new file mode 100644 index 000000000..931226bee --- /dev/null +++ b/src/assets/img/icons/payment-icon-green.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/img/icons/payment-icon-grey.svg b/src/assets/img/icons/payment-icon-grey.svg new file mode 100644 index 000000000..bef98d2de --- /dev/null +++ b/src/assets/img/icons/payment-icon-grey.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/constants/progress-bar-mapper.js b/src/constants/progress-bar-mapper.js new file mode 100644 index 000000000..98ae07992 --- /dev/null +++ b/src/constants/progress-bar-mapper.js @@ -0,0 +1,26 @@ +const pagePercentageMapper = { + vehicle: 4, + "vehicle-damage": 16, + estimate: 28, + "service-zip": 32, + "vin-lookup": 32, + "license-plate-lookup": 32, + "address-lookup": 32, + "address-vehicles": 36, + "part-questions": 40, + "molding-questions": 40, + "vehicle-parts": 40, + "capability-questions": 40, + quote: 48, + "insurance-company": 52, + "service-location": 60, + schedule: 72, + "customer-details": 84, + "payment-method": 92, + payment: 96, + confirmation: 100, +}; + +export const getProgressBarPercentage = (page) => { + return pagePercentageMapper[page] || 0; +}; diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index 71c09bbbf..0d0ef066d 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -1,5 +1,7 @@ @@ -60,8 +60,9 @@ export default { flex-direction: column; width: 100%; background: transparent; - align-items: end; + align-items: center; margin-top: auto; + padding: 0 0 2rem 0; .skyline { height: 96px; @@ -70,30 +71,34 @@ export default { height: 48px; } + p { + font-size: 0.875rem; + margin-top: 0.25rem; + @include media-breakpoint-up(md) { + font-size: 1rem; + margin-top: 0.5rem; + } + } + .footer-inner-wrapper { display: flex; flex-direction: column; border-top: 1px solid $gray; - padding: 2rem 0; + padding: 2rem 0 0 0; width: 100%; justify-content: center; align-items: center; - - p { - font-size: 0.875rem; - color: $black; - @include media-breakpoint-up(md) { - font-size: 1rem; - } - } + color: $gray-600; :deep(a) { text-decoration: none; &.new-window-link { margin: 0 0.75rem; + color: $gray-600; } &.navigation-link { margin: 0 0.75rem; + color: $gray-600; } } diff --git a/src/fmg-components/funnel-header/funnel-header.vue b/src/fmg-components/funnel-header/funnel-header.vue index 4862b5205..716ce29d9 100644 --- a/src/fmg-components/funnel-header/funnel-header.vue +++ b/src/fmg-components/funnel-header/funnel-header.vue @@ -7,6 +7,7 @@ + - 0; }, + getCarIdfromStore() { + return store.getters.vehicle.carId; + }, + getIsVehicleHeavyTruckServiceableFromStore() { + return store.getters.vehicle.isBigTruck && store.getters.vehicle.canSafeliteService; + }, + async findClosestApplicableShops() { + return await this.dispatchStoreActionWithLogging( + storeActions.GET_CLOSEST_APPLICABLE_SHOPS, + { zip: this.serviceZipCode, carId: this.carId }, + "service-zip" + ); + }, + async backButtonAction() { const skipVin = await skipVinLookup(); // route to move backwards @@ -252,6 +268,26 @@ export default { return this.$refs.navbar.removeLoader(); } this.displayNonServiceableZipAlert = false; + if (this.isHeavyTruck) { + const closestShops = await this.findClosestApplicableShops( + this.serviceZipCode, + this.carId + ); + this.displayNoServiceAlert = !closestShops?.data?.providers?.length; + if (this.displayNoServiceAlert) { + return this.$refs.navbar.removeLoader(); + } + } + if (this.isHeavyTruck) { + const closestShops = await this.findClosestApplicableShops( + this.serviceZipCode, + this.carId + ); + this.displayNoServiceAlert = !closestShops?.data?.providers?.length; + if (this.displayNoServiceAlert) { + return this.$refs.navbar.removeLoader(); + } + } const payment = this.$store.getters.payment; const policy = this.$store.getters.policy; @@ -309,6 +345,8 @@ export default { watch: { serviceZipCode() { this.displayNonServiceableZipAlert = false; + this.displayNoServiceAlert = false; + this.displayNoServiceAlert = false; }, }, components: { diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index 4c6897717..1b57ee068 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -197,6 +197,8 @@ export default { vin: this.getVinFromStore(), serviceZipCode: this.getZipFromStore() ?? this.$route.query.zipcode, emailOrSms: this.getEmailOrSmsFromStore(), + carId: this.getCarIdfromStore(), + isHeavyTruck: this.getIsVehicleHeavyTruckServiceableFromStore(), isCarIdDifferent: false, customAlertData: {}, previouslyEnteredCarId: "", @@ -245,6 +247,16 @@ export default { ); } }, + getIsVehicleHeavyTruckServiceableFromStore() { + return store.getters.vehicle.isBigTruck && store.getters.vehicle.canSafeliteService; + }, + async findClosestApplicableShops() { + return await this.dispatchStoreActionWithLogging( + storeActions.GET_CLOSEST_APPLICABLE_SHOPS, + { zip: this.serviceZipCode, carId: this.carId }, + "vin-lookup" + ); + }, async forwardButtonAction() { this.resetAlerts(); @@ -328,6 +340,18 @@ export default { }, false ); + if (this.isHeavyTruck) { + closestShops = await this.findClosestApplicableShops( + this.serviceZipCode, + this.carId + ); + this.displayNoServiceAlert = !closestShops?.data?.providers?.length; + if (this.displayNoServiceAlert) { + return this.$refs.navbar.removeLoader(); + } + } + + // if no value due to field being optional, blank both phone and email address // if no value due to field being optional, blank both phone and email address if (!this.emailOrSms) { @@ -392,7 +416,7 @@ export default { false ); } - + // if no value due to field being optional, blank both phone and email address if (!this.emailOrSms) { await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false); await this.dispatchStoreAction(storeActions.SAVE_EMAIL, "", false); @@ -474,6 +498,8 @@ export default { this.displayInvalidZipAlert = false; this.displayVinNotFoundAlert = false; this.displayVinScanFailedAlert = false; + this.displayNoServiceAlert = false; + this.displayNoServiceAlert = false; }, }, mounted() {