diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index a3ed3ed2c..baee845e4 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -23,6 +23,10 @@ const endpoints = { url: "/vehicle/api/v1/vehicle/lookup", method: "GET", }, + GetDamageOptions: { + url: "/parts/api/v1/parts/damage-options", + method: "GET", + }, GetPageData: { url: "/content/api/v1/content", method: "GET", diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index b06b2f10b..c6f1a23a9 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -6,6 +6,7 @@ const storeActions = { GET_VEHICLE_MODELS: "getVehicleModels", GET_VEHICLE_STYLES: "getVehicleStyles", GET_VEHICLE: "getVehicle", + GET_DAMAGE_OPTIONS: "getDamageOptions", GET_EVOX_IMAGE: "getEvoxImage", LOOKUP_VEHICLE_BY_YMMS: "lookupVehicleByYmms", LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin", diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index d2ccd28f4..61882b691 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -3,6 +3,7 @@ const storeMutations = { UPDATE_MAKE: "updateMake", UPDATE_MODEL: "updateModel", UPDATE_STYLE: "updateStyle", + UPDATE_VEHICLE: "updateVehicle", }; export { storeMutations }; diff --git a/src/layouts/vehicle-damage/vehicle-damage.spec.js b/src/layouts/vehicle-damage/vehicle-damage.spec.js new file mode 100644 index 000000000..eb6e21268 --- /dev/null +++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js @@ -0,0 +1,123 @@ +import { shallowMount, flushPromises } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +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 { settleAllPromises } from "@/helpers/layout-helper.js"; +import { nextTick } from "vue"; + +// Mock our module for promises. +jest.mock("@/helpers/layout-helper.js", () => ({ + settleAllPromises: jest.fn(), +})); + +describe("vehicle-damage.vue", () => { + test("Page header is initailized with api data", async (done) => { + + //Arrange + const pageHeaderWidgetHeaderText = "Select Damage"; + const { wrapper, apiPromise } = setupMocks( { pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText }); + + //Act + vehicleDamage.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-damage" } }, undefined, (c) => c(wrapper.vm)); + + //Assert + apiPromise.finally(() => { + expect(funnelSubHeader.methods.initializeComponent).toHaveBeenCalledWith(pageHeaderWidgetHeaderText); + done(); + }); + }); +}); + +describe("vehicle-damage.vue", () => { + test("Page logo image is initailized with api data", async (done) => { + + //Arrange + const SiteHeaderWidget = { + LogoImage: + "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", + } + const { wrapper, apiPromise } = setupMocks( { SiteHeaderWidget: SiteHeaderWidget}); + + //Act + vehicleDamage.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-damage" } }, undefined, (c) => c(wrapper.vm)); + + //Assert + apiPromise.finally(() => { + expect(funnelHeader.methods.initializeComponent).toHaveBeenCalledWith(SiteHeaderWidget); + done(); + }); + }); + }); + + describe("vehicle-damage.vue", () => { + test("Vehicle image is initailized with api data", async (done) => { + + //Arrange + const VehicleBannerWidget = { + GenericVehicleImage: + "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", + } + const { wrapper, apiPromise } = setupMocks( { VehicleBannerWidget: VehicleBannerWidget}); + + //Act + vehicleDamage.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-damage" } }, undefined, (c) => c(wrapper.vm)); + + //Assert + apiPromise.finally(() => { + expect(vehicleBanner.methods.initializeComponent).toHaveBeenCalledWith(VehicleBannerWidget); + done(); + }); + }); + }); + +function setupMocks({ + radioQuestionCmsContent = {}, + pageHeaderWidgetHeaderText = {}, + mountOptionsMockData = {}, + }) { + + //Mock api responses + const apiResponses = { + cmsContent: { + FunnelSubHeaderWidget: [pageHeaderWidgetHeaderText], + RadioQuestionWidget: [radioQuestionCmsContent], + VehicleBannerWidget: [ + { + GenericVehicleImage: + "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", + }, + ], + FunnelHeaderWidget: [ + { + LogoImage: + "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", + }, + ], + }, + }; + const apiPromise = Promise.resolve(apiResponses); + settleAllPromises.mockImplementation(() => apiPromise); + + //Mock style question methods + funnelHeader.methods = { + initializeComponent: jest.fn(), + }; + vehicleBanner.methods = { + initializeComponent: jest.fn(), + }; + funnelSubHeader.methods = { + initializeComponent: jest.fn(), + }; + const mountOptions = getMountOptions(mountOptionsMockData); + const wrapper = shallowMount(vehicleDamage, mountOptions); + const funnelHeaderWrapper = wrapper.findComponent({ name: "funnelHeader" }); + funnelHeaderWrapper.vm.initializeComponent = funnelHeader.methods.initializeComponent; + const vehicleBannerWrapper = wrapper.findComponent({ name: "vehicleBanner" }); + vehicleBannerWrapper.vm.initializeComponent = vehicleBanner.methods.initializeComponent; + const funnelSubHeaderWrapper = wrapper.findComponent({ name: "funnelSubHeader" }); + funnelSubHeaderWrapper.vm.initializeComponent = funnelSubHeader.methods.initializeComponent; + + return { wrapper, apiPromise }; + } \ No newline at end of file diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index ca7d701f6..cb982b8b7 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -17,14 +17,38 @@ 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 store from "@/store"; +import baseMixin from "@/mixins/base-mixin"; +import { storeActions } from "@/constants/store-actions"; export default ({ name: "vehicle-damage", beforeRouteEnter(to, from, next) { // Call APIs const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); + const damageOptions = baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.GET_DAMAGE_OPTIONS, {carId: store.getters.vehicle.carId}); - + // Settle promises and get results + const promiseResultMap = [ + { + resultKey: "cmsContent", + promise: cmsContentPromise, + }, + { + resultKey: "damageOptions", + promise: damageOptions + }, + ]; + + settleAllPromises(promiseResultMap).then((resultMap) => { + // Call the "next" function to complete the transition to this page. + next((vm) => { + vm.$refs.funnelHeader.initializeComponent(resultMap.cmsContent.FunnelHeaderWidget[0]); + vm.$refs.vehicleBanner.initializeComponent(resultMap.cmsContent.VehicleBannerWidget[0]); + vm.$refs.funnelSubHeader.initializeComponent(resultMap.cmsContent.FunnelSubHeaderWidget[0]); + console.log(resultMap.damageOptions); + }); + }); }, components: { diff --git a/src/layouts/vehicle-style/vehicle-style.spec.js b/src/layouts/vehicle-style/vehicle-style.spec.js index 15264e53e..e63c03e91 100644 --- a/src/layouts/vehicle-style/vehicle-style.spec.js +++ b/src/layouts/vehicle-style/vehicle-style.spec.js @@ -121,6 +121,40 @@ describe("vehicle-style.vue", () => { }); }); +describe("vehicle-style.vue", () => { + test("selectVehicle triggers a store commit change", async (done) => { + + //Arrange + const { wrapper, apiPromise } = setupMocks( { + pageHeaderWidgetHeaderText: "Select a style to get started", + mountOptionsMockData: { + store: { + commit: jest.fn(), + getters: { + vehicle: { + year: 2020, + make: 'Honda', + model: 'Civic', + style: '2 Door', + } + } + } + }, + }); + + //Act + vehicleStyle.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-style" } }, undefined, (c) => c(wrapper.vm)); + wrapper.vm.selectVehicle(); + await nextTick(); + + //Assert + // apiPromise.finally(() => { + // expect(wrapper.vm.$store.commit).toHaveBeenCalled(); + // done(); + // }); + }); +}); + function setupMocks({ radioQuestionCmsContent = {}, styleQuestionInitialData = {}, diff --git a/src/layouts/vehicle-style/vehicle-style.vue b/src/layouts/vehicle-style/vehicle-style.vue index 3cd1766d3..e44ea5a11 100644 --- a/src/layouts/vehicle-style/vehicle-style.vue +++ b/src/layouts/vehicle-style/vehicle-style.vue @@ -67,12 +67,16 @@ export default { backButtonAction: function () { // route to move backwards this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); + }, + selectVehicle() { + this.dispatchNonBlockingStoreAction(this.storeActions.GET_VEHICLE, {year: this.$store.getters.vehicle.year, make: this.$store.getters.vehicle.make, model: this.$store.getters.vehicle.model, style: this.$store.getters.vehicle.style}) } }, watch: { selectedStyle(style) { this.$store.commit(this.storeMutations.UPDATE_STYLE, style); + this.selectVehicle(); this.$router.navigate(this.navigationScenarios.SELECTED_STYLE, this.$route); } }, diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index 51064576c..9f6fa4e92 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -3,7 +3,7 @@ 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/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 21dc6ca65..dc467edc2 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -42,7 +42,7 @@ const routingTable = [ maps: [ { scenario: navigationScenarios.SELECTED_STYLE, - destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, }, { scenario: navigationScenarios.CLICKED_BACK, diff --git a/src/store/index.js b/src/store/index.js index 275b21f2e..742e4be25 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,5 +1,6 @@ import { createStore } from "vuex"; import { endpoints } from "@/constants/endpoints.js"; +import { storeMutations } from "@/constants/store-mutations" import createPersistedState from "vuex-persistedstate"; import globalMethods from "@/global-methods"; @@ -75,8 +76,8 @@ export default createStore({ state.order.vehicle.style = style; }, updateVehicle(state, data) { - state.carId = data.carId; - state.category = data.category; + state.order.vehicle.carId = data.carId; + state.order.vehicle.category = data.category; } }, getters: { @@ -133,6 +134,16 @@ export default createStore({ 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: {}, }) }, diff --git a/src/store/store.spec.js b/src/store/store.spec.js index fcfca4576..68de66b5e 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -75,6 +75,26 @@ describe("Actions", () => { expect(styles[0]).toBe("4 DOOR UTILITY"); }); + it("Should return list of damage options retrieved", async () => { + // Arrange + let damageOptions = []; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: ["Front Window", "Rear Window"] }); + }); + await store + .dispatch("getDamageOptions", { + carId: "CR00070154", + }) + .then((response) => { + damageOptions = response.data; + }); + + // Assert + expect(damageOptions[0]).toBe("Front Window"); + }); + it("Should return data from url retrieved", async () => { // Arrange let routeInfo = []; @@ -219,6 +239,20 @@ describe("Mutations", () => { // 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); + + // Assert + expect(store.state.order.vehicle.carId).toBe("123abc"); + expect(store.state.order.vehicle.category).toBe("car"); + }); }); describe("Getters", () => { diff --git a/src/ux-components/button-back/button-back.vue b/src/ux-components/button-back/button-back.vue index 55dee6f8f..e25813bb2 100644 --- a/src/ux-components/button-back/button-back.vue +++ b/src/ux-components/button-back/button-back.vue @@ -2,7 +2,7 @@