@@ -49,41 +54,66 @@ import { settleAllPromises } from "@/helpers/layout-helper";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import buttonQuestion from "@/digital-components/button-question/button-question";
+import { useMainStore } from "@/store";
+import { getServiceabilityDetails, getZipCodeData } from "@/helpers/service-location-helper";
// Import Component
import baseFormMixin from "@/mixins/base-form-mixin";
+import baseMixin from "@/mixins/base-mixin";
import { Form, defineRule } from "vee-validate";
import siteFooter from "@/iss-components/site-footer/site-footer.vue";
import siteHeader from "@/iss-components/site-header/site-header.vue";
import siteSubHeader from "@/iss-components/site-sub-header/site-sub-header.vue";
+import serviceZipModalQuestion from '@/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue';
// DEFINE VALIDATION RULES
defineRule("selection-required", required(errorMessages.OPTION_REQUIRED));
export default {
name: "service-location",
- mixins: [baseFormMixin],
- components: {
- siteFooter,
- siteHeader,
- siteSubHeader,
- buttonQuestion,
- Form,
- },
- data() {
- return {};
- },
+ mixins: [baseFormMixin, baseMixin],
async beforeRouteEnter(to, from, next) {
+ // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
+
+ const serviceZipCode = useMainStore().order.customer.address.zipCode;
+ const zipCodeData = getZipCodeData(serviceZipCode);
+
+ const serviceabilityDetailsPromise = getServiceabilityDetails(serviceZipCode);
+
// Settle promises and get results
- const promiseResultMap = [{
- resultKey: "cmsContent",
- promise: cmsContentPromise,
- }, ];
+ const promiseResultMap = [
+ {
+ resultKey: "cmsContent",
+ promise: cmsContentPromise,
+ },
+ {
+ resultKey: "serviceabilityDetails",
+ promise: serviceabilityDetailsPromise,
+ },
+ {
+ resultKey: "zipCodeData",
+ promise: zipCodeData,
+ },
+ ];
+
const resultMap = await settleAllPromises(promiseResultMap);
+
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
+ setup() {
+ const mainStore = useMainStore();
+ return { mainStore };
+ },
+ data() {
+ return {
+ zipCode: this.mainStore.order.customer.address.zipCode,
+ state: this.mainStore.order.customer.address.state,
+ isServiceZipServiceable: null,
+ selectedAppointmentType: "",
+ };
+ },
computed: {
questionText() {
return this.getCmsContent("ServiceTypeQuestionWidget", "QuestionText");
@@ -91,6 +121,24 @@ export default {
answersFromCms() {
return this.getCmsContent("ServiceTypeQuestionWidget", "Answers");
},
+ serviceZipCodeQuestion: {
+ get: function() {
+ return {
+ zipCode: this.zipCode,
+ state: this.state,
+ };
+ },
+ set: function (newValue) {
+ if (newValue.zipCode !== this.zipCode) {
+ this.selectedAppointmentType = null;
+ }
+
+ this.zipCode = newValue.zipCode;
+ this.state = newValue.state;
+
+ this.$nextTick();
+ },
+ }
},
methods: {
arePagePrerequisiteValid() {
@@ -105,6 +153,14 @@ export default {
async forwardButtonAction() {},
resetDependentState() {},
},
+ components: {
+ siteFooter,
+ siteHeader,
+ siteSubHeader,
+ buttonQuestion,
+ Form,
+ serviceZipModalQuestion,
+ },
};
diff --git a/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.spec.js b/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.spec.js
new file mode 100644
index 00000000..201b93c7
--- /dev/null
+++ b/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.spec.js
@@ -0,0 +1,251 @@
+import { mount, shallowMount } from "@vue/test-utils";
+import serviceZipModalQuestion from "./service-zip-modal-question";
+import crypto from "crypto";
+global.crypto = crypto;
+
+jest.mock("@/digital-components/textbox-question/textbox-question", () => ({
+ getCmsContent: jest.fn((widgetName, cmsFieldName) => {
+ return widgetName[cmsFieldName];
+ }),
+}));
+
+jest.mock("@/digital-components/modal/modal", () => ({
+ methods: {
+ closeModal: jest.fn(),
+ resetButtonStyle: jest.fn(),
+ },
+}));
+
+const mockGetServiceabilityDetails = (mockServiceZipCode) => {
+ const serviceabilityDetails = {
+ isGlassServiceableInshop: true,
+ isRecalibrationServiceableInshop: true,
+ isGlassServiceableMobile: true,
+ isRecalibrationServiceableMobile: true,
+ };
+
+ return Promise.resolve(serviceabilityDetails);
+};
+
+const mockGetZipCodeData = (mockServiceZipCode) => {
+ if (mockServiceZipCode === "43235") {
+ return {
+ containsMilitaryBase: false,
+ isValid: true,
+ state: "OH",
+ zipCodeCtu: "01820",
+ };
+ }
+
+ if (mockServiceZipCode === "61606") {
+ return {
+ containsMilitaryBase: false,
+ isValid: true,
+ state: "IL",
+ zipCodeCtu: "01526",
+ };
+ }
+
+ return {
+ containsMilitaryBase: false,
+ isValid: false,
+ state: null,
+ zipCodeCtu: null,
+ };
+}
+
+jest.mock(
+ "@/helpers/service-location-helper",
+ () => ({
+ getServiceabilityDetails: jest.fn((mockServiceZipCode) => {
+ return mockGetServiceabilityDetails(mockServiceZipCode);
+ }),
+ getZipCodeData: jest.fn((mockServiceZipCode) => {
+ return mockGetZipCodeData(mockServiceZipCode);
+ }),
+ })
+);
+
+const linkWidgetName = "linkWidgetName";
+const modalWidgetName = "modalWidgetName";
+
+const mockLinkCmsContent = {
+ BodyText: "Sample link body text here.",
+};
+
+const mockModalCmsContent = {
+ FooterText: "Sample modal footer text here.",
+};
+
+const mockMixin = {
+ methods: {
+ getCmsContent: jest.fn((widgetName, cmsFieldName) => {
+ if (widgetName === linkWidgetName) {
+ return mockLinkCmsContent[cmsFieldName];
+ }
+
+ if (widgetName === modalWidgetName) {
+ return mockModalCmsContent[cmsFieldName];
+ }
+ return null;
+ }),
+ }
+}
+
+describe("service-zip-modal-question.vue", () => {
+ it("Initial state on load with existing location info", async () => {
+ // Arrange
+ let serviceZipCodeQuestion = {
+ state: "OH",
+ zipCode: "43235",
+ };
+
+ const wrapper = mount(serviceZipModalQuestion, {
+ mixins: [mockMixin],
+ props: {
+ modelValue: serviceZipCodeQuestion,
+ linkWidgetName: linkWidgetName,
+ modalWidgetName: modalWidgetName,
+ },
+ attachTo: document.body,
+ });
+
+ // Assert
+ expect(wrapper.html()).not.toEqual(expect.stringContaining(mockLinkCmsContent["BodyText"]));
+ });
+
+ it("Should emit update:modelValue on setZipCode for a valid, serviceable zip", async () => {
+ // Arrange
+ let serviceZipCodeQuestion = {
+ state: "IL",
+ zipCode: "61606",
+ };
+
+ const zip = "43235";
+
+ const wrapper = mount(serviceZipModalQuestion, {
+ mixins: [mockMixin],
+ props: {
+ modelValue: serviceZipCodeQuestion,
+ mobileFeePart: {},
+ modalWidgetName: modalWidgetName,
+ },
+ attachTo: document.body,
+ });
+
+ // Act
+ wrapper.vm.internalModel.zipCode = zip;
+
+ await wrapper.vm.setZipCode();
+
+ let expectedEmit = [[{ state: "OH", zipCode: "43235" }]];
+
+ // Assert
+ expect(wrapper.emitted("update:modelValue")).toEqual(expectedEmit);
+ });
+
+ it("Should not emit update:modelValue on setZipCode for an invalid zip", async () => {
+ // Arrange
+ let serviceZipCodeQuestion = {
+ state: "IL",
+ zipCode: "61606",
+ };
+
+ const zip = "";
+
+ const wrapper = mount(serviceZipModalQuestion, {
+ mixins: [mockMixin],
+ props: {
+ modelValue: serviceZipCodeQuestion,
+ modalWidgetName: modalWidgetName,
+ },
+ attachTo: document.body,
+ });
+
+ // Act
+ wrapper.vm.internalModel.zipCode = zip;
+ await wrapper.vm.setZipCode();
+
+ // Assert
+ expect(wrapper.emitted("update:modelValue")).not.toBeTruthy();
+ });
+
+ it("Should display invalid zip alerts for invalid ip inputs", async () => {
+ // Arrange
+ let serviceZipCodeQuestion = {
+ state: "IL",
+ zipCode: "61606",
+ };
+
+ const zip = "11111";
+
+ const wrapper = mount(serviceZipModalQuestion, {
+ mixins: [mockMixin],
+ props: {
+ modelValue: serviceZipCodeQuestion,
+ modalWidgetName: modalWidgetName,
+ },
+ attachTo: document.body,
+ });
+
+ // Act
+ wrapper.vm.internalModel.zipCode = zip;
+ await wrapper.vm.setZipCode();
+
+ // Assert
+ expect(wrapper.vm.displayInvalidZipAlert).toBe(true);
+ });
+
+ it("Should reset all alerts on onModalClosed", async () => {
+ // Arrange
+ let serviceZipCodeQuestion = {
+ state: "IL",
+ zipCode: "61606",
+ };
+
+ const zip = "";
+
+ const wrapper = mount(serviceZipModalQuestion, {
+ mixins: [mockMixin],
+ props: {
+ modelValue: serviceZipCodeQuestion,
+ modalWidgetName: modalWidgetName,
+ },
+ attachTo: document.body,
+ });
+
+ // Act
+ wrapper.vm.internalModel.zipCode = zip;
+ await wrapper.vm.setZipCode();
+ wrapper.vm.onModalClosed();
+
+ // Assert
+ expect(wrapper.vm.displayInvalidZipAlert).toBe(false);
+ });
+
+ it("Should prepopulate the zip on onModalOpened", async () => {
+ // Arrange
+ let serviceZipCodeQuestion = {
+ state: "IL",
+ zipCode: "61606",
+ };
+
+ const zip = "123";
+
+ const wrapper = mount(serviceZipModalQuestion, {
+ mixins: [mockMixin],
+ props: {
+ modelValue: serviceZipCodeQuestion,
+ modalWidgetName: modalWidgetName,
+ },
+ attachTo: document.body,
+ });
+
+ // Act
+ wrapper.vm.internalModel.zipCode = zip;
+ wrapper.vm.onModalOpened();
+
+ // Assert
+ expect(wrapper.vm.internalModel.zipCode).toEqual("61606");
+ });
+});
\ No newline at end of file
diff --git a/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue b/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue
new file mode 100644
index 00000000..5e3493a3
--- /dev/null
+++ b/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.spec.js b/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.spec.js
new file mode 100644
index 00000000..b3276a7a
--- /dev/null
+++ b/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.spec.js
@@ -0,0 +1,40 @@
+import { shallowMount } from "@vue/test-utils";
+import serviceZipQuestion from "./service-zip-question";
+
+describe("service-zip-question.vue", () => {
+ it("Should get the modelValue", async () => {
+ // Arrange
+ const text = "test";
+ const wrapper = shallowMount(serviceZipQuestion, {
+ props: {
+ modelValue: text,
+ },
+ attachTo: document.body,
+ });
+
+ // Act
+ const modelValueText = wrapper.vm.value;
+ wrapper.vm.value = "test also";
+
+ // Assert
+ expect(modelValueText).toEqual("test");
+ });
+
+ it("Should emit to set value", async () => {
+ // Arrange
+ const text = "test";
+ const wrapper = shallowMount(serviceZipQuestion, {
+ props: {
+ modelValue: text,
+ },
+ attachTo: document.body,
+ });
+
+ // Act
+ const modelValueText = wrapper.vm.value;
+ wrapper.vm.value = "test also";
+
+ // Assert
+ expect(wrapper.emitted("update:modelValue")).toEqual([["test also"]]);
+ });
+});
\ No newline at end of file
diff --git a/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.vue b/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.vue
new file mode 100644
index 00000000..c92816af
--- /dev/null
+++ b/src/layouts/service-location/service-zip-modal-question/service-zip-question/service-zip-question.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/store/index.js b/src/store/index.js
index e6726940..54192a1f 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -451,7 +451,7 @@ export const useMainStore = defineStore({
console.error(error);
return [];
});
- },
+ },
async getRainDefense() {
return globalMethods
@@ -525,6 +525,16 @@ export const useMainStore = defineStore({
return availableLineItems;
},
+
+ getServiceabilityDetails({ serviceZipCode }) {
+ const lineItemsToSend = this.order.lineItems.supportingItems;
+ const encodedLineItems = encodeURIComponent(JSON.stringify(lineItemsToSend));
+ return globalMethods.callHttpClient({
+ method: endpoints.GetServiceabilityDetails.method,
+ endpoint: `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&lineItems=${encodedLineItems}`,
+ });
+ },
+
lookupVehicleByVin(vin) {
return globalMethods.callHttpClient({
method: endpoints.LookupVehicleByVin.method,