diff --git a/jest.config.js b/jest.config.js index bce302e5a..36c4507fc 100644 --- a/jest.config.js +++ b/jest.config.js @@ -18,7 +18,7 @@ module.exports = { testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { global: { - statements: 87, + statements: 89, }, }, }; diff --git a/package.json b/package.json index ee8fdeb88..82561e1ca 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,7 @@ }, "extends": [ "plugin:vue/vue3-essential", - "eslint:recommended", - "@vue/prettier" + "eslint:recommended" ], "parserOptions": { "parser": "babel-eslint" diff --git a/src/common-components/button-question/button-question.spec.js b/src/common-components/button-question/button-question.spec.js index 4a1f42822..67e2f010e 100644 --- a/src/common-components/button-question/button-question.spec.js +++ b/src/common-components/button-question/button-question.spec.js @@ -2,20 +2,83 @@ import { shallowMount } from "@vue/test-utils"; import buttonQuestion from "@/common-components/button-question/button-question"; describe("buttonQuestion.vue", () => { - it("Should render the 'questionText' prop value as a span value for the button question and the 'answer' values should render as text values for button components.", async () => { + it("Should show overflow classes on fieldset if isOverflowScrollable is true", async () => { // Act const wrapper = shallowMount(buttonQuestion); await wrapper.setProps({ - questionText: "Question Text", - answers: ["2023", "2022", "2021"], - modelValue: "2020", + isOverflowScrollable: true, }); - wrapper.vm.chooseAnswer("2021"); - // Assert - expect(wrapper.find(".text-center").text()).toEqual("Question Text"); - const buttonButtons = wrapper.findAllComponents('[data-test="button"]'); - expect(buttonButtons.length).toBe(3); - expect(wrapper.props().modelValue).toBe("2020"); + const fieldSet = wrapper.find('fieldset'); + expect(fieldSet.classes()).toContain("overflow-scroll"); }); }); + +describe("buttonQuestion.vue", () => { + it("Fieldset classes should contain row if button type is listCard", async () => { + // Act + const wrapper = shallowMount(buttonQuestion); + await wrapper.setProps({ + buttonType: "listCard", + }); + // Assert + const Div = wrapper.find('fieldset div'); + expect(Div.classes()).toContain("row"); + }); +}); + +describe("buttonQuestion.vue", () => { + it("Fieldset classes should contain d-flex if button type is listButtonHorizontal", async () => { + // Act + const wrapper = shallowMount(buttonQuestion); + await wrapper.setProps({ + buttonType: "listButtonHorizontal", + }); + // Assert + const Div = wrapper.find('fieldset div'); + expect(Div.classes()).toContain("d-flex"); + }); +}); + +describe("buttonQuestion.vue", () => { + it("Should trigger event modelValue change on select", async () => { + // Act + const wrapper = shallowMount(buttonQuestion); + await wrapper.setData({ + modelValueAnswers: ["Windshield"], + chosenAnswer: "Windshield" + }); + wrapper.setValue({ answer: wrapper.vm.chosenAnswer }); + await wrapper.vm.$nextTick(); + // Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{answer: "Windshield"}]); + }); +}); + +describe("buttonQuestion.vue", () => { + it("Should trigger event modelValue change with an array of string values on select if checked is true and multiple options are chosen", async () => { + // Act + const wrapper = shallowMount(buttonQuestion); + await wrapper.setData({ + modelValueAnswers: ["Windshield", "BackDoor"] + }); + wrapper.vm.handleCheckedChanged(true); + // Assert + expect(typeof wrapper.emitted()["update:modelValue"][0]).toEqual('object'); + }); +}); + +describe("buttonQuestion.vue", () => { + it("Should not trigger event modelValue change on select if checked is false", async () => { + // Act + const wrapper = shallowMount(buttonQuestion); + wrapper.setData({ + modelValueAnswers: ["Front-door"] + }) + const val = {isChecked : false, buttonId: "Front-door"} + wrapper.vm.handleCheckedChanged(val); + // Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([undefined]); + }); +}); + diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index bf52b4ac8..13cb43240 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -4,40 +4,36 @@ {{ questionText }}
-
- {{ groupName }} -
- -
+
+ {{groupName}} +
+ +
@@ -79,7 +75,7 @@ export default { isRequired: Boolean, isOverflowScrollable: Boolean, isWide: Boolean, - modelValue: String, + modelValue: Array, }, computed: { getFieldSetClasses() { @@ -96,16 +92,29 @@ export default { case "listButtonHorizontal": classes = "d-flex flex-row p-0"; break; - case "listCard": - classes = "row g-2"; + case 'listCard': + classes = 'row justify-content-center g-2' break; } return classes; }, }, + data(){ + return { + modelValueAnswers: [], + } + }, + mounted(){ + if(Array.isArray(this.answers) && this.answers.length === 1) { + this.modelValueAnswers.push(typeof(this.answers[0]) === 'object' ? this.answers[0].Name : this.answers[0]); + this.$emit("update:modelValue", this.modelValueAnswers); + }; + }, methods: { - chooseAnswer(answer) { - this.$emit("update:modelValue", answer); + handleCheckedChanged(val) { + // Add or remove item to array of data to emit + val.isChecked ? this.modelValueAnswers.push(val.buttonId) : this.modelValueAnswers.splice(this.modelValueAnswers.indexOf(val.buttonId), 1); + this.$emit("update:modelValue", this.modelValueAnswers.length ? this.modelValueAnswers : undefined); }, }, components: { diff --git a/src/common-components/funnel-footer/funnel-footer.spec.js b/src/common-components/funnel-footer/funnel-footer.spec.js new file mode 100644 index 000000000..f1a885dfd --- /dev/null +++ b/src/common-components/funnel-footer/funnel-footer.spec.js @@ -0,0 +1,60 @@ +import { mount } from "@vue/test-utils"; +import funnelFooter from "./funnel-footer"; + +describe("funnel-footer.vue", () => { + + it("Should return footer-link class", async () => { + // Act + try { + global.document.getElementById = jest.fn().mockImplementation(()=> { + return {} + }); + + const wrapper = mount(funnelFooter, { + propsData: { + footer: true + }, + }); + + // Assert + const link = wrapper.find("a"); + + // Expect + expect(link.attributes('class')).toContain("footer-link"); + expect(global.document.getElementById).toBeCalled(); + } catch(error) { + console.log(error); + } + + }); + + it("Should return link text", async () => { + // Act + const wrapper = mount(funnelFooter, { + propsData: { + text: "Terms of use", + }, + }); + + // Assert + const link = wrapper.find("a"); + + expect(link.text()).toEqual("Terms of use"); + }); + + it("Should return class btn-primary", async () => { + // Act + const wrapper = mount(funnelFooter, { + propsData: { + isPrimary: true, + }, + }); + + // Assert + const input = wrapper.find("button"); + + // Expect + expect(input.attributes("class")).toContain("btn-primary"); + }); + +}); diff --git a/src/common-components/funnel-footer/funnel-footer.vue b/src/common-components/funnel-footer/funnel-footer.vue new file mode 100644 index 000000000..90c380b08 --- /dev/null +++ b/src/common-components/funnel-footer/funnel-footer.vue @@ -0,0 +1,81 @@ + + + diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 263f153df..177211ae0 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -11,9 +11,12 @@ const storeActions = { GET_EVOX_IMAGE: "getEvoxImage", LOOKUP_VEHICLE_BY_YMMS: "lookupVehicleByYmms", LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin", - // EVENT BUS - ADD_EVENT_TO_BUS: "addEventToBus", - REMOVE_EVENT_FROM_BUS: "removeEventFromBus", + + // DEPENDENCY MUTATIONS + RESET_VEHICLE_AND_DEPS: "resetVehicleAndDependencies", + RESET_DAMAGE_AND_DEPS: "resetDamageAndDependencies", + RESET_REGISTRATION_AND_DEPS: "resetRegistrationAndDependencies", + RESET_PARTS_AND_DEPS: "resetPartsAndDependencies", }; export { storeActions }; diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index 61882b691..f779b11cf 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -1,9 +1,23 @@ const storeMutations = { + + // VEHICLE MUTATIONS UPDATE_YEAR: "updateYear", UPDATE_MAKE: "updateMake", UPDATE_MODEL: "updateModel", UPDATE_STYLE: "updateStyle", - UPDATE_VEHICLE: "updateVehicle", + UPDATE_CAR_ID: "updateCarId", + UPDATE_VEHICLE_CATEGORY: "updateVehicleCategory", + + // EVENT BUS MUTATIONS + ADD_EVENT_TO_BUS: "addEventToBus", + REMOVE_EVENT_FROM_BUS: "removeEventFromBus", + + // DEPENDENCY MUTATIONS + RESET_VEHICLE_AND_DEPS: "resetVehicleAndDependencies", + RESET_DAMAGE_AND_DEPS: "resetDamageAndDependencies", + RESET_REGISTRATION_AND_DEPS: "resetRegistrationAndDependencies", + RESET_PARTS_AND_DEPS: "resetPartsAndDependencies", + }; export { storeMutations }; diff --git a/src/helpers/event-bus/event-bus.js b/src/helpers/event-bus/event-bus.js index 47f229210..19b30870c 100644 --- a/src/helpers/event-bus/event-bus.js +++ b/src/helpers/event-bus/event-bus.js @@ -1,10 +1,10 @@ import store from "@/store"; -import { storeActions } from "@/constants/store-actions.js"; +import { storeMutations } from "@/constants/store-mutations.js"; export default { // Adds event to the bus given its category, subcategory, and eventValue; addEventToBus(category, subCategory, eventValue) { - store.commit(storeActions.ADD_EVENT_TO_BUS, { + store.commit(storeMutations.ADD_EVENT_TO_BUS, { category: category, subCategory: subCategory, eventValue: eventValue, @@ -15,7 +15,7 @@ export default { readAndPopEventFromBus(category, subCategory) { const event = store.getters.eventBusItem(category, subCategory); - store.commit(storeActions.REMOVE_EVENT_FROM_BUS, { + store.commit(storeMutations.REMOVE_EVENT_FROM_BUS, { category: category, subCategory: subCategory, }); diff --git a/src/layouts/component-test/component-test.vue b/src/layouts/component-test/component-test.vue index 4b59af5fd..2fd05b516 100644 --- a/src/layouts/component-test/component-test.vue +++ b/src/layouts/component-test/component-test.vue @@ -40,6 +40,31 @@ +
+
+

Links

+
+
+
+
+

+

+

+ +
+

List Card

@@ -60,7 +85,7 @@ /> Button Question
- - - - - - - - - - - +
+
+ + + + + + + + + + + +
+
+
+
+

Funnel Footer

+
+
+
+
+ +
+
@@ -911,6 +950,7 @@ import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-bu import checkbox from "@/ux-components/checkbox/checkbox"; import buttonQuestion from "@/common-components/button-question/button-question"; import textLink from "@/ux-components/text-link/text-link"; +import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; export default { name: "App", components: { @@ -925,6 +965,7 @@ export default { funnelHeader, buttonQuestion, textLink, + funnelFooter }, data() { return { @@ -947,7 +988,7 @@ export default { Text: "List Card CB", SubText: "checkbox", ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955", - AnswerImageUrl: "side-window-damage-right-all.svg", + AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3", }, ], listCardRadio: [ @@ -956,7 +997,7 @@ export default { Text: "List Card R", SubText: "Radio", ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955", - AnswerImageUrl: "side-window-damage-right-all.svg", + AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3", }, ], listCardCheckBoxHorizontalSubText: [ @@ -965,7 +1006,7 @@ export default { Text: "List Card CBHst", SubText: "With Subtext", ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955", - AnswerImageUrl: "side-window-damage-right-all.svg", + AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3", }, ], listCardCheckBoxHorizontal: [ @@ -973,7 +1014,7 @@ export default { Name: "List-Card-CBH", Text: "List Card CBH", ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955", - AnswerImageUrl: "side-window-damage-right-all.svg", + AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3", }, ], listRadioHorizontalSubText: [ @@ -982,7 +1023,7 @@ export default { Text: "List Card RHst", SubText: "With Subtext", ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955", - AnswerImageUrl: "side-window-damage-right-all.svg", + AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3", }, ], listRadioHorizontal: [ @@ -990,7 +1031,7 @@ export default { Name: "List-Card-RH", Text: "List Card RH", ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955", - AnswerImageUrl: "side-window-damage-right-all.svg", + AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3", }, ], listCardGroup: [ @@ -999,20 +1040,20 @@ export default { Text: "Side Window", SubText: "With Subtext", ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955", - AnswerImageUrl: "side-window-damage-right-all.svg", + AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3", }, { Name: "Side-Window-2", Text: "Side Window", SubText: "With Subtext that is more than one line", ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955", - AnswerImageUrl: "side-window-damage-right-all.svg", + AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3", }, { Name: "Side-Window-3", Text: "Side Window", ImageId: "53343ce4-5b6a-46aa-a80a-f948c1723955", - AnswerImageUrl: "side-window-damage-right-all.svg", + AnswerImageUrl: "https://digitalconsumercms-dev.safelite.com/images/default-source/damage-options/car.svg?sfvrsn=1468d7f1_3", }, ], }; diff --git a/src/layouts/vehicle-damage/damage-location-question/damage-location-question.spec.js b/src/layouts/vehicle-damage/damage-location-question/damage-location-question.spec.js new file mode 100644 index 000000000..01f063eef --- /dev/null +++ b/src/layouts/vehicle-damage/damage-location-question/damage-location-question.spec.js @@ -0,0 +1,83 @@ +import { shallowMount } from "@vue/test-utils"; +import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import store from "@/store"; +jest.mock("@/store", () => { return {}; }, {virtual: true}); + +describe("damage-location-question.vue", () => { + test("Selected location option is emitted upon selection.", async () => { + + //Arrange + const { wrapper } = setupMocks({ modelValueProp: ["Windshield"] }); + const locationToSelect = ["Backseat"]; + + //Act + wrapper.setValue({ modelValue: locationToSelect }); + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{modelValue: ["Backseat"]}]); + }); + }); + + describe("damage-location-question.vue", () => { + test("Answers to display filtered by data from api.", async () => { + + //Arrange + const { wrapper, cmsContent, damageOptions } = setupMocks({ dataFromStoreApi: { + driverSideOptions: { + availableReplacementOptions: ['Quarter', 'Front'] + }, + windshieldOptions: { + availableReplacementOptions: ['Single'] + }, + backGlassOptions: { + availableReplacementOptions: [] + } + } + }); + + //Act + damageLocationQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, damageOptions, "car-group"); + + //Assert + expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'car-Windshield' }, { Name: 'car-SideDoor' } ]) + }); + }); + + function setupMocks({ + modelValueProp = ["Windshield", "SideDoor"], + isMultiSelect = false, + groupName = "damageQuestion", + cmsQuestionText = "CMS text goes here", + cmsAnswers = [{Name: "car-Windshield"}, {Name: "car-SideDoor"}, {Name: "car-RearWindow"}], + dataFromStoreApi = [], + }) { + + //Mock store + store.dispatch = jest.fn(() => dataFromStoreApi); + store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} }; + const mountOptions = getMountOptions({ + store: { + dispatch: store.dispatch, + getters: store.getters, + }, + }); + + //Mock props + mountOptions.propsData = { + modelValue: modelValueProp, + isMultiSelect: isMultiSelect, + }; + + const wrapper = shallowMount(damageLocationQuestion, mountOptions); + + //Mock CMS content + const cmsContent = { + groupName: groupName, + QuestionText: cmsQuestionText, + Answers: cmsAnswers, + }; + const damageOptions = dataFromStoreApi; + return { wrapper, cmsContent, damageOptions }; + } \ No newline at end of file diff --git a/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue b/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue new file mode 100644 index 000000000..97c08c3b8 --- /dev/null +++ b/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue @@ -0,0 +1,68 @@ + + + \ No newline at end of file diff --git a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js new file mode 100644 index 000000000..103aecd4c --- /dev/null +++ b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js @@ -0,0 +1,76 @@ +import { shallowMount } from "@vue/test-utils"; +import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import store from "@/store"; +jest.mock("@/store", () => { return {}; }, {virtual: true}); + +describe("replace-options-question.vue", () => { + test("Selected damage option is emitted upon selection.", async () => { + + //Arrange + const { wrapper } = setupMocks({ modelValueProp: ["Windshield"] }); + const damageToSelect = ["Backseat"]; + + //Act + wrapper.setValue({ modelValue: damageToSelect }); + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{modelValue: ["Backseat"]}]); + }); + }); + + describe("replace-options-question.vue", () => { + test("Answers to display filtered by data from api.", async () => { + + //Arrange + const { wrapper, cmsContent, replaceOptions } = setupMocks({ dataFromStoreApi: ["Windshield", "FrontDoor"]}); + + //Act + replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, replaceOptions, "car-group"); + + //Assert + expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'car-Windshield' }, { Name: 'car-FrontDoor' } ]) + }); + }); + + function setupMocks({ + modelValueProp = ["Windshield"], + isAvailale = true, + isMultiSelect = false, + filterByVehicleCategory = false, + groupName = "damageQuestion", + cmsQuestionText = "CMS text goes here", + cmsAnswers = [{Name: "car-Windshield"}, {Name: "car-BackDoor"}, {Name: "car-FrontDoor"}], + dataFromStoreApi = [], + }) { + + //Mock store + store.dispatch = jest.fn(() => dataFromStoreApi); + store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} }; + const mountOptions = getMountOptions({ + store: { + dispatch: store.dispatch, + getters: store.getters, + }, + }); + + //Mock props + mountOptions.propsData = { + modelValue: modelValueProp, + isAvailable: isAvailale, + isMultiSelect: isMultiSelect, + filterByVehicleCategory: filterByVehicleCategory + }; + + const wrapper = shallowMount(replaceOptionsQuestion, mountOptions); + + //Mock CMS content + const cmsContent = { + groupName: groupName, + QuestionText: cmsQuestionText, + Answers: cmsAnswers, + }; + const replaceOptions = dataFromStoreApi; + return { wrapper, cmsContent, replaceOptions }; + } \ No newline at end of file diff --git a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue new file mode 100644 index 000000000..f01f0cd90 --- /dev/null +++ b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/layouts/vehicle-damage/vehicle-damage.spec.js b/src/layouts/vehicle-damage/vehicle-damage.spec.js index 81c7c8b7a..49df756c2 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.spec.js +++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js @@ -3,6 +3,8 @@ import vehicleDamage from "@/layouts/vehicle-damage/vehicle-damage.vue"; import funnelHeader from "@/common-components/funnel-header/funnel-header"; import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; +import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question"; +import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question"; // Supporting Files import { settleAllPromises } from "@/helpers/layout-helper.js"; @@ -11,6 +13,8 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { nextTick } from "vue"; +import { storeActions } from "@/constants/store-actions"; +import store from "@/store"; // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ @@ -24,6 +28,8 @@ jest.mock("@/helpers/cms-content-helper", () => ({ // Mock Store jest.mock("@/store", () => ({ + commit: jest.fn(), + dispatch: jest.fn(), getters: { vehicle: { carId: "C00000000", @@ -136,6 +142,28 @@ describe("vehicle-damage.vue", () => { }); }); +describe("vehicle-damage.vue", () => { + test("Call invalidation, ResetPartsAndDeps should be called", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.resetDependentState(); + + //Assert + expect(store.dispatch).toBeCalledWith(storeActions.RESET_PARTS_AND_DEPS) + + }); +}); + function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {}, @@ -144,7 +172,9 @@ function setupMocks({ baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn(); baseMixin.methods.dispatchNonBlockingStoreAction.mockImplementation(() => { return Promise.resolve({ - data: ["Front Window", "Back Window", "Side Window"], + driverSideOptions: { + availableReplacementOptions: ["Front", "Back", "Side"], + } }); }); const apiResponses = { @@ -159,7 +189,11 @@ function setupMocks({ "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", }, }, - damageOptions: ["Front Window", "Back Window", "Side Window"], + damageOptions: { + driverSideOptions: { + availableReplacementOptions: ["Front", "Back", "Side"], + } + }, }; const apiPromise = Promise.resolve(apiResponses); @@ -180,6 +214,15 @@ function setupMocks({ initializeComponent: jest.fn(), }; + const driverSideOptions = replaceOptionsQuestion + driverSideOptions.methods = { + initializeComponent: jest.fn(), + }; + + damageLocationQuestion.methods = { + initializeComponent: jest.fn(), + } + const mountOptions = getMountOptions(mountOptionsMockData); const wrapper = shallowMount(vehicleDamage, mountOptions); @@ -197,5 +240,17 @@ function setupMocks({ funnelSubHeaderWrapper.vm.initializeComponent = funnelSubHeader.methods.initializeComponent; + const driverSideOptionsWrapper = wrapper.findComponent({ + name: "replaceOptionsQuestion", + }); + driverSideOptionsWrapper.vm.initializeComponent = + replaceOptionsQuestion.methods.initializeComponent; + + const damageLocationQuestionWrapper = wrapper.findComponent({ + name: "damageLocationQuestion", + }); + damageLocationQuestionWrapper.vm.initializeComponent = + damageLocationQuestion.methods.initializeComponent; + return { wrapper, apiPromise }; } diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index 021db39ef..9d57e4345 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -1,8 +1,10 @@ @@ -11,12 +13,15 @@ import funnelHeader from "@/common-components/funnel-header/funnel-header"; import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; +import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question"; +import damageLocationQuestion from"@/layouts/vehicle-damage/damage-location-question/damage-location-question"; // Supporting files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; +import { storeActions } from "@/constants/store-actions"; import store from "@/store"; import baseMixin from "@/mixins/base-mixin"; -import { storeActions } from "@/constants/store-actions"; + export default { name: "vehicle-damage", @@ -54,18 +59,38 @@ export default { vm.$refs.funnelSubHeader.initializeComponent( resultMap.cmsContent.FunnelSubHeaderWidget ); - // use resultMap.damageOptions for damage options + vm.$refs.driverSideOptions.initializeComponent( + resultMap.cmsContent.DriverSideReplaceOptionsQuestion, resultMap.damageOptions.driverSideOptions.availableReplacementOptions, 'DriverSideReplaceOptionsQuestion' + ); + vm.$refs.damageLocation.initializeComponent( + resultMap.cmsContent.DamageLocationQuestion, resultMap.damageOptions, 'DamageLocationQuestion' + ); + + // for damageOptions and cms content data + //console.log(resultMap) }); }, + data(){ + return { + damageLocationQuestionData: [], + driverSideOptionsData: [], + } + }, methods: { arePagePrerequisitesValid() { return store.getters.vehicle.carId !== null; }, + resetDependentState() { + // Invokes + store.dispatch(storeActions.RESET_PARTS_AND_DEPS); + }, }, components: { funnelHeader, vehicleBanner, funnelSubHeader, + replaceOptionsQuestion, + damageLocationQuestion, }, }; diff --git a/src/layouts/vehicle-make/make-question/make-question.vue b/src/layouts/vehicle-make/make-question/make-question.vue index 2a94deafe..3bda78285 100644 --- a/src/layouts/vehicle-make/make-question/make-question.vue +++ b/src/layouts/vehicle-make/make-question/make-question.vue @@ -8,6 +8,7 @@ textPosition="text-start" :loaderEnabled="true" v-model="modelValue" + isRequired=true /> @@ -27,7 +28,7 @@ export default { }; }, props: { - modelValue: String, + modelValue: Array, }, components: { buttonQuestion, diff --git a/src/layouts/vehicle-make/vehicle-make.spec.js b/src/layouts/vehicle-make/vehicle-make.spec.js index 146dd6318..b2c4dcaf7 100644 --- a/src/layouts/vehicle-make/vehicle-make.spec.js +++ b/src/layouts/vehicle-make/vehicle-make.spec.js @@ -1,15 +1,23 @@ -import { shallowMount, flushPromises } from "@vue/test-utils"; +// Supporting Files +import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import { settleAllPromises } from "@/helpers/layout-helper.js"; +import { nextTick } from "vue"; +import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; +import { storeMutations } from "@/constants/store-mutations"; +import { storeActions } from "@/constants/store-actions"; + +// Components import vehicleMake from "@/layouts/vehicle-make/vehicle-make.vue"; import makeQuestion from "@/layouts/vehicle-make/make-question/make-question"; import funnelHeader from "@/common-components/funnel-header/funnel-header"; import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; -import { settleAllPromises } from "@/helpers/layout-helper.js"; -import { nextTick } from "vue"; -import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; +import store from "@/store"; jest.mock("@/store", () => ({ + commit: jest.fn(), + dispatch: jest.fn(), getters: { vehicle: { year: 2019, @@ -27,9 +35,6 @@ jest.mock("@/helpers/layout-helper.js", () => ({ settleAllPromises: jest.fn(), })); -beforeEach(() => { - jest.resetModules(); -}); describe("vehicle-make.vue", () => { test("Make question component is initized with api data", async (done) => { @@ -190,13 +195,40 @@ describe("vehicle-make.vue", () => { ); let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - await nextTick(); //Assert expect(arePagePrerequisitesValid).toBe(true); }); }); +describe("vehicle-make.vue", () => { + test("Year set, call invalidation, model, style, carId, category should be null", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleMake.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-make" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.resetDependentState(); + + //Assert + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_MODEL, null) + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_STYLE, null) + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null) + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null) + + expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_AND_DEPS) + expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_AND_DEPS) + + }); +}); + function setupMocks({ vehicleMakeQuestionCmsContent = {}, makeQuestionInitialData = {}, diff --git a/src/layouts/vehicle-make/vehicle-make.vue b/src/layouts/vehicle-make/vehicle-make.vue index bd1621e6c..a4d76fa85 100644 --- a/src/layouts/vehicle-make/vehicle-make.vue +++ b/src/layouts/vehicle-make/vehicle-make.vue @@ -25,12 +25,15 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he // Supporting files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; +import { storeMutations } from "@/constants/store-mutations"; +import { storeActions } from "@/constants/store-actions"; import store from "@/store"; + export default { name: "vehicle-make", data() { return { - selectedMake: null, + selectedMake: [], }; }, computed: {}, @@ -75,17 +78,31 @@ export default { methods: { backButtonAction() { // route to move backwards - this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); + this.$router.navigate( + this.navigationScenarios.CLICKED_BACK, + this.$route + ); }, arePagePrerequisitesValid() { return store.getters.vehicle.year !== null; }, + resetDependentState() { + // Set + store.commit(storeMutations.UPDATE_MODEL, null); + store.commit(storeMutations.UPDATE_STYLE, null); + store.commit(storeMutations.UPDATE_CAR_ID, null); + store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + + // Invokes + store.dispatch(storeActions.RESET_DAMAGE_AND_DEPS); + store.dispatch(storeActions.RESET_REGISTRATION_AND_DEPS); + }, }, watch: { selectedMake(make) { this.$store.commit(this.storeMutations.UPDATE_MAKE, make); - this.$router.navigate( + this.$router.navigateAfterSave( this.navigationScenarios.SELECTED_MAKE, this.$route ); diff --git a/src/layouts/vehicle-model/model-question/model-question.vue b/src/layouts/vehicle-model/model-question/model-question.vue index 34a811e27..be2c64be5 100644 --- a/src/layouts/vehicle-model/model-question/model-question.vue +++ b/src/layouts/vehicle-model/model-question/model-question.vue @@ -8,6 +8,7 @@ textPosition="text-start" :loaderEnabled="true" v-model="modelValue" + isRequired=true /> @@ -27,7 +28,7 @@ export default { }; }, props: { - modelValue: String, + modelValue: Array, }, components: { buttonQuestion, diff --git a/src/layouts/vehicle-model/vehicle-model.spec.js b/src/layouts/vehicle-model/vehicle-model.spec.js index 4c61215ba..7f8f96eb6 100644 --- a/src/layouts/vehicle-model/vehicle-model.spec.js +++ b/src/layouts/vehicle-model/vehicle-model.spec.js @@ -7,10 +7,13 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he // Supporting files import { settleAllPromises } from "@/helpers/layout-helper.js"; -import { shallowMount, flushPromises } from "@vue/test-utils"; +import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { nextTick } from "vue"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; +import { storeMutations } from "@/constants/store-mutations"; +import { storeActions } from "@/constants/store-actions"; +import store from "@/store"; // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ @@ -24,6 +27,8 @@ jest.mock("@/helpers/cms-content-helper", () => ({ // Mock Store jest.mock("@/store", () => ({ + commit: jest.fn(), + dispatch: jest.fn(), getters: { vehicle: { make: "Acura", @@ -182,6 +187,33 @@ describe("vehicle-model.vue", () => { }); }); +describe("vehicle-model.vue", () => { + test("Year set, call invalidation, style, carId, category should be null", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleModel.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-model" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.resetDependentState(); + + //Assert + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_STYLE, null) + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null) + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null) + + expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_AND_DEPS) + expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_AND_DEPS) + + }); +}); + function setupMocks({ buttonQuestionContent = {}, modelQuestionInitialData = {}, diff --git a/src/layouts/vehicle-model/vehicle-model.vue b/src/layouts/vehicle-model/vehicle-model.vue index dc45c3bb7..3d52404bc 100644 --- a/src/layouts/vehicle-model/vehicle-model.vue +++ b/src/layouts/vehicle-model/vehicle-model.vue @@ -24,6 +24,8 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; // Supporting files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; +import { storeMutations } from "@/constants/store-mutations"; +import { storeActions } from "@/constants/store-actions"; import { settleAllPromises } from "@/helpers/layout-helper"; import store from "@/store"; @@ -31,7 +33,7 @@ export default { name: "vehicle-model", data() { return { - selectedModel: null, + selectedModel: [], }; }, computed: {}, @@ -77,17 +79,30 @@ export default { methods: { backButtonAction() { // route to move backwards - this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); + this.$router.navigate( + this.navigationScenarios.CLICKED_BACK, + this.$route + ); }, arePagePrerequisitesValid() { return store.getters.vehicle.make !== null; }, + resetDependentState() { + // Set + store.commit(storeMutations.UPDATE_STYLE, null); + store.commit(storeMutations.UPDATE_CAR_ID, null); + store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + + // Invokes + store.dispatch(storeActions.RESET_DAMAGE_AND_DEPS); + store.dispatch(storeActions.RESET_REGISTRATION_AND_DEPS); + }, }, watch: { selectedModel(model) { this.$store.commit(this.storeMutations.UPDATE_MODEL, model); - this.$router.navigate( + this.$router.navigateAfterSave( this.navigationScenarios.SELECTED_MODEL, this.$route ); diff --git a/src/layouts/vehicle-style/style-question/style-question.vue b/src/layouts/vehicle-style/style-question/style-question.vue index f82ac6981..e44514904 100644 --- a/src/layouts/vehicle-style/style-question/style-question.vue +++ b/src/layouts/vehicle-style/style-question/style-question.vue @@ -8,6 +8,7 @@ textPosition="text-start" :loaderEnabled="true" v-model="modelValue" + isRequired=true /> @@ -27,7 +28,7 @@ export default { }; }, props: { - modelValue: String, + modelValue: Array, }, components: { buttonQuestion, diff --git a/src/layouts/vehicle-style/vehicle-style.vue b/src/layouts/vehicle-style/vehicle-style.vue index e654ffff1..912264ffc 100644 --- a/src/layouts/vehicle-style/vehicle-style.vue +++ b/src/layouts/vehicle-style/vehicle-style.vue @@ -25,13 +25,14 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; // Supporting files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; +import { storeActions } from "@/constants/store-actions"; import store from "@/store"; export default { name: "vehicle-style", data() { return { - selectedStyle: null, + selectedStyle: [], }; }, computed: {}, @@ -77,7 +78,10 @@ export default { methods: { backButtonAction() { // route to move backwards - this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); + this.$router.navigate( + this.navigationScenarios.CLICKED_BACK, + this.$route + ); }, setVehicle() { return this.dispatchNonBlockingStoreAction( @@ -93,13 +97,18 @@ export default { arePagePrerequisitesValid() { return store.getters.vehicle.model !== null; }, + resetDependentState() { + // Invokes + store.dispatch(storeActions.RESET_DAMAGE_AND_DEPS); + store.dispatch(storeActions.RESET_REGISTRATION_AND_DEPS); + }, }, watch: { selectedStyle(style) { this.$store.commit(this.storeMutations.UPDATE_STYLE, style); this.setVehicle().then(() => { - this.$router.navigate( + this.$router.navigateAfterSave( this.navigationScenarios.SELECTED_STYLE, this.$route ); diff --git a/src/layouts/vehicle-year/vehicle-year.spec.js b/src/layouts/vehicle-year/vehicle-year.spec.js index 9b27e6555..5ef3ea404 100644 --- a/src/layouts/vehicle-year/vehicle-year.spec.js +++ b/src/layouts/vehicle-year/vehicle-year.spec.js @@ -1,18 +1,35 @@ import { shallowMount, flushPromises } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import { settleAllPromises } from "@/helpers/layout-helper.js"; +import { nextTick } from "vue"; +import { storeMutations } from "@/constants/store-mutations"; +import { storeActions } from "@/constants/store-actions"; +import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; + import vehicleYear from "@/layouts/vehicle-year/vehicle-year.vue"; import yearQuestion from "@/layouts/vehicle-year/year-question/year-question"; import funnelHeader from "@/common-components/funnel-header/funnel-header"; import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; -import { settleAllPromises } from "@/helpers/layout-helper.js"; -import { nextTick } from "vue"; + +import store from "@/store"; + +jest.mock("@/store", () => ({ + commit: jest.fn(), + dispatch: jest.fn(), +})); // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ settleAllPromises: jest.fn(), })); +// Mock fetchCmsContentForPage +jest.mock("@/helpers/cms-content-helper", () => ({ + fetchCmsContentForPage: jest.fn(), +})); + + describe("vehicle-year.vue", () => { test("Year question component is initized with api data", async (done) => { //Arrange @@ -123,6 +140,34 @@ describe("vehicle-year.vue", () => { }); }); +describe("vehicle-year.vue", () => { + test("Year set, call invalidation, make, model, style, carId, category should be null", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleYear.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-year" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.resetDependentState(); + + //Assert + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_MAKE, null) + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_MODEL, null) + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_STYLE, null) + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null) + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null) + + expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_AND_DEPS) + expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_AND_DEPS) + + }); +}); function setupMocks({ vehicleYearQuestionCmsContent = {}, yearQuestionInitialData = {}, @@ -146,7 +191,9 @@ function setupMocks({ yearQuestionInitialData: yearQuestionInitialData, }; const apiPromise = Promise.resolve(apiResponses); + settleAllPromises.mockImplementation(() => apiPromise); + fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); //Mock year question methods yearQuestion.methods = { @@ -162,6 +209,7 @@ function setupMocks({ funnelSubHeader.methods = { initializeComponent: jest.fn(), }; + const mountOptions = getMountOptions(mountOptionsMockData); const wrapper = shallowMount(vehicleYear, mountOptions); const yearQuestionWrapper = wrapper.findComponent({ name: "yearQuestion" }); diff --git a/src/layouts/vehicle-year/vehicle-year.vue b/src/layouts/vehicle-year/vehicle-year.vue index 257c866d7..4756fad3c 100644 --- a/src/layouts/vehicle-year/vehicle-year.vue +++ b/src/layouts/vehicle-year/vehicle-year.vue @@ -22,13 +22,15 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he // Supporting files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; -import eventBus from "@/helpers/event-bus/event-bus"; +import { storeMutations } from "@/constants/store-mutations"; +import { storeActions } from "@/constants/store-actions"; +import store from "@/store"; export default { name: "vehicle-year", data() { return { - selectedYear: null, + selectedYear: [], }; }, computed: {}, @@ -73,8 +75,9 @@ export default { watch: { selectedYear(year) { + this.$store.commit(this.storeMutations.UPDATE_YEAR, year); - this.$router.navigate( + this.$router.navigateAfterSave( this.navigationScenarios.SELECTED_YEAR, this.$route ); @@ -84,6 +87,19 @@ export default { arePagePrerequisitesValid() { return true; }, + resetDependentState() { + + // Set + store.commit(storeMutations.UPDATE_MAKE, null); + store.commit(storeMutations.UPDATE_MODEL, null); + store.commit(storeMutations.UPDATE_STYLE, null); + store.commit(storeMutations.UPDATE_CAR_ID, null); + store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + + // Invokes + store.dispatch(storeActions.RESET_DAMAGE_AND_DEPS); + store.dispatch(storeActions.RESET_REGISTRATION_AND_DEPS); + } }, components: { yearQuestion, diff --git a/src/layouts/vehicle-year/year-question/year-question.vue b/src/layouts/vehicle-year/year-question/year-question.vue index 7ee281c06..36af97bca 100644 --- a/src/layouts/vehicle-year/year-question/year-question.vue +++ b/src/layouts/vehicle-year/year-question/year-question.vue @@ -26,8 +26,9 @@ export default { }; }, props: { - modelValue: String, + modelValue: Array, }, + emits: ['update:modelValue'], components: { buttonQuestion, }, diff --git a/src/router/index.js b/src/router/index.js index eb4942d6a..02859e660 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -45,16 +45,16 @@ const routes = [ // If we already have our route, go to it. if (router.hasRoute(to.query.fmgPage)) { // Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function. - const arePagePrerequisitesValid = router + const component = router .getRoutes() - .filter((x) => x.name === to.query.fmgPage)[0] - .components.default.methods.arePagePrerequisitesValid(); + .filter((x) => x.name === to.query.fmgPage)[0].components; - if (!arePagePrerequisitesValid) { + if (!arePagePrerequisitesValid(component)) { await GoToFunnelStartOn404(next); } - return next({ name: to.query.fmgPage, query: to.query, params: to.params }); + + return next({ name: to.query.fmgPage, query: to.query, params: to.params }); } // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms. @@ -74,7 +74,7 @@ const routes = [ .filter((x) => x.name === routeData[0].name)[0] .components.default(); - if (!nextComponent.default.methods.arePagePrerequisitesValid()) { + if (!arePagePrerequisitesValid(nextComponent)) { await GoToFunnelStartOn404(next); } @@ -102,23 +102,36 @@ const router = createRouter({ //---------------------------------------------------------- Router Functions ---------------------------------------------------------- +router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}) => { + navigate(scenario, currentRoute, false, optionalQuery, optionalParams); +} + +router.navigateAfterSave = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}) => { + navigate(scenario, currentRoute, true, optionalQuery, optionalParams); +} + +// PRIVATE FUNCTIONS + // Navigate to the next route, depending on the scenario. -router.navigate = ( - scenario, - currentRoute, - optionalQuery = {}, - optionalParams = {} -) => { +function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {}, optionalParams = {}) { if (!scenario) { console.error("No scenario provided. Please review the routing table."); return; } // Match our maps up and navigate if we have a destination. - const matchingScenarioMap = router.getNavigationMap(scenario, currentRoute); + const matchingScenarioMap = getNavigationMap(scenario, currentRoute); if (matchingScenarioMap.destinationFmgPageValue !== undefined) { // We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one. + + // If we need to do invalidation + const currentComponent = currentRoute.matched[0].components; + + if (invalidateOnSave) { + resetDependentState(currentComponent); + } + router.push({ name: "root", query: Object.assign(optionalQuery, { @@ -132,7 +145,7 @@ router.navigate = ( }; // Get navigation map depending on the scenario and the current 'page' you're on. -router.getNavigationMap = (scenario, currentRoute) => { +function getNavigationMap(scenario, currentRoute) { const fmgPageValue = currentRoute.query.fmgPage; const matchedQueryValue = routingTable .filter( @@ -197,4 +210,14 @@ async function GoToFunnelStartOn404(next) { }); } +// Checks arePagePrerequisitesValid on the component passed in. +function arePagePrerequisitesValid(component) { + return component.default.methods.arePagePrerequisitesValid(); +} + +// Reset dependant state on route change. +function resetDependentState(component) { + return component.default.methods.resetDependentState(); +} + export default router; diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index 9f6fa4e92..48e36996b 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -3,7 +3,6 @@ const navigationScenarios = { SELECTED_MODEL: "SELECTED_MODEL", SELECTED_MAKE: "SELECTED_MAKE", SELECTED_STYLE: "SELECTED_STYLE", - VEHICLE_DAMAGE: "VEHICLE_DAMAGE", CLICKED_BACK: "CLICKED_BACK", }; diff --git a/src/store/index.js b/src/store/index.js index 64a06758f..b4177f8d8 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -3,7 +3,231 @@ import { endpoints } from "@/constants/endpoints.js"; import { storeMutations } from "@/constants/store-mutations"; import createPersistedState from "vuex-persistedstate"; import globalMethods from "@/global-methods"; -import eventBus from "../helpers/event-bus/event-bus"; + + + +// Export State +export const state = { + order: { + vehicle: { + year: null, + make: null, + model: null, + style: null, + carId: null, + category: null, + imageUrl: null, + imageVifNumber: null, + imageColor: null, + }, + damage: { + isRepair: null, + numberOfChips: null, + glassToReplace: null, + }, + }, + applicationUser: { + eventBus: [], + }, +} + +// Export Mutations +export const mutations = { + // VEHICLE MUTATIONS + updateYear(state, year) { + state.order.vehicle.year = year; + }, + updateMake(state, make) { + state.order.vehicle.make = make; + }, + updateModel(state, model) { + state.order.vehicle.model = model; + }, + updateStyle(state, style) { + state.order.vehicle.style = style; + }, + updateCarId(state, carId) { + state.order.vehicle.carId = carId; + }, + updateVehicleCategory(state, category) { + state.order.vehicle.category = category; + }, + + // EVENT BUS MUTATIONS + addEventToBus(state, event) { + state.applicationUser.eventBus.push(event); + }, + removeEventFromBus(state, eventData) { + const matchedEvent = state.applicationUser.eventBus.find( + ({ category, subCategory }) => + category === eventData.category && + subCategory === eventData.subCategory + ); + const itemIndex = state.applicationUser.eventBus.indexOf(matchedEvent); + + // If the item exists, remove it. + if (itemIndex > -1) { + state.applicationUser.eventBus.splice(itemIndex, 1); + } + }, + + // DEPENDENCY MUTATIONS + resetVehicleAndDependencies(state) { + state.order.vehicle.year = null; + state.order.vehicle.make = null; + state.order.vehicle.model = null; + state.order.vehicle.style = null; + state.order.vehicle.carId = null; + state.order.vehicle.category = null; + }, + resetDamageAndDependencies(state) { + state.order.damage.isRepair = null; + state.order.damage.numberOfChips = null; + state.order.damage.windshieldGlassToReplace = null; + state.order.damage.driverSideGlassToReplace = null; + state.order.damage.passengerSideGlassToReplace = null; + state.order.damage.rearGlassToReplace = null; + + }, + resetRegistrationAndDependencies(state) { + + }, + resetPartsAndDependencies(state) { + + } +} + +// Export Getters +export const getters = { + vehicle: (state) => state.order.vehicle, + eventBusItem: (state) => (eventCategory, eventSubCategory) => { + const matchedEvent = state.applicationUser.eventBus.find( + ({ category, subCategory }) => + category === eventCategory && subCategory === eventSubCategory + ); + + return matchedEvent !== undefined ? matchedEvent.eventValue : undefined; + }, + eventBus: (state) => state.applicationUser.eventBus, +} + +// Export Actions +export const actions = { + // Vehicle API Actions + getVehicleYears(context) { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleYears.method, + endpoint: endpoints.GetVehicleYears.url, + payload: {}, + }); + }, + lookupVehicleByYmms(context, { year, make, model, style }) { + return globalMethods.callHttpClient({ + method: endpoints.LookupVehicleByYmms.method, + endpoint: `${endpoints.LookupVehicleByYmms.url}/${year}/${make}/${model}/${style}`, + payload: {}, + }); + }, + lookupVehicleByVin(context, { vin }) { + return globalMethods.callHttpClient({ + method: endpoints.LookupVehicleByVin.method, + endpoint: endpoints.LookupVehicleByVin.url, + payload: { + vin: vin, // EX "1J4GW58S4XC541166" + }, + }); + }, + getVehicleMakes(context, { year }) { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleMakes.method, + endpoint: `${endpoints.GetVehicleMakes.url}/${year}`, + payload: {}, + }); + }, + getVehicleModels(context, { year, make }) { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleModels.method, + endpoint: `${endpoints.GetVehicleModels.url}/${year}/${make}`, + payload: {}, + }); + }, + getVehicleStyles(context, { year, make, model }) { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleStyles.method, + endpoint: `${endpoints.GetVehicleStyles.url}/${year}/${make}/${model}`, + payload: {}, + }); + }, + setVehicle(context, { year, make, model, style }) { + return globalMethods + .callHttpClient({ + methods: endpoints.GetVehicle.method, + endpoint: `${endpoints.GetVehicle.url}/${year}/${make}/${model}/${style}`, + payload: {}, + }) + .then((response) => { + context.commit(storeMutations.UPDATE_CAR_ID, response.data.carId); + context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, response.data.category); + return response; + }); + }, + getDamageOptions(context, { carId }) { + return globalMethods.callHttpClient({ + methods: endpoints.GetDamageOptions.method, + endpoint: `${endpoints.GetDamageOptions.url}/${carId}`, + payload: {}, + }); + }, + + // DEPENDENCY ACTIONS + resetVehicleAndDependencies(context) { + context.commit(storeMutations.RESET_VEHICLE_AND_DEPS); + context.commit(storeMutations.RESET_DAMAGE_AND_DEPS); + context.commit(storeMutations.RESET_REGISTRATION_AND_DEPS); + }, + resetDamageAndDependencies(context) { + context.commit(storeMutations.RESET_DAMAGE_AND_DEPS); + context.commit(storeMutations.RESET_PARTS_AND_DEPS); + }, + resetRegistrationAndDependencies(context) { + context.commit(storeMutations.RESET_REGISTRATION_AND_DEPS); + context.commit(storeMutations.RESET_PARTS_AND_DEPS) + }, + resetPartsAndDependencies(context) { + context.commit(storeMutations.RESET_PARTS_AND_DEPS); + }, + + // Content API Actions + getRouteInfo(context, { pageName }) { + return globalMethods.callHttpClient({ + method: endpoints.GetRouteInfo.method, + endpoint: endpoints.GetRouteInfo.url, + payload: { + pageName: pageName, + }, + }); + }, + getHomepageName(context) { + return globalMethods.callHttpClient({ + method: endpoints.GetHomepageInfo.method, + endpoint: endpoints.GetHomepageInfo.url, + }); + }, + getPageData(context, { pageName }) { + return globalMethods.callHttpClient({ + method: endpoints.GetPageData.method, + endpoint: `${endpoints.GetPageData.url}/${pageName}`, + payload: {}, + }); + }, + getEvoxImage(context, { relativeUrl }) { + return globalMethods.callHttpClient({ + method: endpoints.GetPageData.method, + endpoint: relativeUrl, + payload: {}, + }); + }, +} export default createStore({ plugins: [createPersistedState()], @@ -12,175 +236,8 @@ export default createStore({ // * The CMS can reference the fields by name // * Return users may have a previous "version" of the model, and we don't want // them to have a breaking experience, because the model might have changed. - state: { - order: { - vehicle: { - year: null, - make: null, - model: null, - style: null, - carId: null, - category: null, - imageUrl: null, - imageVifNumber: null, - imageColor: null, - damage: { - isRepair: null, - numberOfChips: null, - glassToReplace: null, - }, - }, - }, - applicationUser: { - eventBus: [], - }, - }, - // See IMPORTANT note at top of "state" declaration. - - mutations: { - updateYear(state, year) { - state.order.vehicle.year = year; - }, - updateMake(state, make) { - state.order.vehicle.make = make; - }, - updateModel(state, model) { - state.order.vehicle.model = model; - }, - updateStyle(state, style) { - state.order.vehicle.style = style; - }, - updateVehicle(state, data) { - state.order.vehicle.carId = data.carId; - state.order.vehicle.category = data.category; - state.order.vehicle.imageUrl = data.imageUrl; - state.order.vehicle.imageVifNumber = data.imageVifNumber; - state.order.vehicle.imageColor = data.imageVifColor; - }, - addEventToBus(state, event) { - state.applicationUser.eventBus.push(event); - }, - removeEventFromBus(state, eventData) { - const matchedEvent = state.applicationUser.eventBus.find( - ({ category, subCategory }) => - category === eventData.category && - subCategory === eventData.subCategory - ); - const itemIndex = state.applicationUser.eventBus.indexOf(matchedEvent); - - // If the item exists, remove it. - if (itemIndex > -1) { - state.applicationUser.eventBus.splice(itemIndex, 1); - } - }, - }, - getters: { - vehicle: (state) => state.order.vehicle, - eventBusItem: (state) => (eventCategory, eventSubCategory) => { - const matchedEvent = state.applicationUser.eventBus.find( - ({ category, subCategory }) => - category === eventCategory && subCategory === eventSubCategory - ); - - return matchedEvent !== undefined ? matchedEvent.eventValue : undefined; - }, - eventBus: (state) => state.applicationUser.eventBus, - }, - actions: { - // Vehicle API Actions - getVehicleYears(context) { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleYears.method, - endpoint: endpoints.GetVehicleYears.url, - payload: {}, - }); - }, - lookupVehicleByYmms(context, { year, make, model, style }) { - return globalMethods.callHttpClient({ - method: endpoints.LookupVehicleByYmms.method, - endpoint: `${endpoints.LookupVehicleByYmms.url}/${year}/${make}/${model}/${style}`, - payload: {}, - }); - }, - lookupVehicleByVin(context, { vin }) { - return globalMethods.callHttpClient({ - method: endpoints.LookupVehicleByVin.method, - endpoint: endpoints.LookupVehicleByVin.url, - payload: { - vin: vin, // EX "1J4GW58S4XC541166" - }, - }); - }, - getVehicleMakes(context, { year }) { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleMakes.method, - endpoint: `${endpoints.GetVehicleMakes.url}/${year}`, - payload: {}, - }); - }, - getVehicleModels(context, { year, make }) { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleModels.method, - endpoint: `${endpoints.GetVehicleModels.url}/${year}/${make}`, - payload: {}, - }); - }, - getVehicleStyles(context, { year, make, model }) { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleStyles.method, - endpoint: `${endpoints.GetVehicleStyles.url}/${year}/${make}/${model}`, - payload: {}, - }); - }, - setVehicle(context, { year, make, model, style }) { - return globalMethods - .callHttpClient({ - methods: endpoints.GetVehicle.method, - endpoint: `${endpoints.GetVehicle.url}/${year}/${make}/${model}/${style}`, - payload: {}, - }) - .then((response) => { - context.commit(storeMutations.UPDATE_VEHICLE, response.data); - return response; - }); - }, - getDamageOptions(context, { carId }) { - return globalMethods.callHttpClient({ - methods: endpoints.GetDamageOptions.method, - endpoint: `${endpoints.GetDamageOptions.url}/${carId}`, - payload: {}, - }); - }, - - // Content API Actions - getRouteInfo(context, { pageName }) { - return globalMethods.callHttpClient({ - method: endpoints.GetRouteInfo.method, - endpoint: endpoints.GetRouteInfo.url, - payload: { - pageName: pageName, - }, - }); - }, - getHomepageName(context) { - return globalMethods.callHttpClient({ - method: endpoints.GetHomepageInfo.method, - endpoint: endpoints.GetHomepageInfo.url, - }); - }, - getPageData(context, { pageName }) { - return globalMethods.callHttpClient({ - method: endpoints.GetPageData.method, - endpoint: `${endpoints.GetPageData.url}/${pageName}`, - payload: {}, - }); - }, - getEvoxImage(context, { relativeUrl }) { - return globalMethods.callMockHttpClient({ - method: endpoints.GetPageData.method, - endpoint: relativeUrl, - payload: {}, - }); - }, - }, + state, + mutations, + getters, + actions, }); diff --git a/src/store/store.spec.js b/src/store/store.spec.js index e71f07909..f33595e58 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -1,328 +1,483 @@ -import store from "./index"; import globalMethods from "@/global-methods"; +import { mutations, state, actions, getters } from "@/store"; +import { storeMutations } from "@/constants/store-mutations"; -describe("Actions", () => { - it("Should return list of years retrieved", async () => { +// Mock global method +globalMethods.callHttpClient = jest.fn(); + + +describe("Mutations", () => { + + it("Updates vehicle year in state", () => { // Arrange - let years = []; + const storeState = state; + + // Act + mutations.updateYear(storeState, "2019"); + + // Assert + expect(storeState.order.vehicle.year).toEqual("2019"); + }); + + it("Updates vehicle make in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateMake(storeState, "Acura"); + + // Assert + expect(storeState.order.vehicle.make).toEqual("Acura"); + }); + + it("Updates vehicle model in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateModel(storeState, "ILX"); + + // Assert + expect(storeState.order.vehicle.model).toEqual("ILX"); + }); + + it("Updates vehicle style in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateStyle(storeState, "4 DOOR SEDAN"); + + // Assert + expect(storeState.order.vehicle.style).toEqual("4 DOOR SEDAN"); + }); + + it("Updates vehicle carId in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateCarId(storeState, "C0000001"); + + // Assert + expect(storeState.order.vehicle.carId).toEqual("C0000001"); + }); + + it("Updates vehicle vehicle category in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateVehicleCategory(storeState, "CAR"); + + // Assert + expect(storeState.order.vehicle.category).toEqual("CAR"); + }); + + it("Remove item to eventBus in state", () => { + // Arrange + const storeState = state; + const event = { category: "CategoryOne", subCategory: "SubCategoryOne" } + + // Act / Assert + mutations.addEventToBus(storeState, event); + expect(storeState.applicationUser.eventBus).toEqual([event]); + + // Act / Assert + mutations.removeEventFromBus(storeState, event); + expect(storeState.applicationUser.eventBus).toEqual([]); + + }); + + it("Adds item to eventBus in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.addEventToBus(storeState, { EventOne: "ValueOne" }); + + // Assert + expect(storeState.applicationUser.eventBus).toEqual([{ EventOne: "ValueOne" }]); + }); + + it("resetVehicleAndDependencies, should set fields to null", () => { + // Arrange + const storeState = state; + + mutations.updateYear(storeState, "2019"); + mutations.updateMake(storeState, "Acura"); + mutations.updateModel(storeState, "ILX"); + mutations.updateStyle(storeState, "4 DOOR SEDAN"); + mutations.updateCarId(storeState, "C0000001"); + mutations.updateVehicleCategory(storeState, "CAR"); + + // Expect + expect(storeState.order.vehicle.year).toEqual("2019"); + expect(storeState.order.vehicle.make).toEqual("Acura"); + expect(storeState.order.vehicle.model).toEqual("ILX"); + expect(storeState.order.vehicle.style).toEqual("4 DOOR SEDAN"); + expect(storeState.order.vehicle.carId).toEqual("C0000001"); + expect(storeState.order.vehicle.category).toEqual("CAR"); + + // Act + mutations.resetVehicleAndDependencies(storeState); + + // Expect + expect(storeState.order.vehicle.year).toEqual(null); + expect(storeState.order.vehicle.make).toEqual(null); + expect(storeState.order.vehicle.model).toEqual(null); + expect(storeState.order.vehicle.style).toEqual(null); + expect(storeState.order.vehicle.carId).toEqual(null); + expect(storeState.order.vehicle.category).toEqual(null); + + }); + + it("resetDamageAndDependencies, should set fields to null", () => { + // Arrange + const storeState = state; + + storeState.order.damage = { + isRepair: true, + numberOfChips: 2, + windshieldGlassToReplace: "Front", + driverSideGlassToReplace: "Rear", + passengerSideGlassToReplace: "Rear", + rearGlassToReplace: "Slider" + } + + // Expect + expect(storeState.order.damage.isRepair).toEqual(true); + expect(storeState.order.damage.numberOfChips).toEqual(2); + expect(storeState.order.damage.windshieldGlassToReplace).toEqual("Front"); + expect(storeState.order.damage.driverSideGlassToReplace).toEqual("Rear"); + expect(storeState.order.damage.passengerSideGlassToReplace).toEqual("Rear"); + expect(storeState.order.damage.rearGlassToReplace).toEqual("Slider"); + + // Act + mutations.resetDamageAndDependencies(storeState); + + // Expect + expect(storeState.order.damage.isRepair).toEqual(null); + expect(storeState.order.damage.numberOfChips).toEqual(null); + expect(storeState.order.damage.windshieldGlassToReplace).toEqual(null); + expect(storeState.order.damage.driverSideGlassToReplace).toEqual(null); + expect(storeState.order.damage.passengerSideGlassToReplace).toEqual(null); + expect(storeState.order.damage.rearGlassToReplace).toEqual(null); + + }); + +}); + +describe("Actions", () => { + it("getVehicleYears action, should return years array", async () => { + + // Arrange + const context = state; // Act - globalMethods.callHttpClient = jest.fn(); globalMethods.callHttpClient.mockImplementation(() => { return Promise.resolve({ data: [2023, 2022, 2021] }); }); - await store.dispatch("getVehicleYears").then((response) => { - years = response.data; - }); // Assert - expect(years[0]).toBe(2023); + const response = await actions.getVehicleYears(context) + + expect(response.data).toEqual([2023, 2022, 2021]); }); - it("Should return list of makes retrieved", async () => { + it("lookupVehicleByYmms action, should return car data", async () => { + // Arrange - let makes = []; + const context = state; // Act globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: ["Baic", "Honda", "Ford"] }); - }); - await store.dispatch("getVehicleMakes", { year: 2023 }).then((response) => { - makes = response.data; + return Promise.resolve({ data: { carId: "C00000001" } }); }); // Assert - expect(makes[0]).toBe("Baic"); + const response = await actions.lookupVehicleByYmms(context, "2019", "Acura", "ILX", "4 DOOR SEDAN") + + expect(response.data).toEqual({ carId: "C00000001" }); }); - it("Should return list of models retrieved", async () => { + it("lookupVehicleByVin action, should return car data", async () => { + // Arrange - let models = []; + const context = state; // Act globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: ["BJ40 (MEX)", "Civic", "Accord"] }); + return Promise.resolve({ data: { carId: "C00000001" } }); }); - await store - .dispatch("getVehicleModels", { year: 2023, make: "Baic" }) - .then((response) => { - models = response.data; - }); // Assert - expect(models[0]).toBe("BJ40 (MEX)"); + const response = await actions.lookupVehicleByVin(context, "12345678901234567") + + expect(response.data).toEqual({ carId: "C00000001" }); }); - it("Should return list of styles retrieved", async () => { + it("getVehicleMakes action, should return makes list", async () => { + // Arrange - let styles = []; + const context = state; // Act globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: ["4 DOOR UTILITY", "2 DOOR"] }); + return Promise.resolve({ data: ["Acura", "Honda"] }); }); - await store - .dispatch("getVehicleStyles", { - year: 2023, - make: "Baic", - model: "BJ40 (MEX)", - }) - .then((response) => { - styles = response.data; - }); // Assert - expect(styles[0]).toBe("4 DOOR UTILITY"); + const response = await actions.getVehicleMakes(context, "2019") + + expect(response.data).toEqual(["Acura", "Honda"]); }); - it("Should return list of damage options retrieved", async () => { + it("getVehicleModels action, should return models list", async () => { + // Arrange - let damageOptions = []; + const context = state; // Act globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: ["Front Window", "Rear Window"] }); + return Promise.resolve({ data: ["ILX", "RDX"] }); }); - await store - .dispatch("getDamageOptions", { - carId: "CR00070154", - }) - .then((response) => { - damageOptions = response.data; - }); // Assert - expect(damageOptions[0]).toBe("Front Window"); + const response = await actions.getVehicleModels(context, "2019", "Acura") + + expect(response.data).toEqual(["ILX", "RDX"]); }); - it("Should return data from url retrieved", async () => { + it("getVehicleStyles action, should return models list", async () => { + // Arrange - let routeInfo = []; + const context = state; // Act globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ - data: { - Result: "Route Info Data", - }, - }); + return Promise.resolve({ data: { style: "4 DOOR SEDAN" } }); }); - await store - .dispatch("getRouteInfo", { pageName: "vehicle-year" }) - .then((response) => { - routeInfo = response.data.Result; - }); // Assert - expect(routeInfo).toBe("Route Info Data"); + const response = await actions.getVehicleStyles(context, "2019", "Acura", "ILX") + + expect(response.data).toEqual({ style: "4 DOOR SEDAN" }); }); - it("Should return page data from url retrieved", async () => { + it("setVehicle action, should get vehicle data and set carId and vehicle category", async () => { + // Arrange - let pageData = []; + const context = state; + const commit = jest.fn(); + + context.commit = commit; // Act globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ - data: { - Result: "Page Info Data", - }, - }); + return Promise.resolve({ data: { carId: "C00000000", category: "CAR" } }); }); - await store - .dispatch("getPageData", { pageName: "vehicle-year" }) - .then((response) => { - pageData = response.data.Result; - }); // Assert - expect(pageData).toBe("Page Info Data"); + const response = await actions.setVehicle(context, "C00000000") + + expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, "C00000000"); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, "CAR"); + expect(response.data).toEqual({ carId: "C00000000", category: "CAR" }); }); - it("Should return data from url retrieved", async () => { + it("getDamageOptions action", async () => { + // Arrange - let returnData = []; + const context = state; // Act globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ - data: { - Result: "2018 Honda Civic", - }, - }); + return Promise.resolve({ data: ["Windshield", "DriversFrontDoor"] }); }); - await store - .dispatch("lookupVehicleByYmms", { - year: "2018", - make: "Honda", - model: "Civic", - style: "2 Door", - }) - .then((response) => { - returnData = response.data.Result; - }); + + const response = await actions.getDamageOptions(context, "C00000000") // Assert - expect(returnData).toBe("2018 Honda Civic"); + expect(response.data).toEqual(["Windshield", "DriversFrontDoor"]); }); - it("Should return vehicle data from url retrieved", async () => { + it("resetVehicleAndDependencies action", async () => { + // Arrange - let returnData = []; + const context = state; + const commit = jest.fn(); + + context.commit = commit; // Act + await actions.resetVehicleAndDependencies(context) + + expect(commit).toBeCalledWith(storeMutations.RESET_VEHICLE_AND_DEPS); + expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_AND_DEPS); + expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_AND_DEPS); + + }); + + it("resetDamageAndDependencies action", async () => { + + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + + // Act + await actions.resetDamageAndDependencies(context) + + expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_AND_DEPS); + expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_AND_DEPS); + + }); + + it("resetRegistrationAndDependencies action", async () => { + + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + + // Act + await actions.resetRegistrationAndDependencies(context) + + expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_AND_DEPS); + expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_AND_DEPS); + + }); + + it("resetPartsAndDependencies action", async () => { + + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + + // Act + await actions.resetPartsAndDependencies(context) + + expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_AND_DEPS); + + }); + + it("getRouteInfo action, returns route info", async () => { + + // Arrange + const context = state; + globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ - data: { - Result: "2021 Honda Civic", - }, - }); + return Promise.resolve({ data: { Widget: "Data" } }); }); - await store - .dispatch("lookupVehicleByVin", { vin: "12345678" }) - .then((response) => { - returnData = response.data.Result; - }); - - // Assert - expect(returnData).toBe("2021 Honda Civic"); - }); - - it("Should return vehicle image data from url retrieved", async () => { - // Arrange - let returnData = []; // Act - globalMethods.callMockHttpClient = jest.fn(); - globalMethods.callMockHttpClient.mockImplementation(() => { - return Promise.resolve({ - data: { - Result: "2008_honda_civic.jpg", - }, - }); + const response = await actions.getRouteInfo(context, "vehicle-year") + + + expect(response.data).toEqual({ Widget: "Data" }); + + }); + + it("getHomepageName action, returns homepage name", async () => { + + // Arrange + const context = state; + + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { Name: "vehicle-year" } }); }); - await store - .dispatch("getEvoxImage", { relativeUrl: "evox_image.com" }) - .then((response) => { - returnData = response.data.Result; - }); - // Assert - expect(returnData).toBe("2008_honda_civic.jpg"); - }); -}); - -describe("Mutations", () => { - it("Should update the year property in the store", () => { // Act - store.commit("updateYear", 2020); + const response = await actions.getHomepageName(context) + + + expect(response.data).toEqual({ Name: "vehicle-year" }); - // Assert - expect(store.state.order.vehicle.year).toBe(2020); }); - it("Should update the make property in the store", () => { - // Act - store.commit("updateMake", "Honda"); + it("getPageData action, returns page data", async () => { - // Assert - expect(store.state.order.vehicle.make).toBe("Honda"); - }); - - it("Should update the model property in the store", () => { - // Act - store.commit("updateModel", "Civic"); - - // Assert - expect(store.state.order.vehicle.model).toBe("Civic"); - }); - - it("Should update the style property in the store", () => { - // Act - store.commit("updateStyle", "2 Door"); - - // Assert - expect(store.state.order.vehicle.style).toBe("2 Door"); - }); - - it("Should update the carID and category propertys in the store", () => { // Arrange - const carData = { - carId: "123abc", - category: "car", - }; - // Act - store.commit("updateVehicle", carData); + const context = state; + + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { Results: [{ Widget: "Data" }] } }); + }); + + // Act + const response = await actions.getPageData(context, "vehicle-year") + + + expect(response.data).toEqual({ Results: [{ Widget: "Data" }] }); - // Assert - expect(store.state.order.vehicle.carId).toBe("123abc"); - expect(store.state.order.vehicle.category).toBe("car"); }); - it("Should add event onto bus and update state", () => { + it("getEvoxImage action, returns image url", async () => { // Arrange - const event = { - category: "TestCategoryOne", - subCategory: "TestSubCategoryOne", - eventValue: "TestEventValueOne", - }; + const context = state; + + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { imageUrl: "https://test.com" } }); + }); // Act - store.commit("addEventToBus", event); + const response = await actions.getEvoxImage(context, { relativeUrl: "https://relativeurl.com" }); - //Assert - expect(store.state.applicationUser.eventBus[0].category).toBe( - "TestCategoryOne" - ); - expect(store.state.applicationUser.eventBus[0].subCategory).toBe( - "TestSubCategoryOne" - ); - expect(store.state.applicationUser.eventBus[0].eventValue).toBe( - "TestEventValueOne" - ); + + expect(response.data).toEqual({ imageUrl: "https://test.com" }); }); + }); describe("Getters", () => { - it("Should validate vehicle getter", () => { + it("Vehicle getter, should return vehicle data", () => { // Arrange - const vehicle = store.getters.vehicle; - - // Assert - expect(typeof vehicle).toBe("object"); - }); - - it("Should get item from bus via getter", () => { - // Arrange - const event = { - category: "TestCategoryOne", - subCategory: "TestSubCategoryOne", - eventValue: "TestEventValueOne", - }; + const storeState = state; // Act - store.commit("addEventToBus", event); + mutations.updateYear(storeState, "2019"); + mutations.updateMake(storeState, "Acura"); + mutations.updateModel(storeState, "ILX"); // Assert - const returnedEventValue = store.getters.eventBusItem( - event.category, - event.subCategory - ); - expect(returnedEventValue).toBe("TestEventValueOne"); + expect(getters.vehicle(storeState).year).toEqual("2019"); + expect(getters.vehicle(storeState).make).toEqual("Acura"); + expect(getters.vehicle(storeState).model).toEqual("ILX"); + }); - it("Should get eventbus from getter, should have length > 0", () => { + it("Get event bus item by event category and eventSubCategory", () => { // Arrange - const event = { - category: "TestCategoryOne", - subCategory: "TestSubCategoryOne", - eventValue: "TestEventValueOne", - }; + const storeState = state; + const event = { category: "CategoryOne", subCategory: "SubCategoryOne", eventValue: "EventValueOne" }; // Act - store.commit("addEventToBus", event); + mutations.addEventToBus(storeState, event); + + // Assert + //expect(storeState.applicationUser.eventBus).toEqual([event]); + expect(getters.eventBusItem(storeState)(event.category, event.subCategory)).toEqual(event.eventValue); - //Assert - expect(store.getters.eventBus.length).toBeGreaterThan(0); }); -}); + + it("Get event bus", () => { + // Arrange + const storeState = state; + storeState.applicationUser.eventBus = []; + + const event = { category: "CategoryOne", subCategory: "SubCategoryOne", eventValue: "EventValueOne" }; + + // Act + mutations.addEventToBus(storeState, event); + + // Assert + expect(getters.eventBus(storeState)).toEqual([event]); + + }); + +}); \ 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 ff2a5f83a..c8888c310 100644 --- a/src/ux-components/button-main/button-main.vue +++ b/src/ux-components/button-main/button-main.vue @@ -1,11 +1,5 @@