diff --git a/package-lock.json b/package-lock.json index 93875c8b..7ff32a19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1794,6 +1794,14 @@ "fastq": "^1.6.0" } }, + "@pinia/testing": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-0.0.14.tgz", + "integrity": "sha512-ZmZwVNd/NnKYLIfjfuKl0zlJ3UdiXFpsHzSlL6wCeezSlyrqGMxsIQKv0J6fleu38gyCNTPBEipfxrt8V4+VIg==", + "requires": { + "vue-demi": "*" + } + }, "@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -2540,6 +2548,29 @@ "webpack-merge": "^5.7.3", "webpack-virtual-modules": "^0.4.2", "whatwg-fetch": "^3.6.2" + }, + "dependencies": { + "@vue/vue-loader-v15": { + "version": "npm:vue-loader@15.10.0", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz", + "integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==", + "dev": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + } + } + } } }, "@vue/cli-shared-utils": { @@ -2789,27 +2820,6 @@ "integrity": "sha512-E2P4oXSaWDqTZNbmKZFVLrNN/siVN78YkEqs7pHryWerrlZR9bBFLWdJwRoguX45Ru6HxIflzKl4vQvwRMwm5g==", "dev": true }, - "@vue/vue-loader-v15": { - "version": "npm:vue-loader@15.10.0", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz", - "integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==", - "dev": true, - "requires": { - "@vue/component-compiler-utils": "^3.1.0", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "vue-hot-reload-api": "^2.3.0", - "vue-style-loader": "^4.1.0" - }, - "dependencies": { - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", - "dev": true - } - } - }, "@vue/vue3-jest": { "version": "27.0.0", "resolved": "https://registry.npmjs.org/@vue/vue3-jest/-/vue3-jest-27.0.0.tgz", diff --git a/package.json b/package.json index 9b586f69..e9250297 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "test:unit:lite": "vue-cli-service test:unit --ci" }, "dependencies": { + "@pinia/testing": "0.0.14", "@popperjs/core": "^2.10.2", "axios": "^0.27.2", "bootstrap": "^5.2.2", diff --git a/src/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js new file mode 100644 index 00000000..20e33a7e --- /dev/null +++ b/src/helpers/unit-test-helper.js @@ -0,0 +1,46 @@ +import { storeActions } from "@/constants/store-actions"; +import { navigationScenarios } from "@/router/router-constants/navigation-scenarios.js"; +import { vehicleCategories } from "@/constants/vehicle-categories.js"; +import { issPageValues } from "@/router/router-constants/issPage-values"; +import { Form } from "vee-validate"; +import { queryStrings } from "@/constants/query-strings"; +import { useMainStore } from "@/store"; +import { mapStores } from "pinia"; +import { createTestingPinia } from '@pinia/testing'; + +const pinia = createTestingPinia(); +useMainStore(pinia); + +const mockMixin = { + methods: { + getCmsContent: jest.fn() + }, + computed: { + ...mapStores(useMainStore), + } +}; + + +// Common methods +export function getMountOptions(mockData) { + // Define our mocks to attached to the 'global' object for Vue/Jest. + const mocks = {}; + + + // Mock const files + mocks.storeActions = storeActions; + mocks.navigationScenarios = navigationScenarios; + mocks.vehicleCategories = vehicleCategories; + mocks.issPageValues = issPageValues; + mocks.queryStrings = queryStrings; + + + const global = { + mocks: mocks, + mixins: [mockMixin], + plugins: [pinia], + stubs: { Form } + }; + + return { global }; +} \ No newline at end of file diff --git a/src/layouts/vehicle-make/make-question/make-question.spec.js b/src/layouts/vehicle-make/make-question/make-question.spec.js new file mode 100644 index 00000000..c9e096e5 --- /dev/null +++ b/src/layouts/vehicle-make/make-question/make-question.spec.js @@ -0,0 +1,84 @@ +import makeQuestion from "@/layouts/vehicle-make/make-question/make-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("make-question.vue", () => { + + test("Data from store api are used as radio question answers.", async () => { + //Arrange + const { wrapper, cmsContent } = setupMocks({ + dataFromStoreApi: ["honda", "ford", "dodge"], + }); + + //Act + const initialData = makeQuestion.methods.loadInitialData.call(wrapper.vm); + makeQuestion.methods.initializeComponent.call( + wrapper.vm, + initialData + ); + + //Assert + const buttonQuestionComponent = await wrapper.findComponent({ + name: "buttonQuestion", + }); + expect(buttonQuestionComponent.attributes("answers")).toBe( + "honda,ford,dodge" + ); + }); + + test("Selected make is emitted upon selection.", async () => { + //Arrange + const { wrapper } = setupMocks({ modelValueProp: "honda" }); + const makeToSelect = "ford"; + + //Act + wrapper.setValue({ selectedMake: makeToSelect }); + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([ + { selectedMake: "ford" }, + ]); + }); + +}); + +function setupMocks({ + modelValueProp = "1900", + cmsQuestionText = "CMS text goes here", + dataFromStoreApi = [], +}) { + //Mock store + store.getVehicleMakes = jest.fn(() => dataFromStoreApi); + const mountOptions = getMountOptions({ + store: { + getVehicleMakes: store.getVehicleMakes, + }, + }); + + //Mock props + const mockMixin = { + methods: { + getCmsContent: jest.fn() + } + } + mountOptions.propsData = { + modelValue: modelValueProp, + }; + mountOptions.mixins = [mockMixin]; + const wrapper = shallowMount(makeQuestion, mountOptions); + + //Mock CMS content + const cmsContent = { + QuestionText: cmsQuestionText, + }; + return { wrapper, cmsContent }; +} diff --git a/src/layouts/vehicle-make/make-question/make-question.vue b/src/layouts/vehicle-make/make-question/make-question.vue index f99f5d45..0062f422 100644 --- a/src/layouts/vehicle-make/make-question/make-question.vue +++ b/src/layouts/vehicle-make/make-question/make-question.vue @@ -41,7 +41,6 @@ set: function(newValue) { this.$emit("update:modelValue", newValue); this.mainStore.updateVehicleMake(newValue); - console.log(this.mainStore.order.vehicle.make) this.$router.navigate(this.navigationScenarios.SELECTED_MAKE, this.$route) } } diff --git a/src/layouts/vehicle-make/vehicle-make.spec.js b/src/layouts/vehicle-make/vehicle-make.spec.js index 66012402..d61297cd 100644 --- a/src/layouts/vehicle-make/vehicle-make.spec.js +++ b/src/layouts/vehicle-make/vehicle-make.spec.js @@ -1 +1,130 @@ -test.todo("some test to be written in the future"); \ No newline at end of file +// 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"; + + +// Components +import vehicleMake from "@/layouts/vehicle-make/vehicle-make.vue"; +import makeQuestion from "@/layouts/vehicle-make/make-question/make-question"; + +jest.mock("@/store", () => ({ + getVehicleMakes: jest.fn(), + order: { + vehicle: { + year: 2019, + }, + }, +})); + +// Mock fetchCmsContentForPage +jest.mock("@/helpers/cms-content-helper", () => ({ + fetchCmsContentForPage: jest.fn(), +})); + +// Mock our module for promises. +jest.mock("@/helpers/layout-helper.js", () => ({ + settleAllPromises: jest.fn(), +})); + +/* +describe("vehicle-make.vue", () => { + test("Make question component is initized with api data", async (done) => { + //Arrange + const makeQuestionInitialData = ["honda", "ford", "dodge"]; + const { wrapper, apiPromise } = setupMocks({ + makeQuestionInitialData: makeQuestionInitialData, + }); + + //Act + vehicleMake.beforeRouteEnter.call( + wrapper.vm, + { query: { issPage: "vehicle-make" } }, + undefined, + (c) => c(wrapper.vm) + ); + + //Assert + apiPromise.finally(() => { + expect(makeQuestion.methods.initializeComponent).toHaveBeenCalledWith( + makeQuestionInitialData + ); + //done(); + }); + }); +}); +*/ + +describe("vehicle-make.vue", () => { + test("BackButtonAction triggers a router.navigate change", async (done) => { + const { wrapper, apiPromise } = setupMocks({ + pageHeaderWidgetHeaderText: "Select a make to get started", + mountOptionsMockData: { + router: { + navigate: jest.fn(), + }, + }, + }); + + vehicleMake.beforeRouteEnter.call( + wrapper.vm, + { query: { issPage: "vehicle-make" } }, + undefined, + (c) => c(wrapper.vm) + ); + wrapper.vm.backButtonAction(); + await nextTick(); + + apiPromise.finally(() => { + expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); + //done(); + }); + + }); +}); + +function setupMocks({ + vehicleMakeQuestionCmsContent = {}, + makeQuestionInitialData = {}, + pageHeaderWidgetHeaderText = {}, + mountOptionsMockData = {}, +}) { + //Mock api responses + const apiResponses = { + cmsContent: { + FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, + VehicleMakeQuestion: vehicleMakeQuestionCmsContent, + 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", + }, + }, + makeQuestionInitialData: makeQuestionInitialData, + }; + + const apiPromise = Promise.resolve(apiResponses); + + fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); + settleAllPromises.mockImplementation(() => apiPromise); + + //Mock make question methods + makeQuestion.methods = { + loadInitialData: jest.fn(), + initializeComponent: jest.fn(), + }; + + const mountOptions = getMountOptions(mountOptionsMockData); + const wrapper = shallowMount(vehicleMake, mountOptions); + const makeQuestionWrapper = wrapper.findComponent({ name: "makeQuestion" }); + makeQuestionWrapper.vm.initializeComponent = + makeQuestion.methods.initializeComponent; + wrapper.vm.setCmsContent = jest.fn(); + + return { wrapper, apiPromise }; +}