diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 1cc1c1187..baee845e4 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -19,6 +19,14 @@ const endpoints = { url: "/vehicle/api/v1/vehicle/Styles", method: "GET", }, + GetVehicle: { + 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 db80d3d3f..c6f1a23a9 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -5,6 +5,8 @@ const storeActions = { GET_VEHICLE_MAKES: "getVehicleMakes", 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..a6688a9af --- /dev/null +++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js @@ -0,0 +1,125 @@ +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 baseMixin from "@/mixins/base-mixin"; +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({ + pageHeaderWidgetHeaderText = {}, + mountOptionsMockData = {}, + }) { + + //Mock api responses + baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn(); + baseMixin.methods.dispatchNonBlockingStoreAction.mockImplementation(() => { + return Promise.resolve({ data: ['Front Window', 'Back Window', 'Side Window'] }); + }); + const apiResponses = { + cmsContent: { + FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, + 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", + }, + }, + damageOptions: ['Front Window', 'Back Window', 'Side Window'], + }; + const apiPromise = Promise.resolve(apiResponses); + settleAllPromises.mockImplementation(() => apiPromise); + + //Mock damage initialize 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 new file mode 100644 index 000000000..abf24e998 --- /dev/null +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -0,0 +1,60 @@ + + + diff --git a/src/layouts/vehicle-style/vehicle-style.spec.js b/src/layouts/vehicle-style/vehicle-style.spec.js index 222daa50c..7d3bcaca7 100644 --- a/src/layouts/vehicle-style/vehicle-style.spec.js +++ b/src/layouts/vehicle-style/vehicle-style.spec.js @@ -1,5 +1,6 @@ import { shallowMount, flushPromises } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import { storeActions } from "@/constants/store-actions"; import vehicleStyle from "@/layouts/vehicle-style/vehicle-style.vue"; import funnelHeader from "@/common-components/funnel-header/funnel-header"; import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; @@ -121,6 +122,46 @@ describe("vehicle-style.vue", () => { }); }); +describe("vehicle-style.vue", () => { + test("selectVehicle triggers a dispatchNonBlockingStoreAction commit", 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', + } + } + }, + actionList: [ + { + actionName: storeActions.GET_VEHICLE, + data: 'mockData' + } + ] + }, + }); + + //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.dispatchNonBlockingStoreAction).toHaveBeenCalled(); + done(); + }); + }); +}); + function setupMocks({ vehicleStyleQuestionCmsContent = {}, styleQuestionInitialData = {}, diff --git a/src/layouts/vehicle-style/vehicle-style.vue b/src/layouts/vehicle-style/vehicle-style.vue index 9a1706b97..ff9fb86d3 100644 --- a/src/layouts/vehicle-style/vehicle-style.vue +++ b/src/layouts/vehicle-style/vehicle-style.vue @@ -67,12 +67,16 @@ export default { backButtonAction() { // 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/fmgPage-values.js b/src/router/router-constants/fmgPage-values.js index 38486e111..9b82cdf7e 100644 --- a/src/router/router-constants/fmgPage-values.js +++ b/src/router/router-constants/fmgPage-values.js @@ -3,6 +3,7 @@ const fmgPageValues = { VEHICLE_MAKE: "vehicle-make", VEHICLE_MODEL: "vehicle-model", VEHICLE_STYLE: "vehicle-style", + VEHICLE_DAMAGE: "vehicle-damage", }; export { fmgPageValues }; diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index 48e36996b..9f6fa4e92 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -3,6 +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 daf34e38c..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"; @@ -24,6 +25,7 @@ export default createStore({ vin: null, licensePlate: null, carId: null, + category: null, evoxImageId: null, hasSplitWindshieldOption: null, hasBackglassSliderOption: null, @@ -73,6 +75,10 @@ export default createStore({ updateStyle(state, style) { state.order.vehicle.style = style; }, + updateVehicle(state, data) { + state.order.vehicle.carId = data.carId; + state.order.vehicle.category = data.category; + } }, getters: { vehicle: state => state.order.vehicle @@ -123,6 +129,23 @@ export default createStore({ payload: {}, }); }, + getVehicle(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 }) { 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", () => {