diff --git a/README.md b/README.md index 65e8bfb95..535976602 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,8 @@ This Repository is for Fix My Glass front end Vue and documentation associated w #### Routing We are using [Vue Router](https://next.router.vuejs.org/) 4.x for our front end routing. We are using a runtime dynamic routing solution. Some more information about dynamic routing can be found on the [official Vue Router documentation](https://next.router.vuejs.org/guide/advanced/dynamic-routing.html). -Here is a high level overview of how our routing works: -![FixMyGlass - Routing](https://user-images.githubusercontent.com/44780237/139876801-7d5c495a-f361-4234-a8ec-69126a357f80.png) - -`RetainStructureAndGoTo404(to, next)` - Since our 404 path on our router is `path: '/:pathMatch(.*)*'` (meaning that we don't have a dedicated 404 route) we retain the information entered by the user in the url bar and just serve back our `NotFound` component on Vue. For example if a user was to type in `.../fmg/fmgPage=junkPage` Vue will retain this url in the browser and return our `NotFound` component. This could be especially useful for analytics and reporting. - `Next()` - Is a function that is native to Vue Router in which we call in order to send the user where want them to go based on some logic we have[. You can read more about Navigation Guards and the `Next()` function on the Vue Router documentation. ](https://next.router.vuejs.org/guide/advanced/navigation-guards.html) -`GetRouteInfoFromPageName(PageName)` - This method typically takes the query string value of `fmgPage` and passes it into a call to our Content Service. The service will pull information on the page; matching page name passed to page name in our Cms. If we have a match, we will compile the route information and send it to our router to process. If we do not have a that page in our Cms, the user will be served back a 404 page while retaining the structure. \ No newline at end of file +`GetRouteInfoFromPageName(PageName)` - This method typically takes the query string value of `fmgPage` and passes it into a call to our Content Service. The service will pull information on the page; matching page name passed to page name in our Cms. If we have a match, we will compile the route information and send it to our router to process. If we do not have a that page in our Cms, the user will be served back a 404 page while retaining the structure. + +`GoToFunnelStartOn404(next)` - When a user tries to go to a page that doesn't exist in the CMS, they will be put back to the 'Start' of the funnel which is whatever page is marked as the homepage on Sitefinity. \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 7a6fcbc01..36c4507fc 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,12 +13,12 @@ module.exports = { "!src/helpers/unit-test-helper.js", "!src/layouts/component-test/component-test.vue", "!src/layouts/form-test/form-test.vue", - "!src/layouts/address-poc/address-poc.vue" + "!src/layouts/address-poc/address-poc.vue", ], //! means exclude from coverage. testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { global: { - statements: 85, + 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/App.vue b/src/App.vue index 3d665312e..76a01d29c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,7 +5,6 @@ \ No newline at end of file + @import "@/styles/common-error-styles.scss"; + diff --git a/src/common-components/button-back/button-back.spec.js b/src/common-components/button-back/button-back.spec.js index b8bc3256d..c7bff87f0 100644 --- a/src/common-components/button-back/button-back.spec.js +++ b/src/common-components/button-back/button-back.spec.js @@ -2,7 +2,6 @@ import { shallowMount } from "@vue/test-utils"; import buttonBack from "./button-back"; describe("back button", () => { - test("renders a button", () => { // Arrange const myFunction = () => {}; @@ -14,10 +13,9 @@ describe("back button", () => { backButtonAccessibleText: "something", }, }); - + // Assert expect(wrapper.find("button").exists()).toBe(true); wrapper.unmount(); }); - }); diff --git a/src/common-components/button-back/button-back.vue b/src/common-components/button-back/button-back.vue index 44db0038c..cf3a630bf 100644 --- a/src/common-components/button-back/button-back.vue +++ b/src/common-components/button-back/button-back.vue @@ -1,23 +1,36 @@ diff --git a/src/constants/events.js b/src/constants/events.js index 91461d535..066ef6dcd 100644 --- a/src/constants/events.js +++ b/src/constants/events.js @@ -1,17 +1,17 @@ const globalEvents = { - Categories: { - GLOBAL_ALERT: "GLOBAL_ALERT", - }, - SubCategories: { - PAGE_NOT_FOUND: "PAGE_NOT_FOUND", - } -} + Categories: { + GLOBAL_ALERT: "GLOBAL_ALERT", + }, + SubCategories: { + PAGE_NOT_FOUND: "PAGE_NOT_FOUND", + }, +}; const globalEventTypes = { - Success: "alert-success", - Warning: "alert-warning", - Info: "alert-info", - Danger: "alert-danger", -} + Success: "alert-success", + Warning: "alert-warning", + Info: "alert-info", + Danger: "alert-danger", +}; -export {globalEvents, globalEventTypes} \ No newline at end of file +export { globalEvents, globalEventTypes }; 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..c572c4f69 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -1,9 +1,26 @@ 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", + UPDATE_VEHICLE_IMAGE_URL: "updateVehicleImageUrl", + UPDATE_VEHICLE_IMAGE_VIF_NUMBER: "updateVehicleImageVifNumber", + UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor", + + // 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/constants/vehicle-categories.js b/src/constants/vehicle-categories.js new file mode 100644 index 000000000..4fab279d8 --- /dev/null +++ b/src/constants/vehicle-categories.js @@ -0,0 +1,11 @@ +const vehicleCategories = { + CAR: "CAR", + TRUCK: "TRUCK", + VAN: "VAN", + COMMERCIALVAN: "COMMERCIAL VAN", + SUV: "SUV", + MOTORHOME: "MOTOR HOME", + SEMI: "SEMI", + }; + + export { vehicleCategories }; \ No newline at end of file diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index 1c795aab2..a20446ef8 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -9,20 +9,26 @@ export function fetchCmsContentForPage(fmgPage) { const pageDataFromCms = {}; response.data.Result.forEach((widget) => { - - let widgetWithReplacements = findAndReplaceGlobalStateValues(widget.Model, widget.Name); + let widgetWithReplacements = findAndReplaceGlobalStateValues( + widget.Model, + widget.Name + ); // If we already have this widget, push it on the collection if (widgetWithReplacements.Name in pageDataFromCms) { - pageDataFromCms[widgetWithReplacements.Name].push(widgetWithReplacements.Model); + pageDataFromCms[widgetWithReplacements.Name].push( + widgetWithReplacements.Model + ); return; } - pageDataFromCms[widgetWithReplacements.Name] = [widgetWithReplacements.Model]; + pageDataFromCms[widgetWithReplacements.Name] = [ + widgetWithReplacements.Model, + ]; }); Object.keys(pageDataFromCms).forEach((key) => { - if (pageDataFromCms[key].length === 1){ + if (pageDataFromCms[key].length === 1) { pageDataFromCms[key] = pageDataFromCms[key][0]; } }); @@ -33,31 +39,29 @@ export function fetchCmsContentForPage(fmgPage) { // Function to convert a string, into a matching global state item. function mapStringToState(str) { - // Pull all matches out of the string. - const regexExp = new RegExp('{(.*?):(.*?)}', 'g'); + const regexExp = new RegExp("{(.*?):(.*?)}", "g"); const matches = [...str.matchAll(regexExp)]; // Our final string value that will be built from the matches. - let stringBuilder = ''; + let stringBuilder = ""; for (const match of matches) { - // Reset store state for each match. let storeState = store.state; - for (const s of match[2].split('.')) { + for (const s of match[2].split(".")) { if (storeState[s] != undefined) { storeState = storeState[s]; } else { - return ''; // if we can't map our string to state data, return an empty string. + return ""; // if we can't map our string to state data, return an empty string. } } const stringWithReplacement = str.replace(match[0], storeState); // If we still have values we need to substitute, call this function again. - if(stringWithReplacement.includes('{globalState:')) { + if (stringWithReplacement.includes("{globalState:")) { return mapStringToState(stringWithReplacement); } @@ -71,15 +75,16 @@ function mapStringToState(str) { // Parent function for processWidgetItemForReplacement. This will loop through the parent // object and pass any objects that need additional processing to the processWidgetItemForReplacement function. function findAndReplaceGlobalStateValues(widgetModel, widgetName) { - const objWithReplacements = { Name: widgetName, - Model: {} + Model: {}, }; - Object.keys(widgetModel).forEach(key => { - - let modelWithReplacements = processWidgetItemForReplacement(widgetModel, key); + Object.keys(widgetModel).forEach((key) => { + let modelWithReplacements = processWidgetItemForReplacement( + widgetModel, + key + ); objWithReplacements.Model[key] = modelWithReplacements; }); @@ -91,16 +96,19 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) { // This is a recursive function, it will call itself until it runs out of items to iterate on given the object. function processWidgetItemForReplacement(widgetModel, key) { // If we have a string, and it needs to be replaced. - if (typeof widgetModel[key] === 'string') { - if (widgetModel[key].includes('{globalState:')) { + if (typeof widgetModel[key] === "string") { + if (widgetModel[key].includes("{globalState:")) { widgetModel[key] = mapStringToState(widgetModel[key]); } return widgetModel[key]; } // If we have an object. array, etc - if (typeof widgetModel[key] === 'object' && Object.keys(widgetModel[key]).length) { - Object.keys(widgetModel[key]).forEach(item => { + if ( + typeof widgetModel[key] === "object" && + Object.keys(widgetModel[key]).length + ) { + Object.keys(widgetModel[key]).forEach((item) => { processWidgetItemForReplacement(widgetModel[key], item); }); @@ -109,5 +117,4 @@ function processWidgetItemForReplacement(widgetModel, key) { // If we have something else like a number, boolean, etc. just return it return widgetModel[key]; - -} \ No newline at end of file +} diff --git a/src/helpers/cms-helper.spec.js b/src/helpers/cms-helper.spec.js index efc0ee56c..d7aeb8bf4 100644 --- a/src/helpers/cms-helper.spec.js +++ b/src/helpers/cms-helper.spec.js @@ -4,12 +4,11 @@ import { dispatch } from "@/store"; jest.mock("@/store", () => ({ dispatch: jest.fn(), state: { - order: { vehicle: { year: "2019", make: "Acura" } } - } + order: { vehicle: { year: "2019", make: "Acura" } }, + }, })); - -describe("cms-content-helper.js", () => { +describe("cms-content-helper.js", () => { it("Should return data from CMS", () => { // Arrange const cmsMockData = { @@ -34,9 +33,9 @@ describe("cms-content-helper.js", () => { }, ], }; - + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); - + // Act fetchCmsContentForPage("testPage").then((response) => { // Assert @@ -45,10 +44,9 @@ describe("cms-content-helper.js", () => { ); }); }); - }); -describe("cms-content-helper.js", () => { +describe("cms-content-helper.js", () => { it("Should replace strings for global state", () => { const cmsMockData = { Result: [ @@ -60,21 +58,18 @@ describe("cms-content-helper.js", () => { }, ], }; - + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); - + fetchCmsContentForPage("testPage").then((response) => { // Assert - expect(response.FunnelSubHeaderWidget.HeaderText).toEqual( - "2019" - ); + expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019"); }); }); }); -describe("cms-content-helper.js", () => { +describe("cms-content-helper.js", () => { it("Should replace strings for global state, and leave others the same", () => { - const cmsMockData = { Result: [ { @@ -91,21 +86,21 @@ describe("cms-content-helper.js", () => { }, ], }; - + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); - + fetchCmsContentForPage("testPage").then((response) => { // Assert expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019"); - expect(response.VehicleYearQuestion.ExampleText).toEqual("My widget value!"); - + expect(response.VehicleYearQuestion.ExampleText).toEqual( + "My widget value!" + ); }); }); }); -describe("cms-content-helper.js", () => { +describe("cms-content-helper.js", () => { it("Should replace strings for global state in nested objects", () => { - const cmsMockData = { Result: [ { @@ -119,22 +114,23 @@ describe("cms-content-helper.js", () => { Model: { OtherObjectInside: { ExampleText: "{globalState:order.vehicle.make}", - } + }, }, }, ], }; - + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); - + fetchCmsContentForPage("testPage").then((response) => { // Assert - + expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019"); - expect(response.VehicleMakeQuestion.OtherObjectInside.ExampleText).toEqual("Acura"); - + expect( + response.VehicleMakeQuestion.OtherObjectInside.ExampleText + ).toEqual("Acura"); }); }); }); -test.todo("String cannot be mapped to global state"); \ No newline at end of file +test.todo("String cannot be mapped to global state"); diff --git a/src/helpers/event-bus/event-bus.js b/src/helpers/event-bus/event-bus.js index 8ac156776..19b30870c 100644 --- a/src/helpers/event-bus/event-bus.js +++ b/src/helpers/event-bus/event-bus.js @@ -1,25 +1,32 @@ 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, { category: category, subCategory: subCategory, eventValue: eventValue }); - }, + // Adds event to the bus given its category, subcategory, and eventValue; + addEventToBus(category, subCategory, eventValue) { + store.commit(storeMutations.ADD_EVENT_TO_BUS, { + category: category, + subCategory: subCategory, + eventValue: eventValue, + }); + }, - // Finds event on the bus, removes the item, and returns its value to the caller. - readAndPopEventFromBus(category, subCategory) { - const event = store.getters.eventBusItem(category, subCategory); + // Finds event on the bus, removes the item, and returns its value to the caller. + readAndPopEventFromBus(category, subCategory) { + const event = store.getters.eventBusItem(category, subCategory); - store.commit(storeActions.REMOVE_EVENT_FROM_BUS, { category: category, subCategory: subCategory }); + store.commit(storeMutations.REMOVE_EVENT_FROM_BUS, { + category: category, + subCategory: subCategory, + }); - return event; - }, + return event; + }, - // Finds the event on the bus and returns its value to the caller, does not remove it. - readEventFromBus(category, subCategory) { - const event = store.getters.eventBusItem(category, subCategory); + // Finds the event on the bus and returns its value to the caller, does not remove it. + readEventFromBus(category, subCategory) { + const event = store.getters.eventBusItem(category, subCategory); - return event; - }, -} \ No newline at end of file + return event; + }, +}; diff --git a/src/helpers/event-bus/event-bus.spec.js b/src/helpers/event-bus/event-bus.spec.js index d20c11044..fece3fee2 100644 --- a/src/helpers/event-bus/event-bus.spec.js +++ b/src/helpers/event-bus/event-bus.spec.js @@ -3,48 +3,57 @@ import eventBus from "@/helpers/event-bus/event-bus"; import store from "@/store"; describe("event-bus.js", () => { + let event = { + isDismissible: true, + messageCopy: "You can get a quote by starting on this page.", + messageHeadline: "We're sorry, something went wrong.", + type: globalEventTypes.Danger, + }; - let event = { - isDismissible: true, - messageCopy: 'You can get a quote by starting on this page.', - messageHeadline: 'We\'re sorry, something went wrong.', - type: globalEventTypes.Danger - }; + it("Puts item on bus and then take it off", () => { + // Arrange / Act + eventBus.addEventToBus( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND, + event + ); - it("Puts item on bus and then take it off", () => { + // Assert + expect( + store.getters.eventBusItem( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND + ) + ).toEqual(event); - // Arrange / Act - eventBus.addEventToBus( - globalEvents.Categories.GLOBAL_ALERT, - globalEvents.SubCategories.PAGE_NOT_FOUND, - event - ); + expect(store.state.applicationUser.eventBus.length).toEqual(1); + // Arrange / Act + const eventValue = eventBus.readAndPopEventFromBus( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND + ); - // Assert - expect(store.getters.eventBusItem(globalEvents.Categories.GLOBAL_ALERT, globalEvents.SubCategories.PAGE_NOT_FOUND)).toEqual(event); + // Assert + expect(eventValue).toEqual(event); - expect(store.state.applicationUser.eventBus.length).toEqual(1); + expect(store.state.applicationUser.eventBus.length).toEqual(0); + }); - // Arrange / Act - const eventValue = eventBus.readAndPopEventFromBus(globalEvents.Categories.GLOBAL_ALERT, globalEvents.SubCategories.PAGE_NOT_FOUND); + it("Reads event from bus, should have event value.", () => { + // Arrange / Act + eventBus.addEventToBus( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND, + event + ); - // Assert - expect(eventValue).toEqual(event); - - expect(store.state.applicationUser.eventBus.length).toEqual(0); - }); - - it("Reads event from bus, should have event value.", () => { - // Arrange / Act - eventBus.addEventToBus( - globalEvents.Categories.GLOBAL_ALERT, - globalEvents.SubCategories.PAGE_NOT_FOUND, - event - ); - - // Assert - expect(eventBus.readEventFromBus(globalEvents.Categories.GLOBAL_ALERT, globalEvents.SubCategories.PAGE_NOT_FOUND)).toEqual(event); - - }); -}); \ No newline at end of file + // Assert + expect( + eventBus.readEventFromBus( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND + ) + ).toEqual(event); + }); +}); diff --git a/src/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js index 268a8e524..4e5d86786 100644 --- a/src/helpers/unit-test-helper.js +++ b/src/helpers/unit-test-helper.js @@ -1,6 +1,7 @@ import { storeActions } from "@/constants/store-actions"; import { storeMutations } from "@/constants/store-mutations.js"; import { navigationScenarios } from "@/router/router-constants/navigation-scenarios.js"; +import { vehicleCategories } from "@/constants/vehicle-categories.js"; export function getMountOptions(mockData) { // Define our mocks to attached to the 'global' object for Vue/Jest. @@ -23,6 +24,7 @@ export function getMountOptions(mockData) { mocks.storeActions = storeActions; mocks.storeMutations = storeMutations; mocks.navigationScenarios = navigationScenarios; + mocks.vehicleCategories = vehicleCategories; // Mock $store and $router when accessing this.$store/$router mocks.$store = mockData.store; diff --git a/src/helpers/validation-rules.js b/src/helpers/validation-rules.js new file mode 100644 index 000000000..97c5f54e5 --- /dev/null +++ b/src/helpers/validation-rules.js @@ -0,0 +1,38 @@ +const rules = { + // required: (value) => { + // console.log('value is... ', value); + // if (!value) { + // return 'PASSWORD IS REQUIRED'; + // } + // }, + // lengthOf3: (value) => { + // console.log('value is... ', value); + // if (value.length < 3) { + // return 'PASSWORD MUST BE AT LEAST 3 CHARS LONG'; + // } + // } + required: { + test: (value) => { + // console.log('required value is... ', value); + if (!value || value.length < 1) { + // console.log('return false') + return false; + } + // console.log('no if, return true') + return true; + }, + error: 'ssdfgPASSWORD IS REQUIRED' + }, + lengthOf3: { + test: (value) => { + console.log('lengthof3 value is... ', value); + if (value.length < 3) { + return false; + } + return true; + }, + error: 'sfdgfPASSWORD MUST BE AT LEAST 3 CHARS LONG' + } + }; + + export { rules }; \ No newline at end of file diff --git a/src/layouts/address-poc/address-poc.vue b/src/layouts/address-poc/address-poc.vue index 31a5e66ee..1c10e927d 100644 --- a/src/layouts/address-poc/address-poc.vue +++ b/src/layouts/address-poc/address-poc.vue @@ -2,51 +2,57 @@
- +
- +
-
-
+ + diff --git a/src/layouts/component-test/component-test.vue b/src/layouts/component-test/component-test.vue index 9e7eaca6d..2fd05b516 100644 --- a/src/layouts/component-test/component-test.vue +++ b/src/layouts/component-test/component-test.vue @@ -8,10 +8,10 @@
@@ -22,25 +22,46 @@
- -
-
-
-
-
- +
+
+
+
+ +
+
+
+
+

Links

+
+
+
+
+

+

+

+
@@ -54,31 +75,31 @@ Functioning as Checkbox
@@ -93,13 +114,13 @@
Functioning as Checkbox
@@ -110,13 +131,13 @@
Checkbox no Description
@@ -127,13 +148,13 @@
Functioning as Radio Button
@@ -144,13 +165,13 @@
Radio Button no Description
@@ -161,14 +182,14 @@
Horizontal Checkbox
@@ -179,14 +200,14 @@
Checkbox no Description
@@ -197,13 +218,13 @@
Horizontal Radio Button
@@ -214,13 +235,13 @@
Radio Button no Description
@@ -233,93 +254,99 @@
-
+
-

- Select Vehicle Year -

+

Select Vehicle Year

- @@ -331,187 +358,205 @@
-
+

Select Vehicle Year

Functioning as Radio Buttons
-
+

Select Vehicle Year

-

List Button - Multi-Line Centered

+

+ List Button - Multi-Line Centered +

Functioning as Checkboxes
-
+

Multi-Line Centered

Functioning as Radio Buttons
-
+

Multi-Line Centered

@@ -523,104 +568,108 @@
-
+
-

- Select Vehicle Year -

+

Select Vehicle Year

Functioning as Radio Buttons
-
+
-

- Select Vehicle Year -

+

Select Vehicle Year

@@ -631,18 +680,15 @@
- Text Link (default text link behavior) -

- Text Link Small (.small) -

- Footer Link (.footer-link) -

- Navigation Link (.navigation-link) +

+

+

+
-

Typogrophy

+

Typography

@@ -699,60 +745,60 @@
@@ -764,7 +810,7 @@
@@ -777,6 +823,7 @@
@@ -785,101 +832,114 @@

Button Question

- - - - - - - - - - - +
+
+ + + + + + + + + + + +
+
+
+
+

Funnel Footer

+
+
+
+
+ +
+
\ No newline at end of file +.form-test-error { + color: red; + font-weight: bold; +} +.form-test-invalid { + opacity: 0.5; +} + 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 index 34866e98c..bd583a378 100644 --- a/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue +++ b/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue @@ -1,15 +1,15 @@