diff --git a/jest.config.js b/jest.config.js index f4c60e4f7..a2522e0ac 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,6 +13,7 @@ module.exports = { "!src/helpers/unit-test-helper.js", "!src/layouts/vehicle-damage/windshield-options/windshield-options.vue", "!src/layouts/molding-questions/**/*.vue", + "!src/layouts/capability-questions/**/*.vue", "!src/layouts/part-questions/**/*.vue", "!src/layouts/reveal/**/*.vue", "!src/ux-components/text-link/**/*.vue", diff --git a/src/layouts/capability-questions/capability-questions.vue b/src/layouts/capability-questions/capability-questions.vue new file mode 100644 index 000000000..69fc8aad5 --- /dev/null +++ b/src/layouts/capability-questions/capability-questions.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/src/layouts/molding-questions/molding-questions.vue b/src/layouts/molding-questions/molding-questions.vue index 0ffaae411..d2b469a36 100644 --- a/src/layouts/molding-questions/molding-questions.vue +++ b/src/layouts/molding-questions/molding-questions.vue @@ -6,6 +6,7 @@ v-slot="{ meta }" >
+ @@ -49,6 +50,7 @@ import alert from "@/ux-components/alert/alert"; import questionChain from "@/common-components/question-chain/question-chain"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; +import loadingModal from '@/common-components/loading-modal/loading-modal.vue'; // Supporting Files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; @@ -60,12 +62,15 @@ import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { Form, defineRule } from "vee-validate"; import { required } from "@/helpers/validation-rules"; import { errorMessages } from "@/constants/error-messages"; +import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; +import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; // DEFINE VALIDATION RULES defineRule("questions-required", required(errorMessages.OPTION_REQUIRED)); export default { name: "molding-questions", + mixins: [vehicleQuestionsMixin], async beforeRouteEnter(to, from, next) { // Call APIs const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); @@ -110,7 +115,7 @@ export default { }, methods: { arePagePrerequisitesValid() { - return true; + return true; // TODO - DO TRUE TEST OF PAGEDATA // return Object.keys(store.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)).length > 0; }, showThisPartQuestionChain(part, i) { @@ -146,111 +151,21 @@ export default { const glassNameAndPartsForStore = partsLookup.data.glassNameAndPartsForStore; - console.log("glassNameAndPartsForStore: ", glassNameAndPartsForStore); + const hasCapabilityQuestions = this.hasCapabilityQuestions(glassNameAndPartsForStore); - // HARD CODE RESPONSE FOR NOW... - - this.glassNameAndPartsForStore = [ - { - "glassName": "Single", - "glassLocation": "Windshield", - "parts": [ - { - "partNumber": "FW03861GTYN", - "description": "rain sensor, heated glass, auto dimming mirror, solar, 3rd visor band, condensation sensor", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": false, - "recalibrationType": null, - "childParts": null, - "childPartQuestions": [ - { - "questionSequence": 1, - "questionText": "Does the rubber seal around your windshield have a chrome strip running through it?", - "answers": [ - { - "answerResult": "WKT D1106 C", - "answerText": "Yes", - "nextQuestionSequence": null - }, - { - "answerResult": "WKT D1106 B", - "answerText": "No", - "nextQuestionSequence": null - } - ] - } - ] - } - ] - }, - { - "glassName": "Stationary", - "glassLocation": "Rear", - "parts": [ - { - "partNumber": "FB25992GTNN", - "description": "heated glass, solar", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": false, - "recalibrationType": null, - "childParts": null, - "childPartQuestions": [] - } - ] - } - ]; - - - - - - - // test response data for multiple parts - const hasMultipleParts = glassNameAndPartsForStore.some((glass) => glass.parts?.length > 1); - - // loop through all glass items - const collectedGlassParts = []; - glassNameAndPartsForStore.forEach((glass) => { - if (Array.isArray(glass.parts) && glass.parts.length === 1) { - const singlePart = glass.parts[0]; - collectedGlassParts.push({ - "partNumber": singlePart.partNumber, - "description": singlePart.description, - "color": singlePart.color, - "requiresRecalibration": singlePart.requiresRecalibration, - "childParts": singlePart.childParts, - "price": singlePart.price, - }); - // check for and collect any molding questions - if (Array.isArray(singlePart.childPartQuestions) && singlePart.childPartQuestions.length > 0) { - this.moldingQuestionsForStore.push({ - "glassName": glass.glassName, - "glassLocation": glass.glassLocation, - "childPartQuestions": singlePart.childPartQuestions, - }); - } - } - }); - if (!hasMultipleParts) { - this.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts); - } - this.navigateForward(hasMultipleParts, glassNameAndPartsForStore); - }, - navigateForward(hasMultipleParts, glassNameAndPartsForStore) { - if (hasMultipleParts) { - // if multiple parts on any glass - // go to vehicle-parts page and pass the partsData - this.$router.navigate(this.navigationScenarios.ANSWERED_QUESTIONS_WITH_MULTIPLE_PARTS,this.$route,{},{},{partsOrQuestions: glassNameAndPartsForStore}); + if (hasCapabilityQuestions) { + // if has capability questions + // go to capability-questions page + this.$router.navigate(this.navigationScenarios.HAS_CAPABILITY_QUESTIONS,this.$route,{},{},{partsOrQuestions: glassNameAndPartsForStore}); } else { // if single parts only - // go to quote page - this.$router.navigate(this.navigationScenarios.ANSWERED_QUESTIONS_WITH_SINGLE_PART,this.$route); + const collectedGlassParts = this.reducedGlassPartsArray(glassNameAndPartsForStore); + // save to store lineItems.glassParts + this.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts); + // go to heritage quote page + this.$refs.loadingModal.showModal(); + navigateToHeritageFunnel(); } - - // TODO - ADD CHECK FOR CAPABILITY QUESTIONS TO SEE IF NAVIGATE TO CAPABILITY-QUESTIONS - }, }, watch: { @@ -269,6 +184,7 @@ export default { questionChain, funnelSubHeader, funnelFooter, + loadingModal, Form, }, }; diff --git a/src/layouts/part-questions/part-questions.vue b/src/layouts/part-questions/part-questions.vue index 6f18408b7..db237ce2b 100644 --- a/src/layouts/part-questions/part-questions.vue +++ b/src/layouts/part-questions/part-questions.vue @@ -6,6 +6,7 @@ v-slot="{ meta }" >
+ @@ -49,6 +50,7 @@ import alert from "@/ux-components/alert/alert"; import questionChain from "@/common-components/question-chain/question-chain"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; +import loadingModal from '@/common-components/loading-modal/loading-modal.vue'; // Supporting Files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; @@ -60,6 +62,8 @@ import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { Form, defineRule } from "vee-validate"; import { required } from "@/helpers/validation-rules"; import { errorMessages } from "@/constants/error-messages"; +import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; +import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; // DEFINE VALIDATION RULES defineRule("questions-required", required(errorMessages.OPTION_REQUIRED)); @@ -108,6 +112,7 @@ export default { return this.getCmsContent("AlertPartsQuestions", "BodyText"); }, }, + mixins: [vehicleQuestionsMixin], methods: { showThisPartQuestionChain(part, i) { if (part.partQuestions?.length < 1) { return false; } // return false if only one partQuestion @@ -137,59 +142,37 @@ export default { // call API parts method const partsLookup = await this.dispatchStoreAction(storeActions.GET_PARTS) .catch(() => { - this.$refs.funnelFooter.removeLoader(); + return this.$refs.funnelFooter.removeLoader(); }); - if (!partsLookup) { return } - const glassNameAndPartsForStore = partsLookup.data.glassNameAndParts; - // test response data for multiple parts - const hasMultipleParts = glassNameAndPartsForStore.some((glass) => glass.parts?.length > 1); + const hasGlassLocationWithMultipleParts = this.hasGlassLocationWithMultipleParts(glassNameAndPartsForStore); + const hasChildPartQuestions = this.hasChildPartQuestions(glassNameAndPartsForStore); + const hasCapabilityQuestions = this.hasCapabilityQuestions(glassNameAndPartsForStore); - // loop through all glass items - const collectedGlassParts = []; - glassNameAndPartsForStore.forEach((glass) => { - if (Array.isArray(glass.parts) && glass.parts.length === 1) { - const singlePart = glass.parts[0]; - collectedGlassParts.push({ - "partNumber": singlePart.partNumber, - "description": singlePart.description, - "color": singlePart.color, - "requiresRecalibration": singlePart.requiresRecalibration, - "childParts": singlePart.childParts, - "price": singlePart.price, - }); - // check for and collect any molding questions - if (Array.isArray(singlePart.childPartQuestions) && singlePart.childPartQuestions.length > 0) { - this.moldingQuestionsForStore.push({ - "glassName": glass.glassName, - "glassLocation": glass.glassLocation, - "childPartQuestions": singlePart.childPartQuestions, - }); - } - } - }); - if (!hasMultipleParts) { - store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts); - } - this.navigateForward(hasMultipleParts, glassNameAndPartsForStore); - }, - async navigateForward(hasMultipleParts, glassNameAndPartsForStore) { - if (hasMultipleParts) { + // NAVIGATE FORWARD + if (hasGlassLocationWithMultipleParts) { // if multiple parts on any glass // go to vehicle-parts page and pass the partsData this.$router.navigate(this.navigationScenarios.ANSWERED_QUESTIONS_WITH_MULTIPLE_PARTS,this.$route,{},{},{partsOrQuestions: glassNameAndPartsForStore}); + } else if (hasChildPartQuestions) { + // if any childpart questions + // go to molding-questions page and pass the partsData + this.$router.navigate(this.navigationScenarios.HAS_MOLDING_QUESTIONS,this.$route,{},{},{partsOrQuestions: glassNameAndPartsForStore}); + } else if (hasCapabilityQuestions) { + // if has capability questions + // go to capability-questions page and pass the partsData + this.$router.navigate(this.navigationScenarios.HAS_CAPABILITY_QUESTIONS,this.$route,{},{},{partsOrQuestions: glassNameAndPartsForStore}); } else { // if single parts only - // go to quote page - this.$router.navigate(this.navigationScenarios.ANSWERED_QUESTIONS_WITH_SINGLE_PART,this.$route); + const collectedGlassParts = this.reducedGlassPartsArray(glassNameAndPartsForStore); + // save to store lineItems.glassParts + this.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts); + // go to heritage quote page + this.$refs.loadingModal.showModal(); + navigateToHeritageFunnel(); } - - // TODO - ADD CHECK FOR CAPABILITY QUESTIONS TO SEE IF NAVIGATE TO CAPABILITY-QUESTIONS - - - }, arePagePrerequisitesValid() { return Object.keys(store.getters.pageData(fmgPageValues.PART_QUESTIONS)).length > 0; @@ -211,6 +194,7 @@ export default { questionChain, funnelSubHeader, funnelFooter, + loadingModal, Form, }, }; diff --git a/src/layouts/vehicle-parts/vehicle-parts.spec.js b/src/layouts/vehicle-parts/vehicle-parts.spec.js index dbe0cbfec..9f766c7e9 100644 --- a/src/layouts/vehicle-parts/vehicle-parts.spec.js +++ b/src/layouts/vehicle-parts/vehicle-parts.spec.js @@ -10,6 +10,7 @@ import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { nextTick } from "vue"; import baseMixin from "@/mixins/base-mixin.js"; import store from "@/store"; +import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ @@ -21,6 +22,10 @@ jest.mock("@/helpers/cms-content-helper", () => ({ fetchCmsContentForPage: jest.fn(), })); +jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({ + navigateToHeritageFunnel: jest.fn() + })); + // Mock store jest.mock("@/store", () => { return {}; }, { virtual: true }); @@ -211,17 +216,107 @@ describe("vehicle-parts.vue", () => { }); - test("ForwardButtonAction triggers a router.navigate change and saves selected parts to store", async () => { + test("ForwardButtonAction triggers a router.navigate change if there are child part questions", async () => { //Arrange - store.getters.pageData.mockReturnValueOnce(basePartResponse); - store.getters.lineItems = { glassParts: {} } - store.commit = jest.fn(); - + store.getters.pageData.mockReturnValueOnce({ + partsOrQuestions: [ + { + glassName: "Single", + glassLocation: "Windshield", + parts: [ + { + "partNumber": "FW03861GTYN", + "description": "rain sensor, heated glass, auto dimming mirror, solar, 3rd visor band, condensation sensor", + "color": "Green Tint", + "requiresRecalibration": false, + "requiresCapabilityQuestions": false, + "recalibrationType": null, + "childParts": null, + "childPartQuestions": [ + { + "questionSequence": 1, + "questionText": "Does the rubber seal around your windshield have a chrome strip running through it?", + "answers": [ + { + "answerResult": "WKT D1106 C", + "answerText": "Yes", + "nextQuestionSequence": null + }, + { + "answerResult": "WKT D1106 B", + "answerText": "No", + "nextQuestionSequence": null + } + ] + } + ] + } + ], + partQuestions: null + } + ] + }); + + const { wrapper } = setupMocks({ + mountOptionsMockData: { + router: { + navigate: jest.fn() + }, + route: { + query: { + fmgPage: 'vehicle-parts', + } + }, + store: { + getters: store.getters, + commit: store.commit + }, + } + }); + + wrapper.setData({ glassParts: { "Rear-Stationary": { partNumber: 'FW03861GTYN' } } }); + + //Act + vehicleParts.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-parts" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); + }); + + test("ForwardButtonAction triggers a router.navigate change if there are capability questions", async () => { + + //Arrange + store.getters.pageData.mockReturnValueOnce({ + partsOrQuestions: [ + { + glassName: "Single", + glassLocation: "Windshield", + parts: [ + { + partNumber: "DB12209GTYN", + description: "heated glass, solar, 1 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null + } + ], + partQuestions: null + } + ] + }); + const { wrapper } = setupMocks({ mountOptionsMockData: { router: { - navigate: jest.fn(), navigate: jest.fn() }, route: { @@ -251,6 +346,50 @@ describe("vehicle-parts.vue", () => { //Assert expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); }); + + test("ForwardButtonAction saves selected parts to store if no molding or capability questions", async () => { + + //Arrange + store.getters.pageData.mockReturnValueOnce(basePartResponse); + store.getters.lineItems = { glassParts: {} } + + const { wrapper } = setupMocks({ + mountOptionsMockData: { + router: { + navigate: jest.fn() + }, + route: { + query: { + fmgPage: 'vehicle-parts', + } + }, + store: { + getters: store.getters, + commit: store.commit + }, + navigateToHeritageFunnel: jest.fn() + } + }); + + wrapper.vm.$store.commit = jest.fn(); + wrapper.vm.$refs.loadingModal.showModal = jest.fn(); + + wrapper.setData({ glassParts: { "Rear-Stationary": { partNumber: 'DB12209GTYN' } } }); + + //Act + vehicleParts.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-parts" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.$store.commit).toHaveBeenCalled(); + expect(navigateToHeritageFunnel).toHaveBeenCalled(); + }); }); function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {} }) { @@ -288,6 +427,7 @@ function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {} wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); + wrapper.vm.$refs.loadingModal.showModal = jest.fn(); return { wrapper, apiPromise }; } diff --git a/src/layouts/vehicle-parts/vehicle-parts.vue b/src/layouts/vehicle-parts/vehicle-parts.vue index 80cdf8a97..290910217 100644 --- a/src/layouts/vehicle-parts/vehicle-parts.vue +++ b/src/layouts/vehicle-parts/vehicle-parts.vue @@ -1,6 +1,7 @@