diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2cb18429c..3867fdcd6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,9 +18,11 @@ pr: resources: containers: - container: node - image: packagerepository.sagaws.net:8082/safelite/node-build:18 + image: ghcr.io/safelite/node-build:18 + endpoint: GitHub Docker - container: awscli - image: packagerepository.sagaws.net:8082/safelite/awscli2-build:3.9 + image: ghcr.io/safelite/awscli2-build:3.9 + endpoint: GitHub Docker - container: prettier-node image: ghcr.io/safelite/prettier-node-build:23 endpoint: GitHub Docker diff --git a/jest.config.js b/jest.config.js index e534b3e3f..70916274f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,6 +4,9 @@ module.exports = { testResultsProcessor: "jest-junit", preset: "@vue/cli-plugin-unit-jest", transform: { "^.+\\.vue$": "@vue/vue3-jest" }, + transformIgnorePatterns: [ + '/node_modules/(?!.*\\.mjs$)', + ], moduleFileExtensions: ["js", "vue"], modulePathIgnorePatterns: ["vin-lookup"], collectCoverageFrom: [ diff --git a/src/digital-components/salesforce-webchat/salesforce-webchat.spec.js b/src/digital-components/salesforce-webchat/salesforce-webchat.spec.js index 178e431cc..40fc752fd 100644 --- a/src/digital-components/salesforce-webchat/salesforce-webchat.spec.js +++ b/src/digital-components/salesforce-webchat/salesforce-webchat.spec.js @@ -3,6 +3,7 @@ import salesforceWebchat from "./salesforce-webchat.vue"; import * as devHelper from "./salesforce-helper-dev"; import * as qaHelper from "./salesforce-helper-qa"; import * as prodHelper from "./salesforce-helper-prod"; +import { applicationConfig } from "../../constants/application-config"; describe("salesforceWebchat.vue", () => { it("renders correctly", () => { @@ -21,19 +22,19 @@ describe("salesforceWebchat.vue", () => { }); it("calls the correct initialization function based on environment", () => { - process.env.VUE_APP_CURRENT_ENVIRONMENT = "Prod"; + applicationConfig.CURRENT_ENVIRONMENT = "Prod"; let wrapper = shallowMount(salesforceWebchat); mockProd = jest.spyOn(prodHelper, "initializeSalesforceWebchatForProd"); wrapper.vm.initializeSalesforceWebchat(); expect(mockProd).toHaveBeenCalled(); - process.env.VUE_APP_CURRENT_ENVIRONMENT = "QA"; + applicationConfig.CURRENT_ENVIRONMENT = "QA"; wrapper = shallowMount(salesforceWebchat); mockQa = jest.spyOn(qaHelper, "initializeSalesforceWebchatForQa"); wrapper.vm.initializeSalesforceWebchat(); expect(mockQa).toHaveBeenCalled(); - process.env.VUE_APP_CURRENT_ENVIRONMENT = "Dev"; + applicationConfig.CURRENT_ENVIRONMENT = "Dev"; wrapper = shallowMount(salesforceWebchat); mockDev = jest.spyOn(devHelper, "initializeSalesforceWebchatForDev"); wrapper.vm.initializeSalesforceWebchat(); diff --git a/src/digital-components/salesforce-webchat/salesforce-webchat.vue b/src/digital-components/salesforce-webchat/salesforce-webchat.vue index 75f689d10..9283bda62 100644 --- a/src/digital-components/salesforce-webchat/salesforce-webchat.vue +++ b/src/digital-components/salesforce-webchat/salesforce-webchat.vue @@ -7,6 +7,7 @@ import { initializeSalesforceWebchatForDev } from "./salesforce-helper-dev"; import { initializeSalesforceWebchatForQa } from "./salesforce-helper-qa"; import { initializeSalesforceWebchatForProd } from "./salesforce-helper-prod"; +import { applicationConfig } from "@/constants/application-config"; export default { name: "salesforceWebchat", @@ -28,7 +29,7 @@ export default { }, methods: { initializeSalesforceWebchat() { - switch (process.env.VUE_APP_CURRENT_ENVIRONMENT) { + switch (applicationConfig.CURRENT_ENVIRONMENT) { case "Prod": initializeSalesforceWebchatForProd(); break; diff --git a/src/layouts/address-lookup/address-lookup.spec.js b/src/layouts/address-lookup/address-lookup.spec.js index 677c14069..87486280f 100644 --- a/src/layouts/address-lookup/address-lookup.spec.js +++ b/src/layouts/address-lookup/address-lookup.spec.js @@ -182,6 +182,23 @@ describe("address-lookup.vue", () => { // Assert expect(wrapper.findComponent({ ref: "alertVinNotFound" }).isVisible()).toBe(true); }); + + test("should hide the AlertNoService when displayNoServiceAlert is false", async () => { + const { wrapper } = setupMocks({ + isZipServiceable: true, + displayNoServiceAlert: false, + lookupVinbyAddressResponse: { + isStatePermissible: true, + vinVehicles: [], // Return no vehicles + }, + }); + // Ensure displayNoServiceAlert is false + await wrapper.setData({ displayNoServiceAlert: false }); + await wrapper.vm.forwardButtonAction(); + // Check if the AlertNoService component is not rendered + const alertNoService = wrapper.findComponent({ ref: "AlertNoService" }); + expect(alertNoService.exists()).toBe(false); + }); }); describe("navigation", () => { @@ -741,6 +758,17 @@ function setupMocks({ wrapper.vm.setCmsContent = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.removeLoader = jest.fn(); + const closestShops = { + data: { + providers: [ + { id: 1, name: "Shop 1" }, + { id: 2, name: "Shop 2" }, + ], + }, + }; + wrapper.vm.findClosestApplicableShops = jest.fn().mockImplementation(() => { + return new Promise((resolve) => resolve(closestShops)); + }); return { wrapper }; } diff --git a/src/layouts/address-lookup/address-lookup.vue b/src/layouts/address-lookup/address-lookup.vue index ba2f8f6dd..d0cca48d9 100644 --- a/src/layouts/address-lookup/address-lookup.vue +++ b/src/layouts/address-lookup/address-lookup.vue @@ -81,13 +81,20 @@ + + :isForwardActionDisabled="!meta.valid || displayNoServiceAlert" /> @@ -177,6 +184,7 @@ export default { displayInvalidZipAlert: false, showServiceZipField: this.getServiceZipFromStore(), isZipServiceable: false, + displayNoServiceAlert: false, }; }, methods: { @@ -293,6 +301,17 @@ export default { return this.$refs.navbar.removeLoader(); } this.displayInvalidZipAlert = false; + + if (this.serviceZipCode != null && 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 carsFound = resultMap.vinLookupResponse.vinVehicles; // Handle cases for different amounts of VINS found for the address. @@ -444,6 +463,7 @@ export default { this.displayNonServiceableZipAlert = false; this.displayMatchedDifferentVehicleAlert = false; this.displayVinLookupByHomeAddressNotAllowedAlert = false; + this.displayNoServiceAlert = false; }, }, mounted() { @@ -495,6 +515,7 @@ export default { handler(newValue) { // If they modify the service zip code, then hide the error message. this.displayNonServiceableZipAlert = false; + this.displayNoServiceAlert = false; }, }, showServiceZipField: { diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.spec.js b/src/layouts/license-plate-lookup/license-plate-lookup.spec.js index 14f0408d1..a1b768fb3 100644 --- a/src/layouts/license-plate-lookup/license-plate-lookup.spec.js +++ b/src/layouts/license-plate-lookup/license-plate-lookup.spec.js @@ -620,6 +620,8 @@ function setupMocks({ validateZipResponse = { zipCodeCtu: null, isServiceable: false, isValid: true, state: null }, //Values to set in the state store carId = null, + isHeavyTruck = true, + displayNoServiceAlert = true, serviceLocationZipCode = null, registrationZipCode = null, licensePlate = null, //"TESTPLATE", @@ -672,6 +674,8 @@ function setupMocks({ zipCode: registrationZipCode, }, carId: carId, + isBigTruck: isHeavyTruck, + canSafeliteService: true, }, order: { customer: { @@ -716,5 +720,17 @@ function setupMocks({ wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); wrapper.vm.$refs.navbar.updateButtonText = jest.fn(); wrapper.vm.$refs.navbar.removeLoader = jest.fn(); + const closestShops = { + data: { + providers: [ + { id: 1, name: "Shop 1" }, + { id: 2, name: "Shop 2" }, + ], + }, + }; + + wrapper.vm.findClosestApplicableShops = jest.fn().mockImplementation(() => { + return new Promise((resolve) => resolve(closestShops)); + }); return { wrapper, apiPromise }; } diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.vue b/src/layouts/license-plate-lookup/license-plate-lookup.vue index 8736df956..223ca9ee7 100644 --- a/src/layouts/license-plate-lookup/license-plate-lookup.vue +++ b/src/layouts/license-plate-lookup/license-plate-lookup.vue @@ -76,6 +76,7 @@ :manualCopy="AlertMatchedDifferentVehicleBody" alertClass="alert-warning" v-bind:isDismissible="false" /> + + @ForwardClicked="forwardButtonAction" /> @@ -177,6 +178,7 @@ export default { displayInvalidZipAlert: false, showServiceZipField: this.getServiceZipFromStore(), isZipServiceable: false, + displayNoServiceAlert: false, }; }, methods: { @@ -279,6 +281,17 @@ export default { this.displayInvalidZipAlert = true; 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(); + } + } // Check if the CarId has changed. this.isCarIdDifferent = vinLookup.data.vehicle.carId !== this.$store.getters.vehicle.carId; @@ -393,6 +406,7 @@ export default { this.displayMatchedDifferentVehicleAlert = false; this.displayVinLookupByHomeAddressNotAllowedAlert = false; this.displayInvalidZipAlert = false; + this.displayNoServiceAlert = false; }, }, mounted() { @@ -437,6 +451,7 @@ export default { serviceZipCode() { // If they modify the service zip code, then hide the error message. this.displayNonServiceableZipAlert = false; + this.displayNoServiceAlert = false; this.$refs.navbar.updateButtonText( this.getCmsContent("FunnelFooterWidget", "ForwardButtonText") ); diff --git a/src/layouts/payment-method/afterpay-breakout/afterpay-breakout.vue b/src/layouts/payment-method/afterpay-breakout/afterpay-breakout.vue index 06a717289..0e1e3d58a 100644 --- a/src/layouts/payment-method/afterpay-breakout/afterpay-breakout.vue +++ b/src/layouts/payment-method/afterpay-breakout/afterpay-breakout.vue @@ -3,18 +3,16 @@
-   {{ this.afterpayPrice }}
-   - -   + + data-bind="click:afterpayLearnMore" + class="afterpay-learn-more"> Info Icon
@@ -151,8 +149,8 @@ export default { &.alert-info { .alert-heading { - color: #000000; - font-weight: 600; + color: $black; + font-weight: $font-weight-600; margin-left: 0.5rem; margin-right: 0.5rem; @include media-breakpoint-up(md) { @@ -162,11 +160,12 @@ export default { } } .afterpay-amount { - color: #0c7e47; + color: $green; + margin-left: 0.25rem; } .alert-sub-heading { - color: #4b4c4e; - font-weight: 400; + color: $gray-1100; + font-weight: $font-weight-normal; } .after-pay { .after-pay-toggle { @@ -180,7 +179,7 @@ export default { background-position: right center; margin-left: 0.5rem; width: 1rem; - height: 9px; + height: 0.5625rem; display: inline-flex; } &.expanded:after { @@ -194,8 +193,8 @@ export default { } } .after-pay-toggle a { - font-weight: 600; - color: #0070d1; + font-weight: $font-weight-600; + color: $blue; text-decoration: none; } .after-pay-details { @@ -218,27 +217,45 @@ export default { .payment-card { display: flex; flex-direction: column; - border-radius: 6px; - border: 0.25px #b3b4b5; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + border-radius: 0.375rem; + border: 0.25px $gray-1000; + box-shadow: 0px 4px 8px -4px rgba(0, 0, 0, 0.15); } .calendar-section { - background-color: #fff; /* White background for calendar area */ + background-color: $white; /* White background for calendar area */ padding: 0.5rem; text-align: center; - gap: 2px; - font-weight: 700; + gap: 0.125rem; + font-weight: $font-weight-700; white-space: nowrap; + border-top-left-radius: 0.375rem; + border-top-right-radius: 0.375rem; + width: 4.375rem; + @include media-breakpoint-up(md) { + width: 5rem; + } } .payment-amount-section { - background-color: #e0f7e9; /* Light green background for payment area */ + background-color: $green-600; /* Light green background for payment area */ padding: 0.5rem; text-align: center; - font-weight: 700; + font-weight: $font-weight-700; + border-bottom-left-radius: 0.375rem; + border-bottom-right-radius: 0.375rem; + width: 4.375rem; + @include media-breakpoint-up(md) { + width: 5rem; + } + } + .afterpay-learn-more { + margin-left: 0.25rem; + } + .afterpay-image { + margin-left: 0.25rem; } .amount-due { - color: #00723e; /* Green text */ + color: $green-1000; /* Green text */ } .legend { display: flex; @@ -249,11 +266,11 @@ export default { .legend-item { display: flex; align-items: center; - gap: 2px; + gap: 0.125rem; white-space: nowrap; } .legend-item span { - font-weight: 400; + font-weight: $font-weight-normal; } } diff --git a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue index 0d7a82248..ac597c72b 100644 --- a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue +++ b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue @@ -1,72 +1,70 @@