diff --git a/jest.config.js b/jest.config.js index 4a2601c8f..eceda28b1 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,6 +17,9 @@ module.exports = { "!src/layouts/vehicle-damage/windshield-options/windshield-options.vue", "!src/layouts/address-poc/address-poc.vue", "!src/layouts/nested-radio-poc/nested-radio.vue", + "!src/layouts/button-question-examples/**/*.vue", + "!src/layouts/part-questions/**/*.vue", + "!src/layouts/reveal/**/*.vue", ], //! means exclude from coverage. testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { diff --git a/src/assets/img/tints/Glass-BlueShade-BlueTint.svg b/src/assets/img/tints/Glass-BlueShade-BlueTint.svg new file mode 100644 index 000000000..007c490f0 --- /dev/null +++ b/src/assets/img/tints/Glass-BlueShade-BlueTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-BlueShade-BrownTint.svg b/src/assets/img/tints/Glass-BlueShade-BrownTint.svg new file mode 100644 index 000000000..d1ffeb8e0 --- /dev/null +++ b/src/assets/img/tints/Glass-BlueShade-BrownTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-BlueShade-GrayTint.svg b/src/assets/img/tints/Glass-BlueShade-GrayTint.svg new file mode 100644 index 000000000..417ce474d --- /dev/null +++ b/src/assets/img/tints/Glass-BlueShade-GrayTint.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-BlueShade-GreenTint.svg b/src/assets/img/tints/Glass-BlueShade-GreenTint.svg new file mode 100644 index 000000000..4cf1c3831 --- /dev/null +++ b/src/assets/img/tints/Glass-BlueShade-GreenTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-BlueShade-NoTint.svg b/src/assets/img/tints/Glass-BlueShade-NoTint.svg new file mode 100644 index 000000000..220b1ab04 --- /dev/null +++ b/src/assets/img/tints/Glass-BlueShade-NoTint.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-BrownShade-BrownTint.svg b/src/assets/img/tints/Glass-BrownShade-BrownTint.svg new file mode 100644 index 000000000..dbd059625 --- /dev/null +++ b/src/assets/img/tints/Glass-BrownShade-BrownTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-GrayShade-BlueTint.svg b/src/assets/img/tints/Glass-GrayShade-BlueTint.svg new file mode 100644 index 000000000..2ec4a0535 --- /dev/null +++ b/src/assets/img/tints/Glass-GrayShade-BlueTint.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-GrayShade-BrownTint.svg b/src/assets/img/tints/Glass-GrayShade-BrownTint.svg new file mode 100644 index 000000000..f9cc80c12 --- /dev/null +++ b/src/assets/img/tints/Glass-GrayShade-BrownTint.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-GrayShade-GrayTint.svg b/src/assets/img/tints/Glass-GrayShade-GrayTint.svg new file mode 100644 index 000000000..fcfe78cf6 --- /dev/null +++ b/src/assets/img/tints/Glass-GrayShade-GrayTint.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-GrayShade-GreenTint.svg b/src/assets/img/tints/Glass-GrayShade-GreenTint.svg new file mode 100644 index 000000000..b420ec6f4 --- /dev/null +++ b/src/assets/img/tints/Glass-GrayShade-GreenTint.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-GreenShade-BlueTint.svg b/src/assets/img/tints/Glass-GreenShade-BlueTint.svg new file mode 100644 index 000000000..e1f2e9688 --- /dev/null +++ b/src/assets/img/tints/Glass-GreenShade-BlueTint.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-GreenShade-BrownTint.svg b/src/assets/img/tints/Glass-GreenShade-BrownTint.svg new file mode 100644 index 000000000..b8f49b565 --- /dev/null +++ b/src/assets/img/tints/Glass-GreenShade-BrownTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-GreenShade-GreenTint.svg b/src/assets/img/tints/Glass-GreenShade-GreenTint.svg new file mode 100644 index 000000000..b07e8dbcc --- /dev/null +++ b/src/assets/img/tints/Glass-GreenShade-GreenTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Glass-NoShade-BlueTint.svg b/src/assets/img/tints/Glass-NoShade-BlueTint.svg new file mode 100644 index 000000000..1e4fd4eba --- /dev/null +++ b/src/assets/img/tints/Glass-NoShade-BlueTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Glass-NoShade-BrownTint.svg b/src/assets/img/tints/Glass-NoShade-BrownTint.svg new file mode 100644 index 000000000..658774678 --- /dev/null +++ b/src/assets/img/tints/Glass-NoShade-BrownTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Glass-NoShade-DarkBrownTint.svg b/src/assets/img/tints/Glass-NoShade-DarkBrownTint.svg new file mode 100644 index 000000000..f36ebd878 --- /dev/null +++ b/src/assets/img/tints/Glass-NoShade-DarkBrownTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Glass-NoShade-DaryGrayTint.svg b/src/assets/img/tints/Glass-NoShade-DaryGrayTint.svg new file mode 100644 index 000000000..5aab7ea07 --- /dev/null +++ b/src/assets/img/tints/Glass-NoShade-DaryGrayTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Glass-NoShade-GrayTint.svg b/src/assets/img/tints/Glass-NoShade-GrayTint.svg new file mode 100644 index 000000000..ee14d3c4b --- /dev/null +++ b/src/assets/img/tints/Glass-NoShade-GrayTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Glass-NoShade-GreenTint.svg b/src/assets/img/tints/Glass-NoShade-GreenTint.svg new file mode 100644 index 000000000..5f5b99de0 --- /dev/null +++ b/src/assets/img/tints/Glass-NoShade-GreenTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Glass-NoShade-NoTint.svg b/src/assets/img/tints/Glass-NoShade-NoTint.svg new file mode 100644 index 000000000..ec1ced501 --- /dev/null +++ b/src/assets/img/tints/Glass-NoShade-NoTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Glass-NoShade-Privacy.svg b/src/assets/img/tints/Glass-NoShade-Privacy.svg new file mode 100644 index 000000000..caa726f47 --- /dev/null +++ b/src/assets/img/tints/Glass-NoShade-Privacy.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Windshield-BlueShade-BlueTint.svg b/src/assets/img/tints/Windshield-BlueShade-BlueTint.svg new file mode 100644 index 000000000..b4cf30c92 --- /dev/null +++ b/src/assets/img/tints/Windshield-BlueShade-BlueTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-BlueShade-BrownTint.svg b/src/assets/img/tints/Windshield-BlueShade-BrownTint.svg new file mode 100644 index 000000000..dbca11b88 --- /dev/null +++ b/src/assets/img/tints/Windshield-BlueShade-BrownTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-BlueShade-GrayTint.svg b/src/assets/img/tints/Windshield-BlueShade-GrayTint.svg new file mode 100644 index 000000000..7ef277564 --- /dev/null +++ b/src/assets/img/tints/Windshield-BlueShade-GrayTint.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-BlueShade-GreenTint.svg b/src/assets/img/tints/Windshield-BlueShade-GreenTint.svg new file mode 100644 index 000000000..18bc35fb5 --- /dev/null +++ b/src/assets/img/tints/Windshield-BlueShade-GreenTint.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-BlueShade-NoTint.svg b/src/assets/img/tints/Windshield-BlueShade-NoTint.svg new file mode 100644 index 000000000..2217911a8 --- /dev/null +++ b/src/assets/img/tints/Windshield-BlueShade-NoTint.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-BrownShade-BrownTint.svg b/src/assets/img/tints/Windshield-BrownShade-BrownTint.svg new file mode 100644 index 000000000..8c8b1da66 --- /dev/null +++ b/src/assets/img/tints/Windshield-BrownShade-BrownTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-GrayShade-BlueTint.svg b/src/assets/img/tints/Windshield-GrayShade-BlueTint.svg new file mode 100644 index 000000000..a924b6664 --- /dev/null +++ b/src/assets/img/tints/Windshield-GrayShade-BlueTint.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-GrayShade-BrownTint.svg b/src/assets/img/tints/Windshield-GrayShade-BrownTint.svg new file mode 100644 index 000000000..b26eedee1 --- /dev/null +++ b/src/assets/img/tints/Windshield-GrayShade-BrownTint.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-GrayShade-GrayTint.svg b/src/assets/img/tints/Windshield-GrayShade-GrayTint.svg new file mode 100644 index 000000000..69373ca2c --- /dev/null +++ b/src/assets/img/tints/Windshield-GrayShade-GrayTint.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-GrayShade-GreenTint.svg b/src/assets/img/tints/Windshield-GrayShade-GreenTint.svg new file mode 100644 index 000000000..14824eb51 --- /dev/null +++ b/src/assets/img/tints/Windshield-GrayShade-GreenTint.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-GreenShade-BlueTint.svg b/src/assets/img/tints/Windshield-GreenShade-BlueTint.svg new file mode 100644 index 000000000..fcc3d63a3 --- /dev/null +++ b/src/assets/img/tints/Windshield-GreenShade-BlueTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-GreenShade-BrownTint.svg b/src/assets/img/tints/Windshield-GreenShade-BrownTint.svg new file mode 100644 index 000000000..2fdf4bcf9 --- /dev/null +++ b/src/assets/img/tints/Windshield-GreenShade-BrownTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-GreenShade-GreenTint.svg b/src/assets/img/tints/Windshield-GreenShade-GreenTint.svg new file mode 100644 index 000000000..65a735289 --- /dev/null +++ b/src/assets/img/tints/Windshield-GreenShade-GreenTint.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/tints/Windshield-NoShade-BlueTint.svg b/src/assets/img/tints/Windshield-NoShade-BlueTint.svg new file mode 100644 index 000000000..cbeb84ab4 --- /dev/null +++ b/src/assets/img/tints/Windshield-NoShade-BlueTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Windshield-NoShade-BrownTint.svg b/src/assets/img/tints/Windshield-NoShade-BrownTint.svg new file mode 100644 index 000000000..f70fef994 --- /dev/null +++ b/src/assets/img/tints/Windshield-NoShade-BrownTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Windshield-NoShade-DarkBrownTint.svg b/src/assets/img/tints/Windshield-NoShade-DarkBrownTint.svg new file mode 100644 index 000000000..feedfa3f0 --- /dev/null +++ b/src/assets/img/tints/Windshield-NoShade-DarkBrownTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Windshield-NoShade-DarkGrayTint.svg b/src/assets/img/tints/Windshield-NoShade-DarkGrayTint.svg new file mode 100644 index 000000000..9f55ee8a7 --- /dev/null +++ b/src/assets/img/tints/Windshield-NoShade-DarkGrayTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Windshield-NoShade-GrayTint.svg b/src/assets/img/tints/Windshield-NoShade-GrayTint.svg new file mode 100644 index 000000000..249a9866c --- /dev/null +++ b/src/assets/img/tints/Windshield-NoShade-GrayTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Windshield-NoShade-GreenTint.svg b/src/assets/img/tints/Windshield-NoShade-GreenTint.svg new file mode 100644 index 000000000..ce17261a5 --- /dev/null +++ b/src/assets/img/tints/Windshield-NoShade-GreenTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Windshield-NoShade-NoTint.svg b/src/assets/img/tints/Windshield-NoShade-NoTint.svg new file mode 100644 index 000000000..5a9bc647f --- /dev/null +++ b/src/assets/img/tints/Windshield-NoShade-NoTint.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/img/tints/Windshield-NoShade-Privacy.svg b/src/assets/img/tints/Windshield-NoShade-Privacy.svg new file mode 100644 index 000000000..ca1f024ac --- /dev/null +++ b/src/assets/img/tints/Windshield-NoShade-Privacy.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index e066a8c6a..eb199cdf4 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -14,7 +14,7 @@ :key="answer.Name ? answer.Name : answer" @isCheckedChanged="handleCheckedChanged" :buttonID="answer.Name ? groupName + '-' + answer.Name : groupName + '-' + answer" - :value="answer.Name ? answer.Name : answer" + :value="getValues(answer)" :buttonLabel="answer.Text ? answer.Text : answer" :buttonLabelSubCopy="answer.SubText" :textPosition="textPosition" @@ -23,8 +23,8 @@ :selectingInitiatesLoad="selectingInitiatesLoad" :loaderColor="loaderColor" :loaderPosition="loaderPosition" - :isWide="isWide" - :isRequired="isRequired" + :isWide=isWide + :isRequired=isRequired :buttonImage="answer.AnswerImageUrl" :buttonImageId="answer.ImageId" :altText="answer.Name ? answer.Name : answer" @@ -80,6 +80,7 @@ export default { modelValue: Array, validationRules: String, suppressError: Boolean, + useTextForValue: Boolean, }, computed: { getFieldSetClasses() { @@ -122,6 +123,12 @@ export default { }, }, methods: { + getValues(answer){ + if (this.useTextForValue){ + return answer.Text + } + return answer.Name ? answer.Name : answer; + }, handleCheckedChanged(val) { if(this.isMultiSelect && this.selectedValues) { // Add or remove item to array of data to emit diff --git a/src/constants/damage-locations-cms.js b/src/constants/damage-locations-cms.js new file mode 100644 index 000000000..c4787a598 --- /dev/null +++ b/src/constants/damage-locations-cms.js @@ -0,0 +1,9 @@ +const damageLocationsCms = { + WINDSHIELD: "WINDSHIELD", + SIDEDOOR: "SIDEDOOR", + REARWINDOW: "REARWINDOW", + DRIVERSIDE: "DRIVERSIDE", + PASSENGERSIDE: "PASSENGERSIDE", + }; + + export { damageLocationsCms }; \ No newline at end of file diff --git a/src/constants/damage-locations-parts.js b/src/constants/damage-locations-parts.js new file mode 100644 index 000000000..465d58420 --- /dev/null +++ b/src/constants/damage-locations-parts.js @@ -0,0 +1,8 @@ +const damageLocationsParts = { + WINDSHIELD: "Windshield", + DRIVER: "Driver", + PASSENGER: "Passenger", + REAR: "Rear", + }; + + export { damageLocationsParts }; \ No newline at end of file diff --git a/src/constants/dynamictext-mapper.js b/src/constants/dynamictext-mapper.js new file mode 100644 index 000000000..b450d0561 --- /dev/null +++ b/src/constants/dynamictext-mapper.js @@ -0,0 +1,57 @@ +// Used to support custom (non-state) related dynamic text. +// Example: {custom:glassName}, each key in the array is the value after custom:, like 'glassname' +// Please use lowercase only so that we don't have to worry about case sensitivity. + +const customMappings = { + formattedglassname: [ + { key: 'Windshield Single', transformedValue: 'windshield' }, + { key: 'Windshield Driver', transformedValue: 'driver side split windshield' }, + { key: 'Windshield Passenger', transformedValue: 'passenger side split windshield' }, + { key: 'Rear Stationary', transformedValue: 'rear window' }, + { key: 'Rear Slider', transformedValue: 'rear window' }, + { key: 'Driver Front', transformedValue: 'driver side front door' }, + { key: 'Driver Back ', transformedValue: 'driver side back door' }, + { key: 'Driver Vent', transformedValue: 'driver side vent glass' }, + { key: 'Driver Quarter', transformedValue: 'driver side quarter panel' }, + { key: 'Driver SideDoor', transformedValue: 'driver side sliding door' }, + { key: 'Passenger Front', transformedValue: 'passenger side front door' }, + { key: 'Passenger Back', transformedValue: 'passenger side back door' }, + { key: 'Passenger Vent', transformedValue: 'passenger side vent glass' }, + { key: 'Passenger Quarter', transformedValue: 'passenger side quarter panel' }, + { key: 'Passenger SlideDoor', transformedValue: 'passenger side sliding door' }, + ] +} + +// Gets an instance of a string where the dynamic portion of the text {custom:KeyName} +// is replaced by a value from the above map. +// If the value isn't found, return the original dynamic string without replacement +export function getCustomTransformValue(dynamicString, key) { + + // Get array key from the dynamic string + const regexExp = new RegExp("{(.*?):(.*?)}", "g"); + const matches = [...dynamicString.matchAll(regexExp)]; + + if (matches.length === 0) { + return dynamicString; + } + + const arrayKey = matches[0][2]; + + // Get the array of possible values based on the key name. + const transformArray = customMappings[arrayKey.toLowerCase()]; + + if(transformArray === undefined) { + return dynamicString; + } + + // Get the value where the name matches the key name, there should only be one so find() is used. + const mapObject = transformArray.find(map => map.key.toLowerCase() === key.toLowerCase()); + + if(mapObject === undefined){ + return dynamicString; + } + + const finalString = dynamicString.replace(`{custom:${arrayKey}}`, mapObject.transformedValue); + + return finalString +} \ No newline at end of file diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 3f258a310..c547bd28c 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -43,6 +43,14 @@ const endpoints = { url: "/vehicle/api/v1/vehicle/Lookup", method: "POST", }, + GetPartsOrQuestions: { + url: "/parts/api/v1/parts/parts-or-questions", + method: "POST", + }, + SaveOrder: { + url: "/order/api/v1/order/save", + method: "POST", + } }; export { endpoints }; diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 177211ae0..97bd30bc0 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -11,12 +11,14 @@ const storeActions = { GET_EVOX_IMAGE: "getEvoxImage", LOOKUP_VEHICLE_BY_YMMS: "lookupVehicleByYmms", LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin", + GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions", + SAVE_ORDER: "saveOrder", // DEPENDENCY MUTATIONS - RESET_VEHICLE_AND_DEPS: "resetVehicleAndDependencies", - RESET_DAMAGE_AND_DEPS: "resetDamageAndDependencies", - RESET_REGISTRATION_AND_DEPS: "resetRegistrationAndDependencies", - RESET_PARTS_AND_DEPS: "resetPartsAndDependencies", + RESET_VEHICLE_STATE: "resetVehicleAndDependencies", + RESET_DAMAGE_STATE: "resetDamageAndDependencies", + RESET_REGISTRATION_STATE: "resetRegistrationAndDependencies", + RESET_PARTS_STATE: "resetPartsAndDependencies", }; export { storeActions }; diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index c572c4f69..c07e88bf2 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -10,16 +10,23 @@ const storeMutations = { UPDATE_VEHICLE_IMAGE_URL: "updateVehicleImageUrl", UPDATE_VEHICLE_IMAGE_VIF_NUMBER: "updateVehicleImageVifNumber", UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor", + UPDATE_IS_REPAIR: "updateIsRepair", + UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips", + UPDATE_GLASS_TO_REPLACE: "updateGlassToReplace", + UPDATE_PARTS: "updateParts", // 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", + RESET_VEHICLE_STATE: "resetVehicleState", + RESET_DAMAGE_STATE: "resetDamageState", + RESET_REGISTRATION_STATE: "resetRegistrationState", + RESET_PARTS_STATE: "resetPartsState", + + // OTHER MUTATIONS + UPDATE_PAGE_DATA: "updatePageData", }; diff --git a/src/constants/tint-mapper.js b/src/constants/tint-mapper.js new file mode 100644 index 000000000..949ab19a7 --- /dev/null +++ b/src/constants/tint-mapper.js @@ -0,0 +1,95 @@ + +// TintMap with array keys by location, lowercase to avoid as much string mismatching as possible. +// Src assumes you have a @/assets/img/tints/, making the final value @/assets/img/tints/{Src} in the markup. +// See vehicle-parts for implementation example. +const tintMap = { + other: [ + // Blue Shade + { name: "blue tint, blue shade", src: "Glass-BlueShade-BlueTint.svg" }, + { name: "brown tint, blue shade", src: "Glass-BlueShade-BrownTint.svg" }, + { name: "gray tint, blue shade", src: "Glass-BlueShade-GrayTint.svg" }, + { name: "green tint, blue shade", src: "Glass-BlueShade-GreenTint.svg" }, + { name: "blue shade", src: "Glass-BlueShade-NoTint.svg" }, + + // Brown Shade + { name: "brown tint, brown shade", src: "Glass-BrownShade-BrownTint.svg" }, + + // Gray Shade + { name: "blue tint, gray shade", src: "Glass-GrayShade-BlueTint.svg" }, + { name: "brown tint, gray shade", src: "Glass-GrayShade-BrownTint.svg" }, + { name: "gray tint, gray shade", src: "Glass-GrayShade-GrayTint.svg" }, + { name: "green tint, gray shade", src: "Glass-GrayShade-GreenTint.svg" }, + + // Green Shade + { name: "blue tint, green shade", src: "Glass-GreenShade-BlueTint.svg" }, + { name: "brown tint, green shade", src: "Glass-GreenShade-BrownTint.svg" }, + { name: "green tint, green shade", src: "Glass-GreenShade-GreenTint.svg" }, + + // Tints Only + { name: "blue tint", src: "Glass-NoShade-BlueTint.svg" }, + { name: "brown tint", src: "Glass-NoShade-BrownTint.svg" }, + { name: "dark brown tint", src: "Glass-NoShade-DarkBrownTint.svg" }, + { name: "dark gray tint", src: "Glass-NoShade-DarkGrayTint.svg" }, + { name: "gray tint", src: "Glass-NoShade-GrayTint.svg" }, + { name: "green tint", src: "Glass-NoShade-GreenTint.svg" }, + { name: "gray tint privacy", src: "Glass-NoShade-Privacy.svg" }, + + // No shade or tint + { name: "no shade, no tint", src: "Glass-NoShade-NoTint.svg" }, // ??? + ], + + windshield: [ + // Blue Shade + { name: "blue tint, blue shade", src: "Windshield-BlueShade-BlueTint.svg" }, + { name: "brown tint, blue shade", src: "Windshield-BlueShade-BrownTint.svg" }, + { name: "gray tint, blue shade", src: "Windshield-BlueShade-GrayTint.svg" }, + { name: "green tint, blue shade", src: "Windshield-BlueShade-GreenTint.svg" }, + { name: "blue shade", src: "Windshield-BlueShade-NoTint.svg" }, + + // Brown Shade + { name: "brown tint, brown shade", src: "Windshield-BrownShade-BrownTint.svg" }, + + // Gray Shade + { name: "blue tint, gray shade", src: "Windshield-GrayShade-BlueTint.svg" }, + { name: "brown tint, gray shade", src: "Windshield-GrayShade-BrownTint.svg" }, + { name: "gray tint, gray shade", src: "Windshield-GrayShade-GrayTint.svg" }, + { name: "green tint, gray shade", src: "Windshield-GrayShade-GreenTint.svg" }, + + // Green Shade + { name: "blue tint, green shade", src: "Windshield-GreenShade-BlueTint.svg" }, + { name: "brown tint, green shade", src: "Windshield-GreenShade-BrownTint.svg" }, + { name: "green tint, green shade", src: "Windshield-GreenShade-GreenTint.svg" }, + + // Tints Only + { name: "blue tint", src: "Windshield-NoShade-BlueTint.svg" }, + { name: "brown tint", src: "Windshield-NoShade-BrownTint.svg" }, + { name: "dark brown tint", src: "Windshield-NoShade-DarkBrownTint.svg" }, + { name: "dark gray tint", src: "Windshield-NoShade-DarkGrayTint.svg" }, + { name: "gray tint", src: "Windshield-NoShade-GrayTint.svg" }, + { name: "green tint", src: "Windshield-NoShade-GreenTint.svg" }, + { name: "gray tint privacy", src: "Windshield-NoShade-Privacy.svg" }, + + // No shade or tint + { name: "no shade, no tint", src: "Windshield-NoShade-NoTint.svg" }, // ??? + + ] +} + +// Gets the tint image source string given the glass location, and the tint description (like 'Green Tint') +// Use lowered strings here to try to avoid mismatch. Returns an empty string if array key doesn't exist. +// Returns undefined if no items are found. +export function getTintImage(glassLocation, colorString) { + + // Windshield glass has special images, all other glass uses the same though. + if(glassLocation.toLowerCase() !== 'windshield'){ + glassLocation = 'other'; + } + + if (tintMap[glassLocation.toLowerCase()] === undefined) { + return ''; + } + + const tintImageSource = tintMap[glassLocation.toLowerCase()].find(item => item.name.toLowerCase() == colorString.toLowerCase()) + + return tintImageSource; +} \ No newline at end of file diff --git a/src/global-methods.js b/src/global-methods.js index 9df098b7d..788d4c2e4 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -19,7 +19,11 @@ export default { }).then( (response) => { if (response.status == httpStatusCodes.OK) { - resolve(response); + if(response.data == undefined) { + reject(response); + }else{ + resolve(response); + } } else { reject(response); } diff --git a/src/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js index 4e5d86786..1509a4b16 100644 --- a/src/helpers/unit-test-helper.js +++ b/src/helpers/unit-test-helper.js @@ -2,11 +2,28 @@ 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"; +import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; +import baseMixin from "@/mixins/base-mixin"; export function getMountOptions(mockData) { // Define our mocks to attached to the 'global' object for Vue/Jest. const mocks = {}; + //this is mocking if you use the mixin directly(baseMixin.methods.dispatchNonBlockingStoreAction) vs this.dispatchNonBlockingStoreAction + if (mockData.actionList !== undefined) { + baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn(); + baseMixin.methods.dispatchNonBlockingStoreAction.mockImplementation((actionName) => { + let actionFilterResult = mockData.actionList.filter( + (x) => x.actionName == actionName + ); + + if (actionFilterResult.length > 0 && actionFilterResult.length === 1) { + return Promise.resolve({ + data: actionFilterResult[0].data, + }); + } + }); + } mocks.dispatchNonBlockingStoreAction = jest.fn(); mocks.dispatchNonBlockingStoreAction.mockImplementation((actionName) => { let actionFilterResult = mockData.actionList.filter( @@ -25,10 +42,13 @@ export function getMountOptions(mockData) { mocks.storeMutations = storeMutations; mocks.navigationScenarios = navigationScenarios; mocks.vehicleCategories = vehicleCategories; + mocks.fmgPageValues = fmgPageValues; // Mock $store and $router when accessing this.$store/$router mocks.$store = mockData.store; mocks.$router = mockData.router; + mocks.$route = mockData.route; + const global = { mocks: mocks, diff --git a/src/layouts/form-test/form-test.vue b/src/layouts/form-test/form-test.vue index f12abb261..774d863a8 100644 --- a/src/layouts/form-test/form-test.vue +++ b/src/layouts/form-test/form-test.vue @@ -144,7 +144,7 @@ export default { }, resetDependentState() { // Invokes - store.dispatch(storeActions.RESET_PARTS_AND_DEPS); + store.dispatch(storeActions.RESET_PARTS_STATE); }, onSubmit(values) { window.alert('Success! Submitted values: ' + JSON.stringify(values, null ,2)) diff --git a/src/layouts/nested-radio-poc/nested-radio.vue b/src/layouts/nested-radio-poc/nested-radio.vue index 3bdeba866..d0a78d085 100644 --- a/src/layouts/nested-radio-poc/nested-radio.vue +++ b/src/layouts/nested-radio-poc/nested-radio.vue @@ -1,20 +1,18 @@ - - Ok, select the features - @@ -66,10 +64,11 @@ export default { .nested-radio { .ui-radio { flex-direction: column; - margin: .25rem 0; + margin: 0.25rem 0; } + p { - font-size: .875rem; + font-size: 0.875rem; } } diff --git a/src/layouts/part-questions/part-questions.vue b/src/layouts/part-questions/part-questions.vue new file mode 100644 index 000000000..75cf54527 --- /dev/null +++ b/src/layouts/part-questions/part-questions.vue @@ -0,0 +1,83 @@ + + + + + + Part Questions Page Placeholder + + + + + diff --git a/src/layouts/reveal/reveal.vue b/src/layouts/reveal/reveal.vue new file mode 100644 index 000000000..18f0e499d --- /dev/null +++ b/src/layouts/reveal/reveal.vue @@ -0,0 +1,71 @@ + + + + Reveal Confetti Page Placeholder + + + + + 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 index 92feec9b8..20c016a12 100644 --- 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 @@ -2,82 +2,81 @@ 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}); +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"]}]); - }); + 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: 'Windshield' }, { Name: 'SideDoor' } ]) + 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: 'Windshield' }, { Name: '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, + }, }); - 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 + //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/replace-options-question/replace-options-question.vue b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue index 978cf6b41..25eca62da 100644 --- a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue +++ b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue @@ -52,7 +52,7 @@ export default ({ }, updateSelectedValues() { // UPDATE SELECTEDVALUES IF ONLY ONE ANSWER - if(Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1 && this.selectedValues) { + if(Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1) { this.selectedValues = [this.answersToDisplay[0].Name]; } }, diff --git a/src/layouts/vehicle-damage/vehicle-damage.spec.js b/src/layouts/vehicle-damage/vehicle-damage.spec.js index 519d33457..3b5913e56 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.spec.js +++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js @@ -17,6 +17,7 @@ import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { nextTick } from "vue"; import { storeActions } from "@/constants/store-actions"; +import { storeMutations } from "@/constants/store-mutations"; import store from "@/store"; // Mock our module for promises. @@ -145,28 +146,6 @@ describe("vehicle-damage.vue", () => { }); }); -describe("vehicle-damage.vue", () => { - test("Call invalidation, ResetPartsAndDeps should be called", async () => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); - - wrapper.vm.resetDependentState(); - - //Assert - expect(store.dispatch).toBeCalledWith(storeActions.RESET_PARTS_AND_DEPS) - - }); -}); - describe("vehicle-damage.vue", () => { test("BackButtonAction triggers a router.navigate change", async () => { @@ -189,6 +168,303 @@ describe("vehicle-damage.vue", () => { }); }); +describe("vehicle-damage.vue", () => { + test("Replace several pieces of glass on ForwardButtonAction triggers a router.navigateAfterSave and saves selections to store", async () => { + //Arrange + const partsData = { + partsOrQuestions: [{ + glassName: "Single", + glassLocation: "Windshield", + parts: null, + partQuestions: [{ + questionSequence: 1, + questionText: "Is your vehicle equipped with a heated steering wheel?", + answers: [{ + answerText: "Yes", + nextQuestionSequence: null, + answerResult: "FW04957" + }, + { + answerText: "No", + nextQuestionSequence: 2, + answerResult: "" + }] + }, + { + questionSequence: 2, + questionText: "Is your vehicle equipped with a remote start?", + answers: [{ + answerText: "Yes", + nextQuestionSequence: null, + answerResult: "FW04181" + }, + { + answerText: "No", + nextQuestionSequence: null, + answerResult: "FW04179" + }] + }] + }] + }; + + const { wrapper } = setupMocks({ + pageHeaderWidgetHeaderText: "", + mountOptionsMockData: { + router: { navigateAfterSave: jest.fn(), }, + actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },], + }, + }); + + wrapper.vm.selectedDamageLocations = ["Windshield", "SideDoor", "RearWindow"]; + wrapper.vm.selectedWindshieldOptions = { + selectedWindshieldChipCount : null, + selectedWindshieldReplaceOptions : ["Single"], + selectedWindshieldDamageType: ["Replace"] + }; + + wrapper.vm.sideDoorOptionsData = { + selectedDoorSides: ["DriverSide", "PassengerSide"], + selectedDriverSideReplaceOptions: ["Back"], + selectedPassengerSideReplaceOptions: ["Quarter"] + }; + + wrapper.vm.selectedRearReplaceOptions = ["Stationary"]; + + const expectedGlassToReplace = [{location: "Windshield", name: "Single"}, {location: "Driver", name: "Back"}, + {location: "Passenger", name: "Quarter"}, {location: "Rear", name: "Stationary"}]; + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalled(); + expect(wrapper.vm.selectedGlassToReplace()).toEqual(expectedGlassToReplace); + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_IS_REPAIR, false); + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_TO_REPLACE, expectedGlassToReplace); + }); +}); + +describe("vehicle-damage.vue", () => { + test("Windshield replace with single part on ForwardButtonAction triggers a router.navigateAfterSave and saves selections to store", async () => { + //Arrange + const partsData = { partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + partQuestions: null, + parts: [ + { + color: "Green Tint, Green Shade", + description: "rain sensor, solar", + partNumber: "FW02728GGYN", + requiresCapabilityQuestions: false, + requiresRecalibration: false + } + ]}]}; + + const { wrapper } = setupMocks({ + pageHeaderWidgetHeaderText: "", + mountOptionsMockData: { + router: { navigateAfterSave: jest.fn(), }, + actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],}, + }); + + wrapper.vm.selectedDamageLocations = ["Windshield"]; + wrapper.vm.selectedWindshieldOptions = { + selectedWindshieldChipCount : null, + selectedWindshieldReplaceOptions : ["Single"], + selectedWindshieldDamageType: ["Replace"] + }; + + const expectedGlassToReplace = [{location: "Windshield", name: "Single"},]; + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalled(); + expect(wrapper.vm.selectedGlassToReplace()).toEqual(expectedGlassToReplace); + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_IS_REPAIR, false); + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_TO_REPLACE, expectedGlassToReplace); + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_PARTS, partsData.partsOrQuestions[0].parts); + }); +}); + +describe("vehicle-damage.vue", () => { + test("Windshield replace with multiple parts on ForwardButtonAction triggers a router.navigateAfterSave and saves selections to store", async () => { + //Arrange + const partsData = { partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + partQuestions: null, + parts: [ + { + color: "Green Tint, Green Shade", + description: "rain sensor, solar", + partNumber: "FW02728GGYN", + requiresCapabilityQuestions: false, + requiresRecalibration: false + }, + { + color: "Green Tint, Green Shade", + description: "rain sensor, solar, hydrophobic coating", + partNumber: "FW02760GGYN", + requiresCapabilityQuestions: false, + requiresRecalibration: false + } + ]}, + { + glassName: "Stationary", + glassLocation: "Rear", + parts: [ + { + partNumber: "DB09626GTYN", + description: "heated glass, solar, antenna", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null + }, + { + partNumber: "DB09821GTYN", + description: "heated glass, solar, antenna, onstar", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null + } + ], + "partQuestions": null + } + ]}; + + const { wrapper } = setupMocks({ + pageHeaderWidgetHeaderText: "", + mountOptionsMockData: { + router: { navigateAfterSave: jest.fn(), }, + actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],}, + }); + + wrapper.vm.selectedDamageLocations = ["Windshield"]; + wrapper.vm.selectedWindshieldOptions = { + selectedWindshieldChipCount : null, + selectedWindshieldReplaceOptions : ["Single"], + selectedWindshieldDamageType: ["Replace"] + }; + + const expectedGlassToReplace = [{location: "Windshield", name: "Single"},]; + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalled(); + expect(wrapper.vm.selectedGlassToReplace()).toEqual(expectedGlassToReplace); + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_IS_REPAIR, false); + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_TO_REPLACE, expectedGlassToReplace); + }); +}); + + +describe("vehicle-damage.vue", () => { + test("Windshield repair on ForwardButtonAction triggers a router.navigateAfterSave and saves selection to store", async () => { + //Arrange + const partsData = { partsOrQuestions: []}; + const { wrapper } = setupMocks({ + pageHeaderWidgetHeaderText: "", + mountOptionsMockData: { + router: { navigateAfterSave: jest.fn(), }, + actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },],}, + }); + + wrapper.vm.selectedDamageLocations = ["Windshield"]; + wrapper.vm.selectedWindshieldOptions = { + selectedWindshieldChipCount : [2], + selectedWindshieldDamageType: ["Repair"] + }; + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalled(); + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_IS_REPAIR, true); + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_NUMBER_OF_CHIPS, 2); + }); +}); + +describe("vehicle-damage.vue", () => { + test("isWindshieldDamageLocation is true when windshield is selected", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.selectedDamageLocations = ["Windshield"]; + + //Assert + expect(wrapper.vm.isWindshieldDamageLocation).toEqual(true); + }); +}); + +describe("vehicle-damage.vue", () => { + test("isSideDoorDamageLocation is true Side door is selected", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.selectedDamageLocations = ["Sidedoor"]; + + //Assert + expect(wrapper.vm.isSideDoorDamageLocation).toEqual(true); + }); +}); + + describe("vehicle-damage.vue", () => { test("isRearWindowDamageLocation is true if Rear Window damage is selected", async () => { @@ -207,6 +483,115 @@ describe("vehicle-damage.vue", () => { //Assert expect(wrapper.vm.isRearWindowDamageLocation).toEqual(true); + }); +}); + +describe("vehicle-damage.vue", () => { + test("isWindshieldRepair is true if windshield repair is selected", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.selectedDamageLocations = ["Windshield"]; + wrapper.vm.selectedWindshieldOptions = { selectedWindshieldDamageType: ["Repair"]}; + + //Assert + expect(wrapper.vm.isWindshieldRepair).toEqual(true); + }); +}); + +describe("vehicle-damage.vue", () => { + test("isWindshieldRepair is false if windshield damage is not selected", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.selectedDamageLocations = ["SideDoor"]; + wrapper.vm.selectedWindshieldOptions = { selectedWindshieldDamageType: ["Repair"]}; + + //Assert + expect(wrapper.vm.isWindshieldRepair).toEqual(false); + }); +}); + +describe("vehicle-damage.vue", () => { + test("isDriverSideReplace is true if side door driver is selected", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.selectedDamageLocations = ["Sidedoor"]; + wrapper.vm.sideDoorOptionsData = { selectedDoorSides: ["DriverSide"]}; + + //Assert + expect(wrapper.vm.isDriverSideReplace).toEqual(true); + }); +}); + +describe("vehicle-damage.vue", () => { + test("isPassengerSideReplace is true if side door passenger selected", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.selectedDamageLocations = ["Sidedoor"]; + wrapper.vm.sideDoorOptionsData = { selectedDoorSides: ["PassengerSide"]}; + + //Assert + expect(wrapper.vm.isPassengerSideReplace).toEqual(true); + }); +}); + +describe("vehicle-damage.vue", () => { + test("Call invalidation, ResetPartsAndState should be called", async () => { + + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.resetDependentState(); + + //Assert + expect(store.dispatch).toBeCalledWith(storeActions.RESET_PARTS_STATE) }); }); @@ -300,14 +685,14 @@ function setupMocks({ const sideDoorOptionsWrapper = wrapper.findComponent({ name: "sideDoorOptions" }); sideDoorOptionsWrapper.vm.initializeComponent = - sideDoorOptions.methods.initializeComponent; + sideDoorOptions.methods.initializeComponent; const windshieldOptionsWrapper = wrapper.findComponent({ name: "windshieldOptions", }); - + windshieldOptionsWrapper.vm.initializeComponent = - windshieldOptions.methods.initializeComponent; + windshieldOptions.methods.initializeComponent; const funnelSubHeaderWrapper = wrapper.findComponent({ name: "funnelSubHeader", @@ -319,13 +704,13 @@ function setupMocks({ name: "replaceOptionsQuestion", }); backGlassOptionsWrapper.vm.initializeComponent = - replaceOptionsQuestion.methods.initializeComponent; + replaceOptionsQuestion.methods.initializeComponent; const damageLocationQuestionWrapper = wrapper.findComponent({ name: "damageLocationQuestion", }); damageLocationQuestionWrapper.vm.initializeComponent = - damageLocationQuestion.methods.initializeComponent; + damageLocationQuestion.methods.initializeComponent; const funnelFooterWrapper = wrapper.findComponent({ name: "funnelFooter", diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index 203e46abb..a5de80389 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -46,7 +46,8 @@ import { settleAllPromises } from "@/helpers/layout-helper"; import { storeActions } from "@/constants/store-actions"; import store from "@/store"; import baseMixin from "@/mixins/base-mixin"; - +import { damageLocationsParts } from "@/constants/damage-locations-parts.js"; +import { damageLocationsCms } from "@/constants/damage-locations-cms.js"; export default { name: "vehicle-damage", @@ -102,14 +103,6 @@ export default { ); }); }, - computed: { - isRearWindowDamageLocation() { - return this.selectedDamageLocations.some(selectedDamages => - { - return selectedDamages.toUpperCase() === "REARWINDOW"; - }); - }, - }, data(){ return { selectedDamageLocations: [], @@ -127,8 +120,7 @@ export default { return store.getters.vehicle.carId !== null; }, resetDependentState() { - // Invokes - store.dispatch(storeActions.RESET_PARTS_AND_DEPS); + store.dispatch(storeActions.RESET_PARTS_STATE); }, backButtonAction() { // route to move backwards @@ -137,14 +129,140 @@ export default { this.$route ); }, - forwardButtonAction() { + async forwardButtonAction() { + var windshieldChipCount = this.selectedWindshieldOptions.selectedWindshieldChipCount && this.isWindshieldDamageLocation ? + this.selectedWindshieldOptions.selectedWindshieldChipCount[0] : null; + + store.commit(this.storeMutations.UPDATE_IS_REPAIR, this.isWindshieldRepair); + + if (windshieldChipCount){ + store.commit(this.storeMutations.UPDATE_NUMBER_OF_CHIPS, parseInt(windshieldChipCount)); + } + + store.commit(this.storeMutations.UPDATE_GLASS_TO_REPLACE, this.selectedGlassToReplace()); + + var partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(this.storeActions.GET_PARTS_OR_QUESTIONS, + { carId: store.getters.vehicle.carId, glassArray: this.selectedGlassToReplace()}, false); + + /******** START CSR-98 ********/ + var savedOrderInfo = await baseMixin.methods.dispatchNonBlockingStoreAction(this.storeActions.SAVE_ORDER); + console.log(savedOrderInfo) + /******** END CSR-98 ********/ + + this.navigateForward(partsData); + }, + navigateForward(partsData){ + /******** START CSR-98 ********/ console.log("Get estimate!") - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD, - this.$route - ); + + // this.$router.navigate( + // this.navigationScenarios.CLICKED_FORWARD, + // this.$route + // ); + /******** END CSR-98 ********/ + + + + //found problem questions + if (partsData.data.partsOrQuestions.some(pq => pq.partQuestions != null && pq.partQuestions.length > 0)){ + this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_PART_QUESTIONS, this.$route, {}, {}, partsData.data); + return; + } + + //found multiple parts for a single glass location (Windshield, Driver, Passenger, Rear) + if (partsData.data.partsOrQuestions){ + var multiParts = false; + partsData.data.partsOrQuestions.forEach(pq => { + if (pq.parts != null && pq.parts.length > 1){ + this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_MULTIPLE_PARTS, this.$route, {}, {}, partsData.data); + multiParts = true; + } + }); + if (multiParts) return; + } + + //if not a repair then there should only be 1 part and no problem questions at this point so save the part to the store. + if (!this.isWindshieldRepair){ + store.commit(this.storeMutations.UPDATE_PARTS, partsData.data.partsOrQuestions[0].parts); + } + + this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART, this.$route); + }, + + selectedGlassToReplace() { + var selectedGlassToReplace = []; + if (this.isWindshieldDamageLocation && !this.isWindshieldRepair){ + this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach(wsItem => { + selectedGlassToReplace.push({ location: damageLocationsParts.WINDSHIELD, name: wsItem}); + }) + } + + if (this.isDriverSideReplace){ + this.sideDoorOptionsData.selectedDriverSideReplaceOptions.forEach(driverItem => { + selectedGlassToReplace.push({ location: damageLocationsParts.DRIVER, name: driverItem}); + }) + } + + if (this.isPassengerSideReplace){ + this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach(passengerItem => { + selectedGlassToReplace.push({ location: damageLocationsParts.PASSENGER, name: passengerItem}); + }) + } + + if (this.isRearWindowDamageLocation) { + this.selectedRearReplaceOptions.forEach(rearItem => { + selectedGlassToReplace.push({ location: damageLocationsParts.REAR, name: rearItem}); + }) + } + + return selectedGlassToReplace; + }, + }, + computed:{ + isWindshieldDamageLocation() { + return this.selectedDamageLocations.some(selectedDamages => + { + return selectedDamages.toUpperCase() === damageLocationsCms.WINDSHIELD; + }); + }, + isSideDoorDamageLocation() { + return this.selectedDamageLocations.some(selectedDamages => + { + return selectedDamages.toUpperCase() === damageLocationsCms.SIDEDOOR; + }); + }, + isRearWindowDamageLocation() { + return this.selectedDamageLocations.some(selectedDamages => + { + return selectedDamages.toUpperCase() === damageLocationsCms.REARWINDOW; + }); + }, + isWindshieldRepair() { + if (!this.isWindshieldDamageLocation) return false; + + return this.selectedWindshieldOptions.selectedWindshieldDamageType.some(selectedDamageType => + { + return selectedDamageType.toUpperCase() === "REPAIR"; + }); + }, + isDriverSideReplace() { + if (!this.isSideDoorDamageLocation) return false; + + return this.sideDoorOptionsData.selectedDoorSides.some(selectedDriverSide => + { + return selectedDriverSide.toUpperCase() === damageLocationsCms.DRIVERSIDE; + }); + }, + isPassengerSideReplace() { + if (!this.isSideDoorDamageLocation) return false; + + return this.sideDoorOptionsData.selectedDoorSides.some(selectedPassengerSide => + { + return selectedPassengerSide.toUpperCase() === damageLocationsCms.PASSENGERSIDE; + }); } }, + components: { funnelHeader, funnelFooter, diff --git a/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue b/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue index 347ee2fdf..3226d9656 100644 --- a/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue +++ b/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue @@ -6,6 +6,7 @@ :answers="answersFromCms" :groupName="groupName" buttonType="listButtonHorizontal" + useTextForValue v-model="selectedChipCountValues" /> diff --git a/src/layouts/vehicle-make/vehicle-make.spec.js b/src/layouts/vehicle-make/vehicle-make.spec.js index b2c4dcaf7..ab04bb1d2 100644 --- a/src/layouts/vehicle-make/vehicle-make.spec.js +++ b/src/layouts/vehicle-make/vehicle-make.spec.js @@ -223,8 +223,8 @@ describe("vehicle-make.vue", () => { expect(store.commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null) expect(store.commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null) - expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_AND_DEPS) - expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_AND_DEPS) + expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE) + expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_STATE) }); }); diff --git a/src/layouts/vehicle-make/vehicle-make.vue b/src/layouts/vehicle-make/vehicle-make.vue index c91379318..41c19a64c 100644 --- a/src/layouts/vehicle-make/vehicle-make.vue +++ b/src/layouts/vehicle-make/vehicle-make.vue @@ -94,8 +94,8 @@ export default { store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); // Invokes - store.dispatch(storeActions.RESET_DAMAGE_AND_DEPS); - store.dispatch(storeActions.RESET_REGISTRATION_AND_DEPS); + store.dispatch(storeActions.RESET_DAMAGE_STATE); + store.dispatch(storeActions.RESET_REGISTRATION_STATE); }, }, diff --git a/src/layouts/vehicle-model/vehicle-model.spec.js b/src/layouts/vehicle-model/vehicle-model.spec.js index 7f8f96eb6..4e40a89cb 100644 --- a/src/layouts/vehicle-model/vehicle-model.spec.js +++ b/src/layouts/vehicle-model/vehicle-model.spec.js @@ -208,8 +208,8 @@ describe("vehicle-model.vue", () => { expect(store.commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null) expect(store.commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null) - expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_AND_DEPS) - expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_AND_DEPS) + expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE) + expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_STATE) }); }); diff --git a/src/layouts/vehicle-model/vehicle-model.vue b/src/layouts/vehicle-model/vehicle-model.vue index 02de04c3b..f57429c8b 100644 --- a/src/layouts/vehicle-model/vehicle-model.vue +++ b/src/layouts/vehicle-model/vehicle-model.vue @@ -94,8 +94,8 @@ export default { store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); // Invokes - store.dispatch(storeActions.RESET_DAMAGE_AND_DEPS); - store.dispatch(storeActions.RESET_REGISTRATION_AND_DEPS); + store.dispatch(storeActions.RESET_DAMAGE_STATE); + store.dispatch(storeActions.RESET_REGISTRATION_STATE); }, }, diff --git a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.spec.js b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.spec.js new file mode 100644 index 000000000..ec687023c --- /dev/null +++ b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.spec.js @@ -0,0 +1,122 @@ +import { shallowMount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass-part-question.vue"; +import store from "@/store"; + +jest.mock("@/store", () => { return {}; }, { virtual: true }); + +const featureListData = { + colorAnswersProp: [ + { ColorAnswerText: 'Green Tint', FeatureAnswers: [{ FeatureAnswerText: "heated glass, solar, 1 hole", PartNumber: "DB12209GTYN" }] }, + { ColorAnswerText: 'Gray Tint Privacy', FeatureAnswers: [{ FeatureAnswerText: "heated glass, solar, 1 hole", PartNumber: "DB12209YPYN" }] } + ], + glassLocationProp: "Rear", + glassNameProp: "Stationary", + modelValueProp: {} +} + +describe("glass-part-question.vue", () => { + + test("Part data passed in, should map data for ButtonQuestion (radio type)", async () => { + + //Arrange + const { wrapper } = setupMocks(featureListData); + + //Act + await wrapper.vm.$nextTick(); + + //Assert + expect(Object.keys(wrapper.vm.featureListData).length).toBe(2); + expect(wrapper.vm.featureListData['Green Tint'][0].Text).toBe("heated glass, solar, 1 hole"); + expect(wrapper.vm.featureListData['Green Tint'][0].Name).toBe("DB12209GTYN"); + expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Text).toBe("heated glass, solar, 1 hole"); + expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Name).toBe("DB12209YPYN"); + + }); + + test("Tint mapper, should get tint image by glassLocation and tintColor", async () => { + + //Arrange + + const { wrapper } = setupMocks(featureListData); + + //Act + await wrapper.vm.$nextTick(); + const tintSourceImage = wrapper.vm.getTintSourceImage("Rear", "Green Tint"); + + //Assert + expect(tintSourceImage).toBe("Glass-NoShade-GreenTint.svg"); + + }); + + + test("Tint mapper, should return empty string if no tint map found", async () => { + + //Arrange + + const { wrapper } = setupMocks(featureListData); + + //Act + await wrapper.vm.$nextTick(); + const tintSourceImage = wrapper.vm.getTintSourceImage("Rear", "Crazy Rainbow Tint"); + + //Assert + expect(tintSourceImage).toBe(""); + + }); + + test("Should emit updateModelValue, and have correct attributes", async () => { + + //Arrange + + const { wrapper } = setupMocks(featureListData); + + //Act + await wrapper.vm.$nextTick(); + const listCard = await wrapper.findComponent({ + name: "listCard", + }); + + wrapper.setValue({ selectedTint: 'Green Tint' }); + + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ selectedTint: 'Green Tint' }]); + expect(listCard.attributes("buttonid")).toBe("Rear-Stationary-Green Tint"); + expect(listCard.attributes("groupname")).toBe("Rear-Stationary"); + expect(listCard.attributes("isradio")).toBe("true"); + }); + + test("ResetTintAndPartSelections, should reset data elements ", async () => { + + //Arrange + const { wrapper } = setupMocks(featureListData); + + //Act + await wrapper.vm.$nextTick(); + wrapper.setData({selectedTint: {"Rear-Stationary" : 'Green Tint' } }); + + expect(wrapper.vm.selectedTint).toStrictEqual({"Rear-Stationary": 'Green Tint'}); + + await wrapper.vm.ResetTintAndPartSelections(); + expect(wrapper.vm.selectedTint).toStrictEqual({}); + }); + +}); + + +function setupMocks({ glassNameProp, glassLocationProp, colorAnswersProp, modelValueProp }) { + + //Mock store + const mountOptions = getMountOptions({}); + + mountOptions.propsData = { + glassName: glassNameProp, + glassLocation: glassLocationProp, + colorAnswers: colorAnswersProp, + modelValue: modelValueProp + }; + + const wrapper = shallowMount(glassPartQuestion, mountOptions); + + return { wrapper }; +} \ No newline at end of file diff --git a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue new file mode 100644 index 000000000..8e159aebe --- /dev/null +++ b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue @@ -0,0 +1,143 @@ + + + + {{ colorQuestionText }} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/layouts/vehicle-parts/vehicle-parts.spec.js b/src/layouts/vehicle-parts/vehicle-parts.spec.js new file mode 100644 index 000000000..5d22a458c --- /dev/null +++ b/src/layouts/vehicle-parts/vehicle-parts.spec.js @@ -0,0 +1,385 @@ +// Components +import vehicleParts from "@/layouts/vehicle-parts/vehicle-parts.vue"; +import funnelHeader from "@/common-components/funnel-header/funnel-header"; +import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; +import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; +import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; +import glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass-part-question"; + +// Supporting Files +import { settleAllPromises } from "@/helpers/layout-helper.js"; +import baseMixin from "@/mixins/base-mixin"; +import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; +import { shallowMount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import { nextTick } from "vue"; +import { storeActions } from "@/constants/store-actions"; +import { storeMutations } from "@/constants/store-mutations"; +import store from "@/store"; + +// Mock our module for promises. +jest.mock("@/helpers/layout-helper.js", () => ({ + settleAllPromises: jest.fn(), +})); + +// Mock fetchCmsContentForPage +jest.mock("@/helpers/cms-content-helper", () => ({ + fetchCmsContentForPage: jest.fn(), +})); + +// Mock store +jest.mock("@/store", () => { return {}; }, { virtual: true }); + +store.getters = { + pageData: jest.fn(), + damage: { + isRepair: false + } +}; + +const basePartResponse = { + partsOrQuestions: [ + { + glassName: "Stationary", + glassLocation: "Rear", + parts: [ + { + partNumber: "DB12209GTYN", + description: "heated glass, solar, 1 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null + }, + { + partNumber: "DB12209YPYNOEM", + description: "heated glass, solar, 1 hole", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null + } + ], + partQuestions: null + } + ] +} + + +describe("vehicle-parts.vue", () => { + + test("Page header is initialized with api data", async (done) => { + //Arrange + store.getters.pageData.mockReturnValueOnce({ + partsOrQuestions: [ + { + glassName: "Stationary", + glassLocation: "Rear", + parts: [ + { + partNumber: "DB12209GTYN", + description: "heated glass, solar, 1 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null + }, + { + partNumber: "DB12209YPYNOEM", + description: "heated glass, solar, 1 hole", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null + } + ], + partQuestions: null + } + ] + }); + + const pageHeaderWidgetHeaderText = "Select Parts"; + const { wrapper, apiPromise } = setupMocks( + { + pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText, + mountOptionsMockData: { + router: { + navigateAfterSave: jest.fn() + }, + route: { + query: { + fmgPage: 'vehicle-parts', + } + }, + store: { + getters: store.getters + }, + } + }); + + //Act + vehicleParts.beforeRouteEnter.call(wrapper.vm, + { query: { fmgPage: "vehicle-parts" } }, + undefined, + (c) => c(wrapper.vm) + ); + + //Assert + apiPromise.finally(() => { + expect(funnelSubHeader.methods.initializeComponent).toHaveBeenCalledWith(pageHeaderWidgetHeaderText); + done(); + }); + }); + + test("Page logo image is initialized 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", + }; + + store.getters.pageData.mockReturnValueOnce(basePartResponse); + + const { wrapper, apiPromise } = setupMocks( + { + SiteHeaderWidget: SiteHeaderWidget, + mountOptionsMockData: { + router: { + navigateAfterSave: jest.fn() + }, + route: { + query: { + fmgPage: 'vehicle-parts', + } + }, + store: { + getters: store.getters + }, + } + }); + + //Act + vehicleParts.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-parts" } }, + undefined, + (c) => c(wrapper.vm) + ); + + //Assert + apiPromise.finally(() => { + expect(funnelHeader.methods.initializeComponent).toHaveBeenCalledWith(SiteHeaderWidget); + done(); + }); + }); + + test("Vehicle image is initialized 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", + }; + + store.getters.pageData.mockReturnValue(basePartResponse); + + const { wrapper, apiPromise } = setupMocks( + { + VehicleBannerWidget: VehicleBannerWidget, + mountOptionsMockData: { + router: { + navigateAfterSave: jest.fn() + }, + route: { + query: { + fmgPage: 'vehicle-parts', + } + }, + store: { + getters: store.getters + }, + } + }); + + //Act + vehicleParts.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-parts" } }, + undefined, + (c) => c(wrapper.vm) + ); + + //Assert + apiPromise.finally(() => { + expect(vehicleBanner.methods.initializeComponent).toHaveBeenCalledWith(VehicleBannerWidget); + done(); + }); + }); + + test("PageData / isRepair populated in Vuex. arePagePrerequisitesValid should be true ", async () => { + + //Arrange + store.getters.pageData.mockReturnValueOnce(basePartResponse); + const { wrapper } = setupMocks({ + mountOptionsMockData: { + router: { + navigateAfterSave: jest.fn() + }, + route: { + query: { + fmgPage: 'vehicle-parts', + } + }, + store: { + getters: store.getters + }, + } + }); + + //Act + vehicleParts.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-parts" } }, + undefined, + (c) => c(wrapper.vm) + ); + + let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + await nextTick(); + + //Assert + expect(arePagePrerequisitesValid).toBe(true); + }); + + test("BackButtonAction triggers a router.navigate change", async () => { + + //Arrange + store.getters.pageData.mockReturnValueOnce(basePartResponse); + const { wrapper } = setupMocks({ + mountOptionsMockData: { + router: { + navigateAfterSave: jest.fn(), + navigate: jest.fn() + }, + route: { + query: { + fmgPage: 'vehicle-parts', + } + }, + store: { + getters: store.getters + }, + } + }); + + //Act + vehicleParts.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-parts" } }, + undefined, + (c) => c(wrapper.vm) + ); + + wrapper.vm.backButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); + + }); + + test("ForwardButtonAction triggers a router.navigate change and saves selected parts to store", async () => { + + //Arrange + store.getters.pageData.mockReturnValueOnce(basePartResponse); + store.commit = jest.fn(); + + const { wrapper } = setupMocks({ + mountOptionsMockData: { + router: { + navigateAfterSave: jest.fn(), + navigate: jest.fn() + }, + route: { + query: { + fmgPage: 'vehicle-parts', + } + }, + store: { + getters: store.getters, + commit: store.commit + }, + } + }); + + wrapper.setData({ glassParts: { "Rear-Stationary": { "Rear": ['DB12209YPYNOEM'] } } }); + + //Act + vehicleParts.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-parts" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalled(); + // expect(wrapper.vm.selectedGlassToReplace()).toEqual(expectedGlassToReplace); + // expect(store.commit).toBeCalledWith(storeMutations.UPDATE_IS_REPAIR, false); + // expect(store.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_TO_REPLACE, expectedGlassToReplace); + }); +}); + +function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {} }) { + //Mock api responses + 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", + }, + ColorQuestionWidget: 'Please choose your rear window tint color', + FeatureQuestionWidget: 'Ok no choose features', + AlertWidget: { + BodyText: 'Please choose your tint color', + HeadlineText: 'Just a few more steps to go', + } + } + }; + + const apiPromise = Promise.resolve(apiResponses); + + settleAllPromises.mockImplementation(() => apiPromise); + fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); + + //Mock damage initialize methods + funnelHeader.methods = { initializeComponent: jest.fn() }; + vehicleBanner.methods = { initializeComponent: jest.fn() }; + funnelSubHeader.methods = { initializeComponent: jest.fn() }; + funnelFooter.methods = { initializeComponent: jest.fn() } + + const mountOptions = getMountOptions(mountOptionsMockData,); + const wrapper = shallowMount(vehicleParts, 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; + + const funnelFooterWrapper = wrapper.findComponent({ name: "funnelFooter", }); + funnelFooterWrapper.vm.initializeComponent = funnelFooter.methods.initializeComponent; + + const partQuestionRearWrapper = wrapper.findComponent({ name: "glassPartQuestion", }); + partQuestionRearWrapper.vm.initializeComponent = glassPartQuestion.methods.initializeComponent; + + return { wrapper, apiPromise }; +} diff --git a/src/layouts/vehicle-parts/vehicle-parts.vue b/src/layouts/vehicle-parts/vehicle-parts.vue new file mode 100644 index 000000000..aaa7f8620 --- /dev/null +++ b/src/layouts/vehicle-parts/vehicle-parts.vue @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/layouts/vehicle-style/vehicle-style.vue b/src/layouts/vehicle-style/vehicle-style.vue index 3009a696a..11653e25d 100644 --- a/src/layouts/vehicle-style/vehicle-style.vue +++ b/src/layouts/vehicle-style/vehicle-style.vue @@ -99,8 +99,8 @@ export default { }, resetDependentState() { // Invokes - store.dispatch(storeActions.RESET_DAMAGE_AND_DEPS); - store.dispatch(storeActions.RESET_REGISTRATION_AND_DEPS); + store.dispatch(storeActions.RESET_DAMAGE_STATE); + store.dispatch(storeActions.RESET_REGISTRATION_STATE); }, }, diff --git a/src/layouts/vehicle-year/vehicle-year.spec.js b/src/layouts/vehicle-year/vehicle-year.spec.js index 5ef3ea404..c04d61b17 100644 --- a/src/layouts/vehicle-year/vehicle-year.spec.js +++ b/src/layouts/vehicle-year/vehicle-year.spec.js @@ -163,8 +163,8 @@ describe("vehicle-year.vue", () => { expect(store.commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null) expect(store.commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null) - expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_AND_DEPS) - expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_AND_DEPS) + expect(store.dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE) + expect(store.dispatch).toBeCalledWith(storeActions.RESET_REGISTRATION_STATE) }); }); diff --git a/src/layouts/vehicle-year/vehicle-year.vue b/src/layouts/vehicle-year/vehicle-year.vue index e8e69c29a..d73ac46f9 100644 --- a/src/layouts/vehicle-year/vehicle-year.vue +++ b/src/layouts/vehicle-year/vehicle-year.vue @@ -95,8 +95,8 @@ export default { store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); // Invokes - store.dispatch(storeActions.RESET_DAMAGE_AND_DEPS); - store.dispatch(storeActions.RESET_REGISTRATION_AND_DEPS); + store.dispatch(storeActions.RESET_DAMAGE_STATE); + store.dispatch(storeActions.RESET_REGISTRATION_STATE); } }, components: { diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index d37ba38d7..d46841b6a 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -18,6 +18,9 @@ export default { return store.dispatch(type, payload); }, + savePageDataToStore(page, data){ + store.commit(storeMutations.UPDATE_PAGE_DATA, { page: page, data: data }); + } }, computed: { storeActions() { diff --git a/src/mixins/base-mixin.spec.js b/src/mixins/base-mixin.spec.js index bdc083399..da684af1b 100644 --- a/src/mixins/base-mixin.spec.js +++ b/src/mixins/base-mixin.spec.js @@ -26,6 +26,14 @@ describe("baseMixin.js", () => { expect(store.dispatch).toBeCalledWith(type, payload); }); + test("savePageDataToStore, should call store commit", () => { + const mixIn = getMixInInstance({}); + + mixIn.methods.savePageDataToStore('vehicle-year', {}); + + expect(store.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: 'vehicle-year', data: {}}); + }); + test("computed: storeActions should be equal to import object", () => { // Arrange const mixIn = getMixInInstance({}); @@ -91,9 +99,11 @@ function getMixInInstance({ isDispatchSuccess = true }) { // Attach mocks to mixin const baseMixIn = baseMixin; baseMixIn.methods.$route = route; - store.dispatch = storeDispatch; baseMixIn.methods.storeActions = storeActions; baseMixIn.methods.widgetNames = widgetNames; + store.dispatch = storeDispatch; + store.commit = jest.fn(); + return baseMixIn; } diff --git a/src/router/index.js b/src/router/index.js index 337c3f766..0d98f6698 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,6 +1,7 @@ // Supporting files import { createWebHistory, createRouter } from "vue-router"; import { storeActions } from "@/constants/store-actions"; +import baseMixin from "@/mixins/base-mixin"; import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js"; import { routingTable } from "@/router/router-constants/routing-table.js"; import { globalEvents, globalEventTypes } from "@/constants/events"; @@ -114,18 +115,18 @@ const router = createRouter({ //---------------------------------------------------------- Router Functions ---------------------------------------------------------- -router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}) => { - navigate(scenario, currentRoute, false, optionalQuery, optionalParams); +router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => { + navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData); } -router.navigateAfterSave = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}) => { - navigate(scenario, currentRoute, true, optionalQuery, optionalParams); +router.navigateAfterSave = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => { + navigate(scenario, currentRoute, true, optionalQuery, optionalParams, optionalPageData); } // PRIVATE FUNCTIONS // Navigate to the next route, depending on the scenario. -function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {}, optionalParams = {}) { +function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) { if (!scenario) { console.error("No scenario provided. Please review the routing table."); return; @@ -144,6 +145,9 @@ function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {}, resetDependentState(currentComponent); } + // Append page data to the store for the NEXT page, if any. It will be an empty object if none is provided. + baseMixin.methods.savePageDataToStore(matchingScenarioMap.destinationFmgPageValue, optionalPageData); + router.push({ name: "root", query: Object.assign(optionalQuery, { @@ -232,4 +236,4 @@ function resetDependentState(component) { return component.default.methods.resetDependentState(); } -export default router; +export default router; \ No newline at end of file diff --git a/src/router/router-constants/externalUrl-values.js b/src/router/router-constants/externalUrl-values.js new file mode 100644 index 000000000..7e9931f6e --- /dev/null +++ b/src/router/router-constants/externalUrl-values.js @@ -0,0 +1,5 @@ +const externalUrls = { + HERITAGE_FUNNEL: process.env.VUE_APP_HERITAGE_FUNNEL, +}; + +export { externalUrls }; \ No newline at end of file diff --git a/src/router/router-constants/fmgPage-values.js b/src/router/router-constants/fmgPage-values.js index 9b82cdf7e..770c1f7b6 100644 --- a/src/router/router-constants/fmgPage-values.js +++ b/src/router/router-constants/fmgPage-values.js @@ -4,6 +4,9 @@ const fmgPageValues = { VEHICLE_MODEL: "vehicle-model", VEHICLE_STYLE: "vehicle-style", VEHICLE_DAMAGE: "vehicle-damage", + VEHICLE_PARTS: "vehicle-parts", + PART_QUESTIONS: "part-questions", + REVEAL : "reveal", }; -export { fmgPageValues }; +export { fmgPageValues }; \ No newline at end of file diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index 338998d0d..55efdcaec 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -4,7 +4,11 @@ const navigationScenarios = { SELECTED_MAKE: "SELECTED_MAKE", SELECTED_STYLE: "SELECTED_STYLE", CLICKED_BACK: "CLICKED_BACK", - CLICKED_FORWARD: "CLICKED_FORWARD" + CLICKED_FORWARD: "CLICKED_FORWARD", + SELECTED_PARTS: "SELECTED_PARTS", + SELECTED_DAMAGE_WITH_SINGLE_PART: "SELECTED_DAMAGE_WITH_SINGLE_PART", + SELECTED_DAMAGE_WITH_MULTIPLE_PARTS: "SELECTED_DAMAGE_WITH_MULTIPLE_PARTS", + SELECTED_DAMAGE_WITH_PART_QUESTIONS: "SELECTED_DAMAGE_WITH_PART_QUESTIONS" }; export { navigationScenarios }; diff --git a/src/router/router-constants/router-parameter-keys.js b/src/router/router-constants/router-parameter-keys.js new file mode 100644 index 000000000..0c75eb738 --- /dev/null +++ b/src/router/router-constants/router-parameter-keys.js @@ -0,0 +1,3 @@ +const routerParameterKeys = { }; + + export { routerParameterKeys }; \ No newline at end of file diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 600def25d..2712c53d2 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -1,4 +1,5 @@ import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; +import { externalUrls } from "@/router/router-constants/externalUrl-values"; import { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; const routingTable = [ @@ -59,7 +60,32 @@ const routingTable = [ }, { scenario: navigationScenarios.CLICKED_FORWARD, - destinationUrl: "http://localhost:38000/default.aspx", + destinationUrl: externalUrls.HERITAGE_FUNNEL, + }, + { + scenario: navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART, + destinationFmgPageValue: fmgPageValues.REVEAL + }, + { + scenario: navigationScenarios.SELECTED_DAMAGE_WITH_MULTIPLE_PARTS, + destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS, + }, + { + scenario: navigationScenarios.SELECTED_DAMAGE_WITH_PART_QUESTIONS, + destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, + }, + ], + }, + { + fmgPageValue: fmgPageValues.VEHICLE_PARTS, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + }, + { + scenario: navigationScenarios.SELECTED_PARTS, + destinationFmgPageValue: fmgPageValues.QUOTE, }, ], }, diff --git a/src/store/index.js b/src/store/index.js index c7227de47..cbab3a84a 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -25,9 +25,16 @@ export const state = { numberOfChips: null, glassToReplace: null, }, + lineItems:{ + glassParts: null, + otherParts: null + }, + referralSeqNum: null, + correlationId: null, }, applicationUser: { eventBus: [], + pageData: {} }, } @@ -61,6 +68,21 @@ export const mutations = { updateVehicleImageColor(state, imageColor) { state.order.vehicle.imageColor = imageColor; }, + updateIsRepair(state, isRepair){ + state.order.damage.isRepair = isRepair; + }, + updateNumberOfChips(state, numberOfChips){ + state.order.damage.numberOfChips = numberOfChips; + }, + updateGlassToReplace(state, glassToReplace){ + state.order.damage.glassToReplace = glassToReplace; + }, + updateParts(state, partsData){ + state.order.lineItems.glassParts = partsData; + }, + updatePageData(state, pageData){ + state.applicationUser.pageData[pageData.page] = pageData.data; + }, // EVENT BUS MUTATIONS addEventToBus(state, event) { @@ -81,7 +103,7 @@ export const mutations = { }, // DEPENDENCY MUTATIONS - resetVehicleAndDependencies(state) { + resetVehicleState(state) { state.order.vehicle.year = null; state.order.vehicle.make = null; state.order.vehicle.model = null; @@ -89,7 +111,7 @@ export const mutations = { state.order.vehicle.carId = null; state.order.vehicle.category = null; }, - resetDamageAndDependencies(state) { + resetDamageState(state) { state.order.damage.isRepair = null; state.order.damage.numberOfChips = null; state.order.damage.windshieldGlassToReplace = null; @@ -98,11 +120,13 @@ export const mutations = { state.order.damage.rearGlassToReplace = null; }, - resetRegistrationAndDependencies(state) { + resetRegistrationState(state) { }, - resetPartsAndDependencies(state) { - + resetPartsState(state) { + state.glassParts = null; + state.childParts = null; + state.otherParts = null; } } @@ -118,6 +142,11 @@ export const getters = { return matchedEvent !== undefined ? matchedEvent.eventValue : undefined; }, eventBus: (state) => state.applicationUser.eventBus, + damage: (state) => state.order.damage, + lineItems: (state) => state.order.lineItems, + pageData: (state) => (page) => { + return state.applicationUser.pageData[page]; + } } // Export Actions @@ -193,20 +222,20 @@ export const actions = { // DEPENDENCY ACTIONS resetVehicleAndDependencies(context) { - context.commit(storeMutations.RESET_VEHICLE_AND_DEPS); - context.commit(storeMutations.RESET_DAMAGE_AND_DEPS); - context.commit(storeMutations.RESET_REGISTRATION_AND_DEPS); + context.commit(storeMutations.RESET_VEHICLE_STATE); + context.commit(storeMutations.RESET_DAMAGE_STATE); + context.commit(storeMutations.RESET_REGISTRATION_STATE); }, resetDamageAndDependencies(context) { - context.commit(storeMutations.RESET_DAMAGE_AND_DEPS); - context.commit(storeMutations.RESET_PARTS_AND_DEPS); + context.commit(storeMutations.RESET_DAMAGE_STATE); + context.commit(storeMutations.RESET_PARTS_STATE); }, resetRegistrationAndDependencies(context) { - context.commit(storeMutations.RESET_REGISTRATION_AND_DEPS); - context.commit(storeMutations.RESET_PARTS_AND_DEPS) + context.commit(storeMutations.RESET_REGISTRATION_STATE); + context.commit(storeMutations.RESET_PARTS_STATE) }, resetPartsAndDependencies(context) { - context.commit(storeMutations.RESET_PARTS_AND_DEPS); + context.commit(storeMutations.RESET_PARTS_STATE); }, // Content API Actions @@ -239,6 +268,41 @@ export const actions = { payload: {}, }); }, + + // Parts API Actions + getPartsOrQuestions(context, { carId, glassArray, zipCode, vin = '' }) { + return globalMethods.callHttpClient({ + method: endpoints.GetPartsOrQuestions.method, + endpoint: endpoints.GetPartsOrQuestions.url, + payload: { + carId: carId, + glass: glassArray, + zip: zipCode, + vin: vin + }, + }); + }, + + // Order API Actions + saveOrder(context) { + const vehicle = context.getters.vehicle; + const damage = context.getters.damage; + + return globalMethods.callHttpClient({ + method: endpoints.SaveOrder.method, + endpoint: endpoints.SaveOrder.url, + payload: { + vehicle: { + carId: vehicle.carId, + year: vehicle.year, + make: vehicle.make, + model: vehicle.model, + style: vehicle.style, + }, + numberOfChips: damage.numberOfChips, + }, + }); + }, } export default createStore({ @@ -252,4 +316,4 @@ export default createStore({ mutations, getters, actions, -}); +}); \ No newline at end of file diff --git a/src/store/store.spec.js b/src/store/store.spec.js index f33595e58..ccb715e50 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -100,7 +100,7 @@ describe("Mutations", () => { expect(storeState.applicationUser.eventBus).toEqual([{ EventOne: "ValueOne" }]); }); - it("resetVehicleAndDependencies, should set fields to null", () => { + it("resetVehicleState, should set fields to null", () => { // Arrange const storeState = state; @@ -120,7 +120,7 @@ describe("Mutations", () => { expect(storeState.order.vehicle.category).toEqual("CAR"); // Act - mutations.resetVehicleAndDependencies(storeState); + mutations.resetVehicleState(storeState); // Expect expect(storeState.order.vehicle.year).toEqual(null); @@ -132,7 +132,7 @@ describe("Mutations", () => { }); - it("resetDamageAndDependencies, should set fields to null", () => { + it("resetDamageState, should set fields to null", () => { // Arrange const storeState = state; @@ -154,7 +154,7 @@ describe("Mutations", () => { expect(storeState.order.damage.rearGlassToReplace).toEqual("Slider"); // Act - mutations.resetDamageAndDependencies(storeState); + mutations.resetDamageState(storeState); // Expect expect(storeState.order.damage.isRepair).toEqual(null); @@ -166,6 +166,50 @@ describe("Mutations", () => { }); + it("Updates number of chips in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateNumberOfChips(storeState, "1"); + + // Assert + expect(storeState.order.damage.numberOfChips).toEqual("1"); + }); + + it("Updates glass to replace in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateGlassToReplace(storeState, ["Windshield"]); + + // Assert + expect(storeState.order.damage.glassToReplace).toEqual(["Windshield"]); + }); + + it("Updates Parts in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateParts(storeState, { 'Windshield-Single': 'PARTNUM101'}); + + // Assert + expect(storeState.order.lineItems.glassParts).toEqual({ 'Windshield-Single': 'PARTNUM101'}); + }); + + it("Updates page data in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updatePageData(storeState, { page: 'vehicle-year', data: {} }); + + // Assert + expect(storeState.applicationUser.pageData['vehicle-year']).toEqual({}); + }); + }); describe("Actions", () => { @@ -313,9 +357,9 @@ describe("Actions", () => { // Act await actions.resetVehicleAndDependencies(context) - expect(commit).toBeCalledWith(storeMutations.RESET_VEHICLE_AND_DEPS); - expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_AND_DEPS); - expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_AND_DEPS); + expect(commit).toBeCalledWith(storeMutations.RESET_VEHICLE_STATE); + expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_STATE); + expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_STATE); }); @@ -330,8 +374,8 @@ describe("Actions", () => { // Act await actions.resetDamageAndDependencies(context) - expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_AND_DEPS); - expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_AND_DEPS); + expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_STATE); + expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_STATE); }); @@ -346,8 +390,8 @@ describe("Actions", () => { // Act await actions.resetRegistrationAndDependencies(context) - expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_AND_DEPS); - expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_AND_DEPS); + expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_STATE); + expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_STATE); }); @@ -362,7 +406,7 @@ describe("Actions", () => { // Act await actions.resetPartsAndDependencies(context) - expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_AND_DEPS); + expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_STATE); }); @@ -480,4 +524,41 @@ describe("Getters", () => { }); + it("Damage getter, should return damage data", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateGlassToReplace(storeState, ["Rear"]); + + // Assert + expect(getters.damage(storeState).glassToReplace).toEqual(["Rear"]); + + }); + + it("lineItems getter, should return lineItem data", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateParts(storeState, {"Rear-Stationary": 'PART101'}); + + // Assert + expect(getters.lineItems(storeState).glassParts).toEqual({"Rear-Stationary": 'PART101'}); + + }); + + + it("PageData getter, should return page data for specific page", () => { + // Arrange + const storeState = state; + + // Act + mutations.updatePageData(storeState, { page: 'vehicle-year', data: {} }); + + // Assert + expect(getters.pageData(storeState)('vehicle-year')).toEqual({}); + + }); + }); \ No newline at end of file diff --git a/src/styles/common-styles.scss b/src/styles/common-styles.scss index ae409dd36..47ea0815d 100644 --- a/src/styles/common-styles.scss +++ b/src/styles/common-styles.scss @@ -3,7 +3,6 @@ body { font-size: 16px; background-color: #fff; - .container-fluid { max-width: 576px; //Remove once desktop app is complete &.container-shadow { @@ -14,6 +13,9 @@ body { display: flex; flex-direction: column; } + .prevent-squish{ + overflow-x: unset; + } } .pointer { cursor: pointer; diff --git a/src/ux-components/list-card/list-card.spec.js b/src/ux-components/list-card/list-card.spec.js index 398c89c78..cee720503 100644 --- a/src/ux-components/list-card/list-card.spec.js +++ b/src/ux-components/list-card/list-card.spec.js @@ -204,11 +204,12 @@ describe("list-card.vue", () => { isRequired: true, isWide: false, modelValue: ["List Card Checkbox"], + buttonID: 'list-card-id' }, }); wrapper.vm.handleCheckChange(); // Assert - expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean}]); + expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean, buttonId: 'list-card-id'}]); }); it("Should set checkValue data if selectedButtonIDs has value(s)", async () => { diff --git a/src/ux-components/list-card/list-card.vue b/src/ux-components/list-card/list-card.vue index 858a3ef7b..c1fc6d089 100644 --- a/src/ux-components/list-card/list-card.vue +++ b/src/ux-components/list-card/list-card.vue @@ -49,7 +49,6 @@
Ok, select the features
{{ colorQuestionText }}