diff --git a/jest.config.js b/jest.config.js index fd7713966..720237d10 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,7 +16,7 @@ module.exports = { testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { global: { - statements: 80, + statements: 85, }, }, }; diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index c6d8484da..d2ccd28f4 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -2,6 +2,7 @@ const storeMutations = { UPDATE_YEAR: "updateYear", UPDATE_MAKE: "updateMake", UPDATE_MODEL: "updateModel", + UPDATE_STYLE: "updateStyle", }; export { storeMutations }; diff --git a/src/layouts/vehicle-make/vehicle-make.spec.js b/src/layouts/vehicle-make/vehicle-make.spec.js index cc2325dfc..120f8de4f 100644 --- a/src/layouts/vehicle-make/vehicle-make.spec.js +++ b/src/layouts/vehicle-make/vehicle-make.spec.js @@ -44,7 +44,6 @@ describe("vehicle-make.vue", () => { //Assert const header = await wrapper.find(".Header"); apiPromise.finally(() => { - console.log(header) expect(header.attributes("text")).toEqual("Select a make to get started"); done(); }); diff --git a/src/layouts/vehicle-style/style-question/style-question.spec.js b/src/layouts/vehicle-style/style-question/style-question.spec.js new file mode 100644 index 000000000..f03ce5e96 --- /dev/null +++ b/src/layouts/vehicle-style/style-question/style-question.spec.js @@ -0,0 +1,82 @@ +import styleQuestion from "@/layouts/vehicle-style/style-question/style-question"; +import { shallowMount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import store from "@/store"; +jest.mock("@/store", () => { return {}; }, {virtual: true}); + +describe("style-question.vue", () => { + test("Selected style is emitted upon selection.", async () => { + + //Arrange + const { wrapper } = setupMocks({ modelValueProp: "2 Door" }); + const styleToSelect = "4 Door"; + + //Act + wrapper.setData({ selectedStyle: styleToSelect }); + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual(["4 Door"]); + }); +}); + +describe("style-question.vue", () => { + test("CMS question text is used as radio question text.", async () => { + + //Arrange + const { wrapper, cmsContent } = setupMocks({ cmsQuestionText: "What style is your vehicle?" }); + + //Act + styleQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, null); + + //Assert + const buttonQuestionComponent = await wrapper.findComponent({ name: "buttonQuestion" }); + expect(buttonQuestionComponent.attributes("questiontext")).toBe("What style is your vehicle?"); + }); +}); + +describe("style-question.vue", () => { + test("Data from store api are used as radio question answers.", async () => { + + //Arrange + const { wrapper, cmsContent } = setupMocks({ dataFromStoreApi: ["2 Door", "4 Door"] }); + + //Act + const initialData = styleQuestion.methods.loadInitialData.call(wrapper.vm); + styleQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, initialData); + + //Assert + const buttonQuestionComponent = await wrapper.findComponent({ name: "buttonQuestion" }); + expect(buttonQuestionComponent.attributes("answers")).toBe("2 Door,4 Door"); + }); +}); + + +function setupMocks({ + modelValueProp = "1900", + cmsQuestionText = "CMS text goes here", + dataFromStoreApi = [], +}) { + + //Mock store + store.dispatch = jest.fn(() => dataFromStoreApi); + store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc'} }; + const mountOptions = getMountOptions({ + store: { + dispatch: store.dispatch, + getters: store.getters, + }, + }); + + //Mock props + mountOptions.propsData = { + modelValue: modelValueProp, + }; + const wrapper = shallowMount(styleQuestion, mountOptions); + + //Mock CMS content + const cmsContent = { + QuestionText: cmsQuestionText + }; + return { wrapper, cmsContent }; +} \ No newline at end of file diff --git a/src/layouts/vehicle-style/style-question/style-question.vue b/src/layouts/vehicle-style/style-question/style-question.vue new file mode 100644 index 000000000..17a008473 --- /dev/null +++ b/src/layouts/vehicle-style/style-question/style-question.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/layouts/vehicle-style/vehicle-style.spec.js b/src/layouts/vehicle-style/vehicle-style.spec.js new file mode 100644 index 000000000..90dd6e926 --- /dev/null +++ b/src/layouts/vehicle-style/vehicle-style.spec.js @@ -0,0 +1,120 @@ +import { shallowMount, flushPromises } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import vehicleStyle from "@/layouts/vehicle-style/vehicle-style.vue"; +import styleQuestion from "@/layouts/vehicle-style/style-question/style-question"; +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-style.vue", () => { + test("Style question component is initized with api data", async (done) => { + + //Arrange + const radioQuestionCmsContent = { QuestionText: "What style is your vehicle?" }; + const styleQuestionInitialData = ["2 Door", "4 Door"]; + const { wrapper, apiPromise } = setupMocks( { + radioQuestionCmsContent: radioQuestionCmsContent, + styleQuestionInitialData: styleQuestionInitialData, + } ); + + //Act + vehicleStyle.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-style" } }, undefined, (c) => c(wrapper.vm)); + + //Assert + apiPromise.finally(() => { + expect(styleQuestion.methods.initializeComponent).toHaveBeenCalledWith(radioQuestionCmsContent, styleQuestionInitialData); + done(); + }); + }); +}); + +describe("vehicle-style.vue", () => { + test("Page header is passed data from CMS", async (done) => { + + //Arrange + const { wrapper, apiPromise } = setupMocks( { pageHeaderWidgetHeaderText: "Select a style to get started" }); + + //Act + vehicleStyle.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-style" } }, undefined, (c) => c(wrapper.vm)); + + //Assert + const header = await wrapper.find(".Header"); + apiPromise.finally(() => { + expect(header.attributes("text")).toEqual("Select a style to get started"); + done(); + }); + }); +}); + +describe("vehicle-style.vue", () => { + test("BackButtonAction triggers a router.navigate change", async (done) => { + + //Arrange + const { wrapper, apiPromise } = setupMocks( { + pageHeaderWidgetHeaderText: "Select a style to get started", + mountOptionsMockData: { + router: { + navigate: jest.fn() + }, + }, + }); + + //Act + vehicleStyle.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-style" } }, undefined, (c) => c(wrapper.vm)); + wrapper.vm.backButtonAction(); + await nextTick(); + + //Assert + apiPromise.finally(() => { + expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); + done(); + }); + }); +}); + +function setupMocks({ + radioQuestionCmsContent = {}, + styleQuestionInitialData = {}, + pageHeaderWidgetHeaderText = {}, + mountOptionsMockData = {}, +}) { + + //Mock api responses + const apiResponses = { + cmsContent: { + PageHeaderWidget: [{ HeaderText: pageHeaderWidgetHeaderText }], + RadioQuestionWidget: [radioQuestionCmsContent], + VehicleBannerWidget: [ + { + GenericVehicleImage: + "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", + }, + ], + SiteHeaderWidget: [ + { + LogoImage: + "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", + }, + ], + }, + styleQuestionInitialData: styleQuestionInitialData, + }; + const apiPromise = Promise.resolve(apiResponses); + settleAllPromises.mockImplementation(() => apiPromise); + + //Mock style question methods + styleQuestion.methods = { + loadInitialData: jest.fn(), + initializeComponent: jest.fn(), + }; + const mountOptions = getMountOptions(mountOptionsMockData); + const wrapper = shallowMount(vehicleStyle, mountOptions); + const styleQuestionWrapper = wrapper.findComponent({ name: "styleQuestion" }); + styleQuestionWrapper.vm.initializeComponent = styleQuestion.methods.initializeComponent; + + return { wrapper, apiPromise }; +} \ No newline at end of file diff --git a/src/layouts/vehicle-style/vehicle-style.vue b/src/layouts/vehicle-style/vehicle-style.vue new file mode 100644 index 000000000..6b5d4bf86 --- /dev/null +++ b/src/layouts/vehicle-style/vehicle-style.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index 65947744e..48e36996b 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -2,6 +2,7 @@ const navigationScenarios = { SELECTED_YEAR: "SELECTED_YEAR", SELECTED_MODEL: "SELECTED_MODEL", SELECTED_MAKE: "SELECTED_MAKE", + SELECTED_STYLE: "SELECTED_STYLE", CLICKED_BACK: "CLICKED_BACK", }; diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 34f4c5579..21dc6ca65 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -37,6 +37,19 @@ const routingTable = [ } ], }, + { + fmgPageValue: fmgPageValues.VEHICLE_STYLE, + maps: [ + { + scenario: navigationScenarios.SELECTED_STYLE, + destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE, + }, + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.VEHICLE_MODEL, + } + ], + }, ]; export { routingTable }; \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index 5e21f7ce4..daf34e38c 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -70,6 +70,9 @@ export default createStore({ updateModel(state, model) { state.order.vehicle.model = model; }, + updateStyle(state, style) { + state.order.vehicle.style = style; + }, }, getters: { vehicle: state => state.order.vehicle diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 76acc0dbf..fcfca4576 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -118,6 +118,73 @@ describe("Actions", () => { // Assert expect(pageData).toBe("Page Info Data"); }); + + it("Should return data from url retrieved", async () => { + // Arrange + let returnData = []; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ + data: { + Result: "2018 Honda Civic", + }, + }); + }); + await store + .dispatch("lookupVehicleByYmms", { year: "2018", make: "Honda", model: "Civic", style: "2 Door"}) + .then((response) => { + returnData = response.data.Result; + }); + + // Assert + expect(returnData).toBe("2018 Honda Civic"); + }); + + it("Should return vehicle data from url retrieved", async () => { + // Arrange + let returnData = []; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ + data: { + Result: "2021 Honda Civic", + }, + }); + }); + 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", + }, + }); + }); + await store + .dispatch("getEvoxImage", { relativeUrl: "evox_image.com"}) + .then((response) => { + returnData = response.data.Result; + }); + + // Assert + expect(returnData).toBe("2008_honda_civic.jpg"); + }); }); describe("Mutations", () => { @@ -128,4 +195,34 @@ describe("Mutations", () => { // Assert expect(store.state.order.vehicle.year).toBe(2020); }); + + it("Should update the make property in the store", () => { + // Act + store.commit("updateMake", "Honda"); + + // 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"); + }); +}); + +describe("Getters", () => { + const vehicle = store.getters.vehicle; + + expect(typeof vehicle).toBe('object'); });