From df62156eaad6899e30ff1c981c32cdc73d755e11 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 29 Sep 2022 10:06:00 -0400 Subject: [PATCH 01/40] CSR-731 | Files created --- .../button-question/button-question.vue | 15 +- src/layouts/quote/quote.vue | 11 +- .../service-package-question.vue | 48 ++++++ .../service-package-radio.vue | 154 ++++++++++++++++++ 4 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 src/layouts/quote/service-package-question/service-package-question.vue create mode 100644 src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 6cd06c7c5..d6c94d8a8 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -61,6 +61,7 @@ import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-bu import listCard from "@/ux-components/list-card/list-card"; import { ErrorMessage } from 'vee-validate'; import radio from "@/ux-components/radio/radio"; +import servicePackageRadio from "@/layouts/quote/service-package-question/service-package-radio/service-package-radio"; export default { name: "buttonQuestion", @@ -121,7 +122,10 @@ export default { classes = "row g-2 justify-content-center"; break; case 'radio': - classes = 'ui-radio d-flex' + classes = 'ui-radio d-flex'; + break; + case 'servicePackageRadio': + classes = "ui-radio d-flex service-package"; break; } return classes; @@ -158,8 +162,12 @@ export default { return str.replace(" ", "-"); }, getValue(answer){ - if (this.useTextForValue) { return answer.Text } - return answer.Name ? answer.Name : answer; + if (this.useTextForValue) + { return answer.Text } + else if (this.buttonType == "servicePackageRadio") + { return answer } + else + { return answer.Name ? answer.Name : answer; } }, getAnswerString(answer, prop = "Name") { switch (typeof answer) { @@ -198,6 +206,7 @@ export default { listCard, ErrorMessage, radio, + servicePackageRadio }, }; diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index b7fa93080..ed67b9afc 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -24,6 +24,13 @@ groupName="CashOrInsuranceQuestion" /> + +
+ +
+ + + \ No newline at end of file diff --git a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue new file mode 100644 index 000000000..1a94d4334 --- /dev/null +++ b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue @@ -0,0 +1,154 @@ + + + + + From 4c8293133d7f074e88ffbbd4af2ff32a20f21a5c Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Mon, 3 Oct 2022 11:07:43 -0400 Subject: [PATCH 02/40] CSR-731 | Two-way binding finished Commit pre-styling changes --- .../button-question/button-question.vue | 4 + src/layouts/quote/quote.vue | 15 +- .../service-package-question.vue | 31 +-- .../service-package-radio.vue | 213 +++++++----------- 4 files changed, 112 insertions(+), 151 deletions(-) diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 585620114..92276c9d4 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -156,6 +156,9 @@ export default { case "radio": classes = "ui-radio d-flex"; break; + case "servicePackageRadio": + classes = "ui-radio d-flex service-package"; + break; } return classes; }, @@ -243,6 +246,7 @@ export default { listCard, ErrorMessage, radio, + servicePackageRadio, }, }; diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index ed67b9afc..30fb48b35 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -156,8 +156,10 @@ export default { // Call the "next" function to complete the transition to this page. next((vm) => { + console.log("cmsContent", cmsContent); vm.setCmsContent(cmsContent); vm.isInsurance = vm.getDefaultIsInsuranceValue(); + vm.selectedPackage = vm.getDefaultSelectedPackageValue(); }); }, data(){ @@ -165,6 +167,13 @@ export default { cashOrInsuranceThreshhold: 600, pricingRequestResult: null, isInsurance: null, + selectedPackage: null, + } + }, + watch: { + selectedPackage(newPackage, oldPackage) { + console.log("new package: ", newPackage); + console.log("old package: ", oldPackage); } }, methods: { @@ -178,7 +187,11 @@ export default { } else { return this.economyPackagePrice > this.cashOrInsuranceThreshhold; } - } + }, + getDefaultSelectedPackageValue() { + // placeholder - to be populated via CSR-774 + return null; + }, }, computed: { years() { diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index ac2f31aec..a73bdd0e1 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -23,23 +23,28 @@ export default ({ }, computed: { cashAnswersFromCms(){ - console.log('Cash answers',this.getCmsContent(this.cashCmsWidgetName, 'Answers')); - return this.getCmsContent(this.cashCmsWidgetName, 'Answers'); + let cashAnswers = ""; + let cmsContent = this.getCmsContent(this.cashCmsWidgetName, 'Answers'); + if (cmsContent) { + cashAnswers = cmsContent?.map((x) => ({ + buttonLabel : x.Name, + value : x.SubWidgetName + })); + } + + return cashAnswers; }, insuranceAnswersFromCms() { return this.getCmsContent(this.insuranceCmsWidgetName, 'Answers'); }, - // selectedValues: { - // get: function() { - // // Convert to CMS answer name from bool - // var cmsAnswerValue = this.modelValue ? insuranceAnswer : cashAnswer; - // return cmsAnswerValue; - // }, - // set: function(newValue) { - // // Convert back to bool for parent component - // this.$emit("update:modelValue", newValue === insuranceAnswer); - // } - // }, + selectedValues: { + get: function() { + return this.modelValue; + }, + set: function(newValue) { + this.$emit("update:modelValue", newValue); + } + }, }, components: { buttonQuestion, diff --git a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue index 1a94d4334..70a0a71d5 100644 --- a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue +++ b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue @@ -1,154 +1,93 @@ - From a60398a55bb7717427c0eb8e7f9e85211400c666 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Wed, 12 Oct 2022 10:53:12 -0400 Subject: [PATCH 03/40] CSR-731 | Working version before refactor --- .../button-question/button-question.vue | 10 +- src/constants/dynamic-strings.js | 3 +- src/helpers/cms-content-helper.js | 103 +++++- .../license-plate-lookup.vue | 1 + src/layouts/quote/quote.vue | 79 +++-- .../service-package-question.vue | 56 ++- .../service-package-radio.vue | 333 +++++++++++++++--- src/mixins/base-mixin.js | 8 + src/mixins/input-button-wrapper-mixin.js | 4 +- src/store/index.js | 7 +- 10 files changed, 488 insertions(+), 116 deletions(-) diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 92276c9d4..be8c1fb82 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -52,7 +52,8 @@ :selectOnKeypress="selectOnKeypress" @blur="handleBlur" @focus="handleFocus" - @buttonClicked="handleAnswerChange" /> + @buttonClicked="handleAnswerChange" + :additionalData="additionalData" />
^.+?)(?=(?:{if))|(?{if:)(?.*?:)(?.*?)}(?.*?)(?=(?:{(?:end|else|if:.*?)}))|(?{else})(?.*?)(?=(?:{(?:end|else|if:.*?)}))|(?{end})(?.*?)(?=(?:{(?:end|else|if:.*?)})|$)", "g"); + const regexMatches = [...str.matchAll(regexExp)]; + // Find and evaluate full globalState if statements + if (regexMatches.length) { + regexMatches.forEach((match, index) => { + if(match.groups.ifOperator) { + if(regexMatches[index+1].groups.endOperator) { + processIfStatement(regexMatches.slice(index, index+2), ifConditionString, replaceStringCallback) + regexMatches[index+1].groups.endTrailingString; + } + else if(regexMatches[index+2].groups.endOperator) { + processIfStatement(regexMatches.slice(index, index+3), ifConditionString, replaceStringCallback)+ regexMatches[index+2].groups.endTrailingString; + } + } + }); + const processedString = joinProcessedRegexArray(regexMatches); + return checkForIfStatements(processedString, ifConditionString, replaceStringCallback); + } else { + return str; + } +} + +function reconstructPageLevelIfStatements(str) { + return str.replaceAll("(((", "{").replaceAll(")))","}"); +} + +function joinProcessedRegexArray(regexMatches) { + let processedString = ""; + regexMatches.forEach((match) => { + processedString += match.groups.cleanString ?? match[0]; + }); + return processedString; +} + +function processIfStatement(ifStatementArray, ifConditionString, replaceStringCallback) { + if(ifStatementArray[0].groups.ifConditionType.includes(ifConditionString)) { + //replace the if condition with a real value + const ifCondition = replaceStringCallback(ifStatementArray[0].groups.ifCondition); + //convert the entire if statement + let processedIfStatementString; + if (ifCondition) { + processedIfStatementString = ifStatementArray[0].groups.ifTrailingString; + } else { + processedIfStatementString = ifStatementArray[1].groups.elseTrailingString ?? ""; + } + ifStatementArray.forEach((entry) => { + if (entry.groups.ifOperator) { + entry.groups.cleanString = processedIfStatementString; + } else if (entry.groups.endOperator) { + entry.groups.cleanString = entry.groups.endTrailingString; + } else { + entry.groups.cleanString = ""; + } + }) + } else { + // stow the if statement to reconstruct later when passing to the page + ifStatementArray.forEach((entry) => { + const groups = entry.groups; + if (groups.ifOperator) + groups.cleanString = "(((if:" + groups.ifConditionType + groups.ifCondition + ")))" + groups.ifTrailingString; + else if (groups.elseOperator) + groups.cleanString = "(((else)))" + groups.elseTrailingString; + else + groups.cleanString = "(((end)))" + groups.endTrailingString; + }); + } +} + +function getStoreValueFromString(str) { + let storeOrStateObject = str.includes('getters') ? store :store.state; + for (const s of str.split(".")) { + if (storeOrStateObject[s] != undefined) { + storeOrStateObject = storeOrStateObject[s]; + } else { + return ""; // if we can't map our string to state data, return an empty string. + } + } + return storeOrStateObject; +} + + + export function doesCopyContainRouterLink(copy) { return copy.includes(this.dynamicStrings.ROUTER_LINK); diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.vue b/src/layouts/license-plate-lookup/license-plate-lookup.vue index 90a447258..43afbc971 100644 --- a/src/layouts/license-plate-lookup/license-plate-lookup.vue +++ b/src/layouts/license-plate-lookup/license-plate-lookup.vue @@ -64,6 +64,7 @@ - { - console.log("cmsContent", cmsContent); vm.setCmsContent(cmsContent); - vm.isInsurance = vm.getDefaultIsInsuranceValue(); + vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(); vm.selectedPackage = vm.getDefaultSelectedPackageValue(); + vm.lineItems = [ + { + PartNumber : "001", + Description : "Windshield with Recal", + PartType : "Windshield", + Quantity : "1", + BasePartNumber : "001", + Color : "Green", + CanSafeliteRecalibrate: true, + Price : 350.00 + }, + { + PartNumber : "002", + Description : "Front Wipers", + PartType : "frontWiper", + Quantity : "1", + Price : 35.00, + }, + { + PartNumber : "003", + Description : "Rear Wiper", + PartType : "rearWiper", + Quantity : "1", + Price : 25.00, + }, + { + PartNumber : "004", + Description : "Rain Defense", + PartType : "rainDefense", + Quantity : "1", + Price : 30.00, + }, + { + PartNumber : "005", + Description : "Recalibration", + PartType : "recalibration", + Quantity : "1", + Price : 150.00, + }, + ]; }); }, data(){ return { cashOrInsuranceThreshhold: 600, pricingRequestResult: null, - isInsurance: null, + isInsuranceSelected: null, selectedPackage: null, - } - }, - watch: { - selectedPackage(newPackage, oldPackage) { - console.log("new package: ", newPackage); - console.log("old package: ", oldPackage); + lineItems: null, } }, methods: { arePagePrerequisitesValid() { return store.getters.order.damage.isRepair || (store.getters.order.lineItems?.glassParts != null && store.getters.order.lineItems.glassParts.length > 0); }, - getDefaultIsInsuranceValue() { - var defaultIsInsuranceValue = this.$store.getters.order.payment.isInsurance; - if(defaultIsInsuranceValue != null) { - return defaultIsInsuranceValue; + getDefaultIsInsuranceSelectedValue() { + const defaultIsInsuranceSelectedValue = this.$store.getters.order.payment.isInsurance; + if(defaultIsInsuranceSelectedValue != null) { + return defaultIsInsuranceSelectedValue; } else { return this.economyPackagePrice > this.cashOrInsuranceThreshhold; } @@ -194,16 +228,6 @@ export default { }, }, computed: { - years() { - return ["2011", "2012"]; - }, - answersFromCms() { - return ["Pay on my own", "Pay with insurance"]; - }, - economyPackagePrice() { - //TODO build out the pricing logic CSR-504 - return 0; - }, selectedChipCountValues: { get: function() { return this.modelValue; @@ -219,7 +243,6 @@ export default { vehicleBanner, funnelSubHeader, Form, - radioServicePackage, textBlock, cashOrInsuranceQuestion, servicePackageQuestion, diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index a73bdd0e1..0e527856d 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -1,13 +1,22 @@ \ No newline at end of file + diff --git a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue index 70a0a71d5..29694b09a 100644 --- a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue +++ b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue @@ -1,27 +1,50 @@ - + diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index c6c8a89b7..aaa8aff42 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -56,6 +56,14 @@ export default { isServiceable: serviceZipValidationResponse.data.isServiceable, state: serviceZipValidationResponse.data.state }; + }, + getEconomyPackagePrice(lineItems) { + const nonVapsItems = lineItems.filter((lineItem) => { + return lineItem.PartType != "frontWiper" && lineItem.PartType != "rearWiper" && lineItem.PartType != "rainDefense"; + }); + let totalPrice = 0; + nonVapsItems.forEach(item => totalPrice += item.Price); + return totalPrice; } }, computed: { diff --git a/src/mixins/input-button-wrapper-mixin.js b/src/mixins/input-button-wrapper-mixin.js index f2b34a83f..c8424504f 100644 --- a/src/mixins/input-button-wrapper-mixin.js +++ b/src/mixins/input-button-wrapper-mixin.js @@ -16,7 +16,8 @@ export default { valueToLogType: String, validationRules: String, isWide: Boolean, - isRequired: Boolean + isRequired: Boolean, + additionalData: null, }, data() { return { @@ -31,7 +32,6 @@ export default { if (this.preHandleAnswerChange) { this.preHandleAnswerChange(e) } - this.$emit("change", e); }, }, diff --git a/src/store/index.js b/src/store/index.js index 0d664c09f..e5915cc9e 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -50,7 +50,7 @@ const getDefaultState = () => { glassToReplace: null, partQuestionAnswers: null, moldingQuestionAnswers: null, - capabilityQuestionAnswers: null + capabilityQuestionAnswers: null, }, lineItems: { glassParts: null @@ -372,6 +372,10 @@ export const getters = { }, eventBus: (state) => state.applicationUser.eventBus, damage: (state) => state.order.damage, + hasAnyNonWindshieldGlassParts: (state) => { + const nonWindshieldItems = state.order.damage.glassToReplace.filter(glassToReplace => glassToReplace.glassLocation != "Windshield"); + return !!nonWindshieldItems.length; + }, lineItems: (state) => state.order.lineItems, pageData: (state) => (page) => { return state.applicationUser.pageData[page]; }, applicationUser: (state) => state.applicationUser, @@ -1133,7 +1137,6 @@ function getHasRecalibrationPart(state) { } else { // Does not have 'requiresRecalibration' return false; } - } function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) { From 18b508a550757b6065ef8d4e3861e0465d07fedb Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Wed, 12 Oct 2022 15:34:02 -0400 Subject: [PATCH 04/40] CSR-731 | Demo-ready commit --- src/layouts/quote/quote.vue | 123 +++++------------- .../service-package-radio.vue | 29 +++-- src/mixins/base-mixin.js | 2 +- 3 files changed, 54 insertions(+), 100 deletions(-) diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 1e0bb16a6..0438aef08 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -38,75 +38,17 @@ typeStyle="caption" class="mt-4" /> -

h1

- -

h2

- -

h3

- -

h4

- -

h5

- -

h6

- -

Body

- -

Label

- -

Small

- -

Caption

- { vm.setCmsContent(cmsContent); - vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(); vm.selectedPackage = vm.getDefaultSelectedPackageValue(); vm.lineItems = [ { PartNumber : "001", Description : "Windshield with Recal", - PartType : "Windshield", + partType : "Windshield", Quantity : "1", BasePartNumber : "001", Color : "Green", CanSafeliteRecalibrate: true, - Price : 350.00 + price : 350.22 }, { - PartNumber : "002", - Description : "Front Wipers", - PartType : "frontWiper", - Quantity : "1", - Price : 35.00, + "partNumber": "SBB16", + "description": "SAFELITE BEAM BLADE 16", + "partType": "PASSENGER FRONT WIPER", + "price": 32.64, }, { - PartNumber : "003", - Description : "Rear Wiper", - PartType : "rearWiper", - Quantity : "1", - Price : 25.00, + "partNumber": "SBB26", + "description": "SAFELITE BEAM BLADE 26", + "partType": "DRIVER FRONT WIPER", + "price": 53.04, }, { - PartNumber : "004", - Description : "Rain Defense", - PartType : "rainDefense", - Quantity : "1", - Price : 30.00, + "partNumber": "SBBR12A", + "description": "SAFELITE REAR BLADE 12A", + "partType": "REAR WIPER", + "price": 24.48, + }, + { + "partNumber": "RAIN DEFENSE", + "description": null, + "partType": "RAIN DEFENSE", + "price": 35.50, }, { PartNumber : "005", Description : "Recalibration", - PartType : "recalibration", + partType : "recalibration", Quantity : "1", - Price : 150.00, + price : 150.00, }, ]; + vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(); }); }, data(){ return { - cashOrInsuranceThreshhold: 600, pricingRequestResult: null, isInsuranceSelected: null, selectedPackage: null, @@ -219,7 +165,7 @@ export default { if(defaultIsInsuranceSelectedValue != null) { return defaultIsInsuranceSelectedValue; } else { - return this.economyPackagePrice > this.cashOrInsuranceThreshhold; + return baseMixin.methods.getEconomyPackagePrice(this.lineItems) > 600; } }, getDefaultSelectedPackageValue() { @@ -246,6 +192,7 @@ export default { textBlock, cashOrInsuranceQuestion, servicePackageQuestion, + modal, }, }; diff --git a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue index 29694b09a..5562ec636 100644 --- a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue +++ b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue @@ -69,10 +69,10 @@ export default { return this.getArrayOfListItemsFromRawCmsCopy(this.getBodyTextFromCms); }, isRecalibrationOnOrder() { - return this.lineItemsContainsPartType("recalibration"); + return this.lineItemsContainsPartType("RECALIBRATION"); }, frontWipersApplicableForStandard() { - const frontWipersAreAvailable = this.lineItemsContainsPartType("frontWiper"); + const frontWipersAreAvailable = this.lineItemsContainsPartType("FRONT WIPER"); const isRepair = this.$store.getters.order.damage.isRepair; const glassToReplaceContainsWindshield = this.glassToReplaceContainsGlassLocation("Windshield"); if (frontWipersAreAvailable) { @@ -90,19 +90,19 @@ export default { } }, rearWiperApplicableForStandard() { - const rearWiperIsAvailable = this.lineItemsContainsPartType("rearWiper"); + const rearWiperIsAvailable = this.lineItemsContainsPartType("REAR WIPER"); return this.glassToReplaceContainsGlassLocation("Rear") && rearWiperIsAvailable; }, frontWipersApplicableForPremium() { - const frontWipersAreAvailable = this.lineItemsContainsPartType("frontWiper"); + const frontWipersAreAvailable = this.lineItemsContainsPartType("FRONT WIPER"); return frontWipersAreAvailable; }, rearWiperApplicableForPremium() { - const rearWiperIsAvailable = this.lineItemsContainsPartType("rearWiper"); + const rearWiperIsAvailable = this.lineItemsContainsPartType("REAR WIPER"); return this.glassToReplaceContainsGlassLocation("Rear") && rearWiperIsAvailable; }, rainDefenseApplicableForPremium() { - const frontWipersAreAvailable = this.lineItemsContainsPartType("frontWiper"); + const frontWipersAreAvailable = this.lineItemsContainsPartType("FRONT WIPER"); const glassToReplaceContainsWindshield = this.glassToReplaceContainsGlassLocation("Windshield"); const glassToReplaceContainsRearGlass = this.glassToReplaceContainsGlassLocation("Rear"); if(this.frontWipersApplicableForStandard) { @@ -155,7 +155,7 @@ export default { return this[str]; }, lineItemsContainsPartType(partType) { - const results = this.additionalData.lineItems.filter(lineItem => lineItem.PartType === partType); + const results = this.additionalData.lineItems.filter(lineItem => lineItem.partType.toUpperCase().includes(partType)); return !!results.length; }, glassToReplaceContainsGlassLocation(glassLocation) { @@ -176,8 +176,8 @@ export default { const priceFrontWipers = this.frontWipersApplicableForStandard; const priceRearWipers = this.rearWiperApplicableForStandard; this.additionalData.lineItems.forEach(item => { - if ((priceFrontWipers && item.PartType === "frontWiper") || (priceRearWipers && item.PartType === "rearWiper")) { - vapsPrice += item.Price; + if ((priceFrontWipers && item.partType.toUpperCase().includes("FRONT WIPER")) || (priceRearWipers && item.partType.toUpperCase().includes("REAR WIPER"))) { + vapsPrice += item.price; } }); return vapsPrice; @@ -188,8 +188,8 @@ export default { const priceRearWipers = this.rearWiperApplicableForPremium; const priceRainDefense = this.rainDefenseApplicableForPremium; this.additionalData.lineItems.forEach(item => { - if ((priceFrontWipers && item.PartType === "frontWiper") || (priceRearWipers && item.PartType === "rearWiper") || (priceRainDefense && item.PartType === "rainDefense")) { - vapsPrice += item.Price; + if ((priceFrontWipers && item.partType.toUpperCase().includes("FRONT WIPER")) || (priceRearWipers && item.partType.toUpperCase().includes("REAR WIPER")) || (priceRainDefense && item.partType.toUpperCase().includes("RAIN DEFENSE"))) { + vapsPrice += item.price; } }); return vapsPrice; @@ -290,6 +290,13 @@ export default { } } + .package-footer { + font-size: 12px; + font-weight: 500; + color: #D4281C; + margin-left: -32px; + } + .package-specs { display: flex; flex-direction: column; diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index aaa8aff42..d42845e33 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -62,7 +62,7 @@ export default { return lineItem.PartType != "frontWiper" && lineItem.PartType != "rearWiper" && lineItem.PartType != "rainDefense"; }); let totalPrice = 0; - nonVapsItems.forEach(item => totalPrice += item.Price); + nonVapsItems.forEach(item => totalPrice += item.price); return totalPrice; } }, From be9f6a008c19e45fb8f34e489df630e128cdf97a Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 13 Oct 2022 14:31:24 -0400 Subject: [PATCH 05/40] CSR-731 | Fix for cash/insurance toggle styling --- .../cash-or-insurance-question.vue | 2 +- src/layouts/quote/quote.vue | 52 +++++++++---------- .../list-button-horizontal.vue | 5 +- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue b/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue index 365f8a6e7..ab2e639a1 100644 --- a/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue +++ b/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue @@ -5,7 +5,7 @@ :groupName="groupName" buttonType="listButtonHorizontal" v-model="selectedValues" - isCashOrInsurance + :additionalData="{isCashOrInsurance: true}" isRequired />
diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 0438aef08..20ec96dfa 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -113,37 +113,37 @@ export default { CanSafeliteRecalibrate: true, price : 350.22 }, - { - "partNumber": "SBB16", - "description": "SAFELITE BEAM BLADE 16", - "partType": "PASSENGER FRONT WIPER", - "price": 32.64, - }, - { - "partNumber": "SBB26", - "description": "SAFELITE BEAM BLADE 26", - "partType": "DRIVER FRONT WIPER", - "price": 53.04, - }, - { - "partNumber": "SBBR12A", - "description": "SAFELITE REAR BLADE 12A", - "partType": "REAR WIPER", - "price": 24.48, - }, + // { + // "partNumber": "SBB16", + // "description": "SAFELITE BEAM BLADE 16", + // "partType": "PASSENGER FRONT WIPER", + // "price": 32.64, + // }, + // { + // "partNumber": "SBB26", + // "description": "SAFELITE BEAM BLADE 26", + // "partType": "DRIVER FRONT WIPER", + // "price": 53.04, + // }, + // { + // "partNumber": "SBBR12A", + // "description": "SAFELITE REAR BLADE 12A", + // "partType": "REAR WIPER", + // "price": 24.48, + // }, { "partNumber": "RAIN DEFENSE", "description": null, "partType": "RAIN DEFENSE", "price": 35.50, }, - { - PartNumber : "005", - Description : "Recalibration", - partType : "recalibration", - Quantity : "1", - price : 150.00, - }, + // { + // PartNumber : "005", + // Description : "Recalibration", + // partType : "recalibration", + // Quantity : "1", + // price : 150.00, + // }, ]; vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(); }); @@ -165,7 +165,7 @@ export default { if(defaultIsInsuranceSelectedValue != null) { return defaultIsInsuranceSelectedValue; } else { - return baseMixin.methods.getEconomyPackagePrice(this.lineItems) > 600; + return baseMixin.methods.getEconomyPackagePrice(this.lineItems) > 500; } }, getDefaultSelectedPackageValue() { diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue index c5baa41ea..9f6084047 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.vue +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.vue @@ -3,7 +3,7 @@ v-bind="$props" :buttonWrapperClasses="[ 'list-group list-button-horizontal d-flex flex-column w-100', - { 'radio-fancy': isCashOrInsurance }, + { 'radio-fancy': additionalData?.isCashOrInsurance }, ]" @buttonClicked="handleAnswerChange">
Date: Tue, 18 Oct 2022 14:32:10 -0400 Subject: [PATCH 06/40] CSR-731 | Refactor cms-content-helper.js Also fixed a looping issue --- src/helpers/cms-content-helper.js | 189 +++++++++++------- src/layouts/quote/quote.vue | 51 ++--- .../service-package-radio.vue | 8 +- 3 files changed, 147 insertions(+), 101 deletions(-) diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index ac945d2b0..aff7586d1 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -40,7 +40,8 @@ export function fetchCmsContentForPage(fmgPage) { // Function to convert a string, into a matching global state item. function mapStringToState(str) { // Pull all matches out of the string. - const regexExp = new RegExp("{(.*?):(.*?)}", "g"); + //const regexExp = new RegExp("{(.*?):(.*?)}", "g"); + const regexExp = new RegExp("{([^{}]*?):([^{}]*?)}", "g"); const regexMatches = [...str.matchAll(regexExp)]; const globalStateMatches = regexMatches.filter(match => { return match[1] === dynamicStrings.GLOBAL_STATE; @@ -93,11 +94,11 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) { function processWidgetItemForReplacement(widgetModel, key) { // If we have a string, and it needs to be replaced. if (typeof widgetModel[key] === "string") { - widgetModel[key] = checkForIfStatements(widgetModel[key], dynamicStrings.GLOBAL_STATE, getStoreValueFromString); + widgetModel[key] = processIfStatements(widgetModel[key], dynamicStrings.GLOBAL_STATE, getStoreValueFromString); if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) { widgetModel[key] = mapStringToState(widgetModel[key]); } - return reconstructPageLevelIfStatements(widgetModel[key]); + return widgetModel[key]; } // If we have an object. array, etc @@ -116,75 +117,6 @@ function processWidgetItemForReplacement(widgetModel, key) { return widgetModel[key]; } -export function checkForIfStatements(str, ifConditionString, replaceStringCallback) { - // Pattern to split off each {if:...}, {else}, {end} and their trailing strings - const regexExp = new RegExp("(?^.+?)(?=(?:{if))|(?{if:)(?.*?:)(?.*?)}(?.*?)(?=(?:{(?:end|else|if:.*?)}))|(?{else})(?.*?)(?=(?:{(?:end|else|if:.*?)}))|(?{end})(?.*?)(?=(?:{(?:end|else|if:.*?)})|$)", "g"); - const regexMatches = [...str.matchAll(regexExp)]; - // Find and evaluate full globalState if statements - if (regexMatches.length) { - regexMatches.forEach((match, index) => { - if(match.groups.ifOperator) { - if(regexMatches[index+1].groups.endOperator) { - processIfStatement(regexMatches.slice(index, index+2), ifConditionString, replaceStringCallback) + regexMatches[index+1].groups.endTrailingString; - } - else if(regexMatches[index+2].groups.endOperator) { - processIfStatement(regexMatches.slice(index, index+3), ifConditionString, replaceStringCallback)+ regexMatches[index+2].groups.endTrailingString; - } - } - }); - const processedString = joinProcessedRegexArray(regexMatches); - return checkForIfStatements(processedString, ifConditionString, replaceStringCallback); - } else { - return str; - } -} - -function reconstructPageLevelIfStatements(str) { - return str.replaceAll("(((", "{").replaceAll(")))","}"); -} - -function joinProcessedRegexArray(regexMatches) { - let processedString = ""; - regexMatches.forEach((match) => { - processedString += match.groups.cleanString ?? match[0]; - }); - return processedString; -} - -function processIfStatement(ifStatementArray, ifConditionString, replaceStringCallback) { - if(ifStatementArray[0].groups.ifConditionType.includes(ifConditionString)) { - //replace the if condition with a real value - const ifCondition = replaceStringCallback(ifStatementArray[0].groups.ifCondition); - //convert the entire if statement - let processedIfStatementString; - if (ifCondition) { - processedIfStatementString = ifStatementArray[0].groups.ifTrailingString; - } else { - processedIfStatementString = ifStatementArray[1].groups.elseTrailingString ?? ""; - } - ifStatementArray.forEach((entry) => { - if (entry.groups.ifOperator) { - entry.groups.cleanString = processedIfStatementString; - } else if (entry.groups.endOperator) { - entry.groups.cleanString = entry.groups.endTrailingString; - } else { - entry.groups.cleanString = ""; - } - }) - } else { - // stow the if statement to reconstruct later when passing to the page - ifStatementArray.forEach((entry) => { - const groups = entry.groups; - if (groups.ifOperator) - groups.cleanString = "(((if:" + groups.ifConditionType + groups.ifCondition + ")))" + groups.ifTrailingString; - else if (groups.elseOperator) - groups.cleanString = "(((else)))" + groups.elseTrailingString; - else - groups.cleanString = "(((end)))" + groups.endTrailingString; - }); - } -} - function getStoreValueFromString(str) { let storeOrStateObject = str.includes('getters') ? store :store.state; for (const s of str.split(".")) { @@ -197,8 +129,121 @@ function getStoreValueFromString(str) { return storeOrStateObject; } +/////////////////////////////////// +// If Statement Processing Logic // +/////////////////////////////////// +/** + * Recursive function - replaces all instances of if statements from the CMS that utilize the specified ifConditionKeyword + * @param {*} str string - Input string to be processed + * @param {*} ifConditionKeyword string - Defines which if statements to process ex: 'globalState' + * @param {*} replacePlaceholderCallback function - Callback to replace CMS placeholder values + * @returns The processed string + */ +export function processIfStatements(str, ifConditionKeyword, replacePlaceholderCallback) { + const containsRelevantIfStatement = new RegExp("{if:" + ifConditionKeyword +":.+?}", "g").test(str); + if(!containsRelevantIfStatement) { + return str; + } else { + const ifStatementRegexExpression = getIfStatementRegexExpression(); + const ifStatementRegexMatches = [...str.matchAll(ifStatementRegexExpression)]; + const completeIfStatementArray = getFirstNonNestedIfStatement(ifStatementRegexMatches, ifConditionKeyword); + const processedIfStatementString = executeIfStatementAndGetProcessedString(completeIfStatementArray, replacePlaceholderCallback); + replaceIfStatementWithProcessedString(completeIfStatementArray, processedIfStatementString); + const reconstructedPostProcessedString = joinProcessedRegexArray(ifStatementRegexMatches); + return processIfStatements(reconstructedPostProcessedString, ifConditionKeyword, replacePlaceholderCallback); + } +} +function getFirstNonNestedIfStatement(matches, ifConditionKeyword) { + let index = 0; + for(const match of matches) { + if (match.groups.ifOperator && match.groups.ifConditionType === ifConditionKeyword) { + let interiorIndex = 0; + let nestedLevel = 0; + for (const interiorMatch of matches.slice(index+1)) { + if (interiorMatch.groups.ifOperator) { + if(interiorMatch.groups.ifConditionType === ifConditionKeyword) { + break; + } else { + nestedLevel++; + } + } else if (interiorMatch.groups.endOperator) { + if (nestedLevel) { + nestedLevel--; + } else { + return matches.slice(index, index + interiorIndex+2); + } + } + interiorIndex++; + } + } + index++; + } +} + +function joinProcessedRegexArray(regexMatches) { + let processedString = ""; + regexMatches.forEach((match) => { + processedString += match.groups.cleanString ?? match[0]; + }); + return processedString; +} + +function executeIfStatementAndGetProcessedString(ifStatementArray, replacePlaceholderCallback) { + const ifCondition = replacePlaceholderCallback(ifStatementArray[0].groups.ifCondition); + if (ifCondition) { + return ifStatementArray[0].groups.ifTrailingString; + } else { + return ifStatementArray[1].groups.elseTrailingString ?? ""; + } +} + +function replaceIfStatementWithProcessedString(ifStatementArray, processedIfStatementString) { + ifStatementArray.forEach((entry) => { + if (entry.groups.ifOperator) { + entry.groups.cleanString = processedIfStatementString; + } else if (entry.groups.endOperator) { + entry.groups.cleanString = entry.groups.endTrailingString; + } else { + entry.groups.cleanString = ""; + } + }) +} + +function getIfStatementRegexExpression() { + // Matches but does not capture: + // {if:...} or {else} or {end} + const anyLogicOperatorNonCapture = "(?:{(?:end|else|if:.*?)})"; + // NOTE: ? syntax stores the captured match like so: match.groups.variableName + const matchStartOfString = ( + "(?^.+?)" + // Match and Capture all characters (lazy), cannot be empty + "(?=(?:{if))" // Looks ahead but does not capture {if + ); + const matchIfOperator = ( + "(?{if:)" + // Match & Capture {if: + "(?.*?):" + // Match all chars up to and including next ":" - Capture all chars up to ":" + "(?.*?)}" + // Match all chars up to and including next "}" - Capture all chars up to "}" + "(?.*?)" + // Match and Capture all characters (lazy), can be empty + "(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator + ); + const matchElseOperator = ( + "(?{else})" + // Match & Capture {else} + "(?.*?)" + // Match & Capture all characters (lazy), can be empty + "(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator + ); + const matchEndOperator = ( + "(?{end})" + // Match & Capture {end} + "(?.*?)" + // Match & Capture all chracters (lazy), can be empty + "(?="+ anyLogicOperatorNonCapture + "|$)" // Looks ahead but does not capture the next logic operator + ); + // Combine all matching patterns, separated by "or" pipes + return new RegExp(matchStartOfString + "|" + matchIfOperator + "|" + matchElseOperator + "|" + matchEndOperator, "g"); +} + +////////////////////////////////////////// +// End of If Statement Processing Logic // +////////////////////////////////////////// export function doesCopyContainRouterLink(copy) { return copy.includes(this.dynamicStrings.ROUTER_LINK); diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 20ec96dfa..7ad86e86c 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -24,6 +24,7 @@ groupName="CashOrInsuranceQuestion" /> )|(?:<\/ul>)/g; return copy.replace(regex, ''); From 5e3abe4ebf14466996def5ec043e14d9fcf8b7f8 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Mon, 24 Oct 2022 11:26:42 -0400 Subject: [PATCH 07/40] CSR-731 | Final copy w/o tests --- .../button-question/button-question.vue | 8 +- src/helpers/cms-content-helper.js | 87 ++++++--- .../address-vehicles/address-vehicles.vue | 10 +- .../service-package-question.vue | 179 ++++++++++++++---- .../service-package-radio.vue | 158 ++-------------- src/mixins/base-mixin.js | 10 +- src/mixins/input-button-wrapper-mixin.js | 4 +- src/ux-components/alert/alert.vue | 10 +- 8 files changed, 235 insertions(+), 231 deletions(-) diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 653aa0d24..b95fd3afb 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -40,6 +40,9 @@ :is="buttonType" :buttonLabel="answer.buttonLabel" :buttonLabelSubCopy="answer.buttonLabelSubCopy" + :buttonBodyCopy="answer.buttonBodyCopy" + :buttonAuxillaryCopy="answer.buttonAuxillaryCopy" + :buttonFooterCopy="answer.buttonFooterCopy" :buttonImage="answer.buttonImage" :buttonImageId="answer.buttonImageId" :groupName="answer.groupName" @@ -49,7 +52,7 @@ :isWide="isWide" :validationRules="validationRules" :textPosition="textPosition" - :additionalData="additionalData" /> + :additionalData="additionalData" :lastValuePushedToGa="lastValuePushedToGa" :setLastValuePushedToGa="setLastValuePushedToGa" v-model="selectedValues" @@ -187,6 +190,9 @@ export default { answer.altText ?? (answer.Name ? answer.Name : answer), buttonLabelSubCopy: answer.buttonLabelSubCopy ?? answer.SubText, + buttonBodyCopy: answer.buttonBodyCopy ?? answer.buttonBodyCopy, + buttonAuxillaryCopy: answer.buttonAuxillaryCopy ?? answer.buttonAuxillaryCopy, + buttonFooterCopy: answer.buttonFooterCopy ?? answer.buttonFooterCopy, buttonImage: answer.buttonImage ?? answer.AnswerImageUrl, buttonImageId: answer.buttonImageId ?? answer.ImageId, groupName: this.formatString(this.groupName), diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index aff7586d1..8d17c8aa6 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -40,7 +40,6 @@ export function fetchCmsContentForPage(fmgPage) { // Function to convert a string, into a matching global state item. function mapStringToState(str) { // Pull all matches out of the string. - //const regexExp = new RegExp("{(.*?):(.*?)}", "g"); const regexExp = new RegExp("{([^{}]*?):([^{}]*?)}", "g"); const regexMatches = [...str.matchAll(regexExp)]; const globalStateMatches = regexMatches.filter(match => { @@ -147,32 +146,38 @@ export function processIfStatements(str, ifConditionKeyword, replacePlaceholderC } else { const ifStatementRegexExpression = getIfStatementRegexExpression(); const ifStatementRegexMatches = [...str.matchAll(ifStatementRegexExpression)]; - const completeIfStatementArray = getFirstNonNestedIfStatement(ifStatementRegexMatches, ifConditionKeyword); - const processedIfStatementString = executeIfStatementAndGetProcessedString(completeIfStatementArray, replacePlaceholderCallback); - replaceIfStatementWithProcessedString(completeIfStatementArray, processedIfStatementString); + const completeIfStatementArray = getAndFlagFirstNonNestedIfStatementWithKeyword(ifStatementRegexMatches, ifConditionKeyword); + executeIfStatementAndSetProcessedStrings(completeIfStatementArray, replacePlaceholderCallback); const reconstructedPostProcessedString = joinProcessedRegexArray(ifStatementRegexMatches); return processIfStatements(reconstructedPostProcessedString, ifConditionKeyword, replacePlaceholderCallback); } } -function getFirstNonNestedIfStatement(matches, ifConditionKeyword) { +function getAndFlagFirstNonNestedIfStatementWithKeyword(matches, ifConditionKeyword) { let index = 0; for(const match of matches) { - if (match.groups.ifOperator && match.groups.ifConditionType === ifConditionKeyword) { + if (match.groups.isIfStatement && match.groups.ifConditionType === ifConditionKeyword) { let interiorIndex = 0; let nestedLevel = 0; + let elseStatementIndex = null; for (const interiorMatch of matches.slice(index+1)) { - if (interiorMatch.groups.ifOperator) { + if (interiorMatch.groups.isIfStatement) { if(interiorMatch.groups.ifConditionType === ifConditionKeyword) { break; } else { nestedLevel++; } - } else if (interiorMatch.groups.endOperator) { + } else if(interiorMatch.groups.isElseStatement) { + if (!nestedLevel) { + elseStatementIndex = interiorIndex+1; + } + } else if (interiorMatch.groups.isEndStatement) { if (nestedLevel) { nestedLevel--; } else { - return matches.slice(index, index + interiorIndex+2); + const ifStatementArray = matches.slice(index, index + interiorIndex+2); + flagMatchesForProcessing(ifStatementArray, elseStatementIndex); + return ifStatementArray; } } interiorIndex++; @@ -182,33 +187,51 @@ function getFirstNonNestedIfStatement(matches, ifConditionKeyword) { } } + + +function flagMatchesForProcessing(matches, elseStatementIndex) { + matches[0].isFlaggedForProcessing = true; + matches[matches.length-1].isFlaggedForProcessing = true; + if (elseStatementIndex) { + matches[elseStatementIndex].isFlaggedForProcessing = true; + } +} + function joinProcessedRegexArray(regexMatches) { let processedString = ""; regexMatches.forEach((match) => { - processedString += match.groups.cleanString ?? match[0]; + const rawString = match[0]; + processedString += match.groups.processedString ?? rawString; }); return processedString; } -function executeIfStatementAndGetProcessedString(ifStatementArray, replacePlaceholderCallback) { +function executeIfStatementAndSetProcessedStrings(ifStatementArray, replacePlaceholderCallback) { const ifCondition = replacePlaceholderCallback(ifStatementArray[0].groups.ifCondition); - if (ifCondition) { - return ifStatementArray[0].groups.ifTrailingString; - } else { - return ifStatementArray[1].groups.elseTrailingString ?? ""; - } + let isInsideDesiredBlock = ifCondition; + + ifStatementArray.forEach(entry => { + if (entry.groups.isElseStatement && entry.isFlaggedForProcessing) { + isInsideDesiredBlock = !ifCondition; + } else if (entry.groups.isEndStatement && entry.isFlaggedForProcessing) { + isInsideDesiredBlock = true; + } + setProcessedStringOnEntry(entry, isInsideDesiredBlock); + }); } -function replaceIfStatementWithProcessedString(ifStatementArray, processedIfStatementString) { - ifStatementArray.forEach((entry) => { - if (entry.groups.ifOperator) { - entry.groups.cleanString = processedIfStatementString; - } else if (entry.groups.endOperator) { - entry.groups.cleanString = entry.groups.endTrailingString; +function setProcessedStringOnEntry(entry, isInsideDesiredBlock) { + if (!isInsideDesiredBlock) { + entry.groups.processedString = ""; + } else { + if (entry.groups.isIfStatement) { + entry.groups.processedString = entry.isFlaggedForProcessing ? entry.groups.ifTrailingString : entry[0]; + } else if (entry.groups.isElseStatement) { + entry.groups.processedString = entry.isFlaggedForProcessing ? entry.groups.elseTrailingString : entry[0]; } else { - entry.groups.cleanString = ""; + entry.groups.processedString = entry.isFlaggedForProcessing ? entry.groups.endTrailingString : entry[0]; } - }) + } } function getIfStatementRegexExpression() { @@ -217,23 +240,23 @@ function getIfStatementRegexExpression() { const anyLogicOperatorNonCapture = "(?:{(?:end|else|if:.*?)})"; // NOTE: ? syntax stores the captured match like so: match.groups.variableName const matchStartOfString = ( - "(?^.+?)" + // Match and Capture all characters (lazy), cannot be empty + "(?^.+?)" + // Match and Capture all characters (lazy), cannot be empty "(?=(?:{if))" // Looks ahead but does not capture {if ); const matchIfOperator = ( - "(?{if:)" + // Match & Capture {if: + "(?{if:)" + // Match & Capture {if: "(?.*?):" + // Match all chars up to and including next ":" - Capture all chars up to ":" "(?.*?)}" + // Match all chars up to and including next "}" - Capture all chars up to "}" "(?.*?)" + // Match and Capture all characters (lazy), can be empty "(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator ); const matchElseOperator = ( - "(?{else})" + // Match & Capture {else} + "(?{else})" + // Match & Capture {else} "(?.*?)" + // Match & Capture all characters (lazy), can be empty "(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator ); const matchEndOperator = ( - "(?{end})" + // Match & Capture {end} + "(?{end})" + // Match & Capture {end} "(?.*?)" + // Match & Capture all chracters (lazy), can be empty "(?="+ anyLogicOperatorNonCapture + "|$)" // Looks ahead but does not capture the next logic operator ); @@ -249,19 +272,23 @@ export function doesCopyContainRouterLink(copy) { return copy.includes(this.dynamicStrings.ROUTER_LINK); } +export function doesCopyContainTextLink(copy) { + return copy.includes(dynamicStrings.TEXT_LINK); +} + export function splitCopyOnCMSPlaceHolder(copy){ // splits copy on { ... } such as {routerlink: ...} return copy.split(/{(.*?)}/g); } -export function getRouterLinkRouteFromCopy(copy){ +export function getLinkTargetFromCopy(copy){ // sample input: {routerLink:estimate,provide your VIN} // first split would return 'estimate,provide your VIN' // second split would return 'estimate' return copy.split(':')[1].split(',')[0]; } -export function getRouterLinkDisplayTextFromCopy(copy){ +export function getLinkDisplayTextFromCopy(copy){ // sample input: {routerLink:estimate,provide your VIN} // first split would return 'estimate,provide your VIN' // second split would return 'provide your VIN' diff --git a/src/layouts/address-vehicles/address-vehicles.vue b/src/layouts/address-vehicles/address-vehicles.vue index a0edf876f..2249ba2ab 100644 --- a/src/layouts/address-vehicles/address-vehicles.vue +++ b/src/layouts/address-vehicles/address-vehicles.vue @@ -29,7 +29,7 @@
- {{ getRouterLinkDisplayTextFromCopy(copy) }} + {{ getLinkDisplayTextFromCopy(copy) }} @@ -67,8 +67,8 @@ import { Form, defineRule } from "vee-validate"; import { isGlassAvailableForCarId } from "@/helpers/damage-helper"; import { doesCopyContainRouterLink, splitCopyOnCMSPlaceHolder, - getRouterLinkRouteFromCopy, - getRouterLinkDisplayTextFromCopy, } from "@/helpers/cms-content-helper"; + getLinkTargetFromCopy, + getLinkDisplayTextFromCopy, } from "@/helpers/cms-content-helper"; import { routerParams } from "@/router/router-constants/router-params"; import vinPagesMixin from "@/mixins/vin-pages-mixin"; @@ -148,8 +148,8 @@ export default { methods: { doesCopyContainRouterLink, splitCopyOnCMSPlaceHolder, - getRouterLinkRouteFromCopy, - getRouterLinkDisplayTextFromCopy, + getLinkTargetFromCopy, + getLinkDisplayTextFromCopy, arePagePrerequisitesValid() { if ( store.getters.order.vehicle.carId diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 0e527856d..6c40c73c4 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -1,26 +1,18 @@ diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index f7d046bf7..93488e26f 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -62,11 +62,13 @@ export default { }; }, getEconomyPackagePrice(lineItems) { - const nonVapsItems = lineItems.filter((lineItem) => { - return lineItem.PartType != "frontWiper" && lineItem.PartType != "rearWiper" && lineItem.PartType != "rainDefense"; - }); let totalPrice = 0; - nonVapsItems.forEach(item => totalPrice += item.price); + lineItems.forEach((lineItem) => { + const partType = lineItem.partType.toUpperCase(); + if (!partType.includes("FRONT WIPER") && !partType.includes("REAR WIPER") && !partType.includes("RAIN DEFENSE")) { + totalPrice += lineItem.price; + } + }); return totalPrice; } }, diff --git a/src/mixins/input-button-wrapper-mixin.js b/src/mixins/input-button-wrapper-mixin.js index ddc7c4583..af2cc91af 100644 --- a/src/mixins/input-button-wrapper-mixin.js +++ b/src/mixins/input-button-wrapper-mixin.js @@ -9,6 +9,9 @@ export default { ...inputButtonProps, buttonLabel: [Number, String], buttonLabelSubCopy: String, + buttonBodyCopy: String, + buttonAuxillaryCopy: String, + buttonFooterCopy: String, buttonImage: String, altText: { type: String, @@ -17,7 +20,6 @@ export default { textPosition: String, screenReaderOnlyText: String, isWide: Boolean, - additionalData: null, }, computed: { selectedValue: { diff --git a/src/ux-components/alert/alert.vue b/src/ux-components/alert/alert.vue index 8462e4436..2b6e8cda8 100644 --- a/src/ux-components/alert/alert.vue +++ b/src/ux-components/alert/alert.vue @@ -11,7 +11,7 @@

@@ -38,8 +38,8 @@ diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 6c40c73c4..5fcd97da0 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -3,6 +3,7 @@ :answers="servicePackageAnswers" :groupName="groupName" buttonType="servicePackageRadio" + :buttonTypeObject="servicePackageRadio" v-model="selectedValues" isRequired /> @@ -13,6 +14,7 @@ import buttonQuestion from "@/common-components/button-question/button-question" import baseMixin from "@/mixins/base-mixin.js"; import { splitCopyOnCMSPlaceHolder, processIfStatements } from "@/helpers/cms-content-helper"; import { damageLocationsSelected as glassLocation } from "@/constants/damage-locations-selected"; +import servicePackageRadio from "./service-package-radio/service-package-radio" export default ({ name: "servicePackageQuestion", @@ -24,6 +26,11 @@ export default ({ isInsuranceSelected: Boolean, lineItems: null, }, + data() { + return { + servicePackageRadio: servicePackageRadio, + } + }, computed: { selectedValues: { get: function() { From f58155728aa550f4d97d6d9a549e4d8e2fc3318f Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Tue, 25 Oct 2022 14:29:17 -0400 Subject: [PATCH 11/40] CSR-731 | Change buttonType to buttonTypeString Removed radio-service-package --- .../button-question/button-question.spec.js | 6 +- .../button-question/button-question.vue | 14 +- .../address-vehicles-question.vue | 2 +- src/layouts/estimate/estimate.vue | 2 +- .../cash-or-insurance-question.vue | 2 +- .../service-package-question.vue | 2 +- .../damage-location-question.vue | 2 +- .../replace-options-question.vue | 2 +- .../side-door-options/side-door-options.vue | 2 +- .../windshield-chip-count-question.vue | 2 +- .../windshield-damage-type-question.vue | 2 +- .../glass-part-question.vue | 4 +- .../radio-service-package.vue | 203 ------------------ 13 files changed, 21 insertions(+), 224 deletions(-) delete mode 100644 src/ux-components/radio-service-package/radio-service-package.vue diff --git a/src/common-components/button-question/button-question.spec.js b/src/common-components/button-question/button-question.spec.js index 0e51949a8..a70ef12bc 100644 --- a/src/common-components/button-question/button-question.spec.js +++ b/src/common-components/button-question/button-question.spec.js @@ -31,7 +31,7 @@ describe("buttonQuestion.vue", () => { // Act const wrapper = shallowMount(buttonQuestion, { propsData: { - buttonType: "listCard", + buttonTypeString: "listCard", groupName: "group-name", }, }); @@ -46,7 +46,7 @@ describe("buttonQuestion.vue", () => { // Act const wrapper = shallowMount(buttonQuestion, { propsData: { - buttonType: "listButtonHorizontal", + buttonTypeString: "listButtonHorizontal", groupName: "group-name", }, }); @@ -61,7 +61,7 @@ describe("buttonQuestion.vue", () => { // Act const wrapper = shallowMount(buttonQuestion, { propsData: { - buttonType: "radio", + buttonTypeString: "radio", groupName: "group-name", }, }); diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 6577ad970..720aed61b 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -37,7 +37,7 @@ v-for="answer in buttonsInfo" :key="answer.value ? answer.value : answer"> -
-
- - -
-
- - -
-
- - -
- - - - -
- - - - - From 57e39be1b6251c065961c9d73888d68d0af161c2 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 27 Oct 2022 15:50:24 -0400 Subject: [PATCH 12/40] CSR-731 | Changes to work with latest merge from dev --- .../quote/service-package-question/service-package-question.vue | 2 +- .../service-package-radio/service-package-radio.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 46f5cc7a9..298a15856 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -172,7 +172,7 @@ export default ({ return !!partNumberMatches.length; }, glassToReplaceContainsGlassLocation(glassLocation) { - const glassLocationMatches = this.$store.getters.order.damage.glassToReplace.filter(glassToReplace => glassToReplace.glassLocation === glassLocation); + const glassLocationMatches = this.$store.getters.order.damage.glassToReplace.filter(glassToReplace => glassToReplace.location === glassLocation); return !!glassLocationMatches.length; }, }, diff --git a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue index f4edd8a29..c6cbeb356 100644 --- a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue +++ b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue @@ -18,7 +18,7 @@ From 6512de6b7a0b6cc4393bf4c8274b1edc276c407f Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Mon, 31 Oct 2022 14:40:51 -0400 Subject: [PATCH 13/40] Update style and images for modal. --- src/common-components/modal/modal.vue | 192 ++++++++++++++------------ 1 file changed, 104 insertions(+), 88 deletions(-) diff --git a/src/common-components/modal/modal.vue b/src/common-components/modal/modal.vue index 04f99f0df..11acdb53f 100644 --- a/src/common-components/modal/modal.vue +++ b/src/common-components/modal/modal.vue @@ -1,108 +1,124 @@ From ed83b15a6b6b10963bf399e31bf255fc8efda246 Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Mon, 31 Oct 2022 14:48:31 -0400 Subject: [PATCH 14/40] Add max-width to image. --- src/common-components/modal/modal.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common-components/modal/modal.vue b/src/common-components/modal/modal.vue index 11acdb53f..a375cf8cc 100644 --- a/src/common-components/modal/modal.vue +++ b/src/common-components/modal/modal.vue @@ -94,6 +94,7 @@ export default { img { display: flex; margin: 0 auto 1.5rem auto; + max-width: 100%; } } } From 8157162f823027b832e76b7683f894a483092e41 Mon Sep 17 00:00:00 2001 From: Katie Date: Wed, 2 Nov 2022 12:31:32 -0400 Subject: [PATCH 15/40] CSR-914 Fix validation UI --- src/styles/common-error-styles.scss | 13 ++++++++++++- .../list-button-horizontal.vue | 14 ++++++++++++-- src/ux-components/list-button/list-button.vue | 2 +- src/ux-components/list-card/list-card.vue | 5 +---- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/styles/common-error-styles.scss b/src/styles/common-error-styles.scss index f2ff5cf23..d497ae6ce 100644 --- a/src/styles/common-error-styles.scss +++ b/src/styles/common-error-styles.scss @@ -2,8 +2,18 @@ html { .has-error { &.list-button, &.list-card, - &.list-card.list-button { + &.list-button-horizontal { + border: 1px solid $red; + + .button-content { + border: none; + } + } + + &.list-button, + &.list-card { color: $red; + input[type="checkbox"]:focus + label, input[type="radio"]:focus + label { box-shadow: 0 0 0 2.5px $red; @@ -38,6 +48,7 @@ html { box-shadow: 0 0 1px $red; } } + &.grid-item { input[type="radio"] { + label { diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue index 9f4a9146c..82183dbc0 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.vue +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.vue @@ -6,7 +6,8 @@ { 'radio-fancy': isCashOrInsurance }, ]" v-model="selectedValue"> -
+
{{ buttonLabel }} @@ -163,9 +164,15 @@ export default { } } -.col { +.col, +.list-group { + border-radius: 0; + &:first-of-type { .list-button-horizontal { + border-bottom-left-radius: 0.5rem; + border-top-left-radius: 0.5rem; + .list-button-horizontal-content { border-bottom-left-radius: 0.5rem; border-top-left-radius: 0.5rem; @@ -175,6 +182,9 @@ export default { &:last-of-type { .list-button-horizontal { + border-bottom-right-radius: 0.5rem; + border-top-right-radius: 0.5rem; + .list-button-horizontal-content { border-bottom-right-radius: 0.5rem; border-top-right-radius: 0.5rem; diff --git a/src/ux-components/list-button/list-button.vue b/src/ux-components/list-button/list-button.vue index 5a55e2be8..4f7d34ae9 100644 --- a/src/ux-components/list-button/list-button.vue +++ b/src/ux-components/list-button/list-button.vue @@ -5,7 +5,7 @@ v-model="selectedValue">
+ class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4"> {{ buttonLabel }} diff --git a/src/ux-components/list-card/list-card.vue b/src/ux-components/list-card/list-card.vue index 90e575ffd..426509384 100644 --- a/src/ux-components/list-card/list-card.vue +++ b/src/ux-components/list-card/list-card.vue @@ -7,7 +7,7 @@ ]" v-model="selectedValue">
Date: Wed, 2 Nov 2022 12:40:34 -0400 Subject: [PATCH 16/40] CSR-914 Formatting --- .../list-button-horizontal/list-button-horizontal.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue index 82183dbc0..1b4232c4c 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.vue +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.vue @@ -167,7 +167,7 @@ export default { .col, .list-group { border-radius: 0; - + &:first-of-type { .list-button-horizontal { border-bottom-left-radius: 0.5rem; From ea866a0c6c9125eef8367d81f4bb028024505c63 Mon Sep 17 00:00:00 2001 From: FrankRua Date: Wed, 2 Nov 2022 13:36:57 -0400 Subject: [PATCH 17/40] Added test env --- azure-pipelines.yml | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1b61fa51c..51d748f1d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -109,6 +109,58 @@ stages: paths: /fmg/* awsProfile: $(devDeploymentProfile) + # Test Build/Deploy + - stage: Test + condition: eq(variables['Build.SourceBranch'], variables['test-branch'] ) + variables: + - group: FixMyGlassTest + jobs: + - deployment: testBuildDeployment + displayName: Build and Deploy FMG - Test + environment: digitalCloud-sys + container: node + workspace: + clean: all + strategy: + runOnce: + deploy: + steps: + - checkout: self + clean: true + - template: templates/digital/step-build-vue.yml@AzureDevOps + parameters: + buildOutputDir: dist + environment: Test + - template: templates/digital/step-deploy-vue.yml@AzureDevOps + parameters: + artifactName: vueDistTest + awsProfile: $(sysDeploymentProfile) + outputPath: /fmg/ + deployBuckets: + safelite-sys-fmg-us-east-1: + clearFolder: true + deployFolder: "" + region: us-east-1 + safelite-sys-fmg-us-east-2: + clearFolder: true + deployFolder: "" + region: us-east-2 + appDeployVariables: + __VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__) + __VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__) + __VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__) + __VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__) + indexDeployVariables: + __VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__) + __VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__) + cfDistributionId: $(vueCfDistributionId) + - template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps + parameters: + awsCliContainer: awscli + distributionId: $(apiCfDistributionId) + paths: /fmg/* + awsProfile: $(sysDeploymentProfile) + # QA Build/Deploy - stage: Qa condition: eq(variables['Build.SourceBranch'], variables['qa-branch'] ) From 29d36e7732952dc03601ec2633b21f4c6f3e4eab Mon Sep 17 00:00:00 2001 From: FrankRua Date: Wed, 2 Nov 2022 14:48:41 -0400 Subject: [PATCH 18/40] changed tag --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 51d748f1d..f6d88a03d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -28,7 +28,7 @@ resources: type: github name: Safelite/AzureDevOps endpoint: Safelite - ref: refs/tags/t5.5.35 + ref: refs/tags/t5.5.40 variables: - group: Digital-Infrastructure From bed5011dbc77ac86ccc334ada850ea460eb6ec2c Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Wed, 2 Nov 2022 15:38:26 -0400 Subject: [PATCH 19/40] CSR-731 | Rech review refactor --- .../button-question/button-question.vue | 4 +- src/constants/part-type-strings.js | 8 ++ .../cash-or-insurance-question.vue | 2 +- src/layouts/quote/quote.vue | 38 +++---- .../service-package-question.vue | 106 +++++++++++------- src/mixins/base-mixin.js | 5 +- src/mixins/input-button-wrapper-mixin.js | 1 + .../list-button-horizontal.vue | 13 ++- 8 files changed, 102 insertions(+), 75 deletions(-) create mode 100644 src/constants/part-type-strings.js diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 8081814d0..53094e206 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -48,7 +48,7 @@ :isWide="isWide" :validationRules="validationRules" :textPosition="textPosition" - :additionalData="additionalData" + :additionalButtonStyling="additionalButtonStyling" :lastValuePushedToGa="lastValuePushedToGa" :setLastValuePushedToGa="setLastValuePushedToGa" v-model="selectedValues" /> @@ -117,7 +117,7 @@ export default { suppressError: Boolean, useTextForValue: Boolean, valueToLogType: String, - additionalData: null + additionalButtonStyling: String, }, beforeMount() { if (this.buttonTypeObject) { diff --git a/src/constants/part-type-strings.js b/src/constants/part-type-strings.js new file mode 100644 index 000000000..6a2a490ff --- /dev/null +++ b/src/constants/part-type-strings.js @@ -0,0 +1,8 @@ +const partTypeStrings = { + FRONT_WIPER : "FRONT WIPER", + REAR_WIPER : "REAR WIPER", + RAIN_DEFENSE: "RAIN DEFENSE", + RECALIBRATION : "RECALIBRATION", + }; + + export {partTypeStrings}; \ No newline at end of file diff --git a/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue b/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue index 6c906aa99..7f2afa177 100644 --- a/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue +++ b/src/layouts/quote/cash-or-insurance-question/cash-or-insurance-question.vue @@ -5,7 +5,7 @@ :groupName="groupName" buttonTypeString="listButtonHorizontal" v-model="selectedValues" - :additionalData="{isCashOrInsurance: true}" + additionalButtonStyling="listButtonHorizontalStrong" isRequired />
diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 2110c8044..5bfa88d11 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -24,13 +24,12 @@ groupName="CashOrInsuranceQuestion" /> { vm.setCmsContent(cmsContent); - vm.selectedPackage = vm.getDefaultSelectedPackageValue(); - vm.lineItems = [ + vm.availableLineItems = [ { partNumber : "001", Description : "Windshield with Recal", @@ -117,13 +114,18 @@ export default { { "partNumber": "SBB16", "description": "SAFELITE BEAM BLADE 16", - "partType": "PASSENGER FRONT WIPER", + "partType": "FRONT WIPER", "price": 32.64, }, + { + "partNumber": "A DISPOSAL FEE", + "partType" : "DISPOSAL FEE", + "price" : 15.00, + }, { "partNumber": "SBB26", "description": "SAFELITE BEAM BLADE 26", - "partType": "DRIVER FRONT WIPER", + "partType": "FRONT WIPER", "price": 53.04, }, { @@ -154,7 +156,7 @@ export default { pricingRequestResult: null, isInsuranceSelected: null, selectedPackage: null, - lineItems: null, + availableLineItems: null, } }, methods: { @@ -167,23 +169,9 @@ export default { if(defaultIsInsuranceSelectedValue != null) { return defaultIsInsuranceSelectedValue; } else { - return baseMixin.methods.getEconomyPackagePrice(this.lineItems) > 500; + return this.availableLineItems ? baseMixin.methods.getTierOnePackagePrice(this.availableLineItems) > 500 : null; } }, - getDefaultSelectedPackageValue() { - // placeholder - to be populated via CSR-774 - return null; - }, - }, - computed: { - selectedChipCountValues: { - get: function() { - return this.modelValue; - }, - set: function(newValue) { - this.$emit("update:modelValue", newValue); - }, - }, }, components: { funnelHeader, diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 298a15856..5311bc2f5 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -12,9 +12,17 @@ diff --git a/src/common-components/base-input-button/base-input-button.spec.js b/src/common-components/base-input-button/base-input-button.spec.js index 646e271a2..87e8af6d2 100644 --- a/src/common-components/base-input-button/base-input-button.spec.js +++ b/src/common-components/base-input-button/base-input-button.spec.js @@ -800,7 +800,7 @@ describe("baseInputButton.vue", () => { // Assert expect(wrapper.vm.isChecked).toBe(true); - expect(wrapper.find("input").attributes().type).toBe("radio") + expect(wrapper.find("input").attributes().type).toBe("radio"); expect(wrapper.vm.pushEventToGA).toHaveBeenCalled(); }); @@ -827,7 +827,7 @@ describe("baseInputButton.vue", () => { // Assert expect(wrapper.vm.isChecked).toBe(true); - expect(wrapper.find("input").attributes().type).toBe("radio") + expect(wrapper.find("input").attributes().type).toBe("radio"); expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled(); }); @@ -854,7 +854,7 @@ describe("baseInputButton.vue", () => { // Assert expect(wrapper.vm.isChecked).toBe(true); - expect(wrapper.find("input").attributes().type).toBe("checkbox") + expect(wrapper.find("input").attributes().type).toBe("checkbox"); expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled(); }); @@ -881,7 +881,7 @@ describe("baseInputButton.vue", () => { // Assert expect(wrapper.vm.isChecked).toBe(true); - expect(wrapper.find("input").attributes().type).toBe("radio") + expect(wrapper.find("input").attributes().type).toBe("radio"); expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled(); }); @@ -908,7 +908,7 @@ describe("baseInputButton.vue", () => { // Assert expect(wrapper.vm.isChecked).toBe(false); - expect(wrapper.find("input").attributes().type).toBe("checkbox") + expect(wrapper.find("input").attributes().type).toBe("checkbox"); expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled(); }); @@ -935,7 +935,7 @@ describe("baseInputButton.vue", () => { // Assert expect(wrapper.vm.isChecked).toBe(false); - expect(wrapper.find("input").attributes().type).toBe("radio") + expect(wrapper.find("input").attributes().type).toBe("radio"); expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled(); }); @@ -962,7 +962,7 @@ describe("baseInputButton.vue", () => { // Assert expect(wrapper.vm.isChecked).toBe(true); - expect(wrapper.find("input").attributes().type).toBe("radio") + expect(wrapper.find("input").attributes().type).toBe("radio"); expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled(); }); }); @@ -1005,16 +1005,16 @@ describe("baseInputButton.vue", () => { const { wrapper } = setupMocks({ mockData: { propsData: { - setLastValuePushedToGa: jest.fn() - } - } + setLastValuePushedToGa: jest.fn(), + }, + }, }); // Act wrapper.vm.pushClickEventToGA("hi there"); // Assert - expect(wrapper.vm.setLastValuePushedToGa).toHaveBeenCalledWith("hi there") + expect(wrapper.vm.setLastValuePushedToGa).toHaveBeenCalledWith("hi there"); }); test("don't pass in a value => setLastValuePushedToGa is called with input button's value", () => { @@ -1023,16 +1023,16 @@ describe("baseInputButton.vue", () => { mockData: { propsData: { value: "hello there", - setLastValuePushedToGa: jest.fn() - } - } + setLastValuePushedToGa: jest.fn(), + }, + }, }); // Act wrapper.vm.pushClickEventToGA(); // Assert - expect(wrapper.vm.setLastValuePushedToGa).toHaveBeenCalledWith("hello there") + expect(wrapper.vm.setLastValuePushedToGa).toHaveBeenCalledWith("hello there"); }); }); }); diff --git a/src/common-components/base-input-button/base-input-button.vue b/src/common-components/base-input-button/base-input-button.vue index c01a9a94c..b71f4a63c 100644 --- a/src/common-components/base-input-button/base-input-button.vue +++ b/src/common-components/base-input-button/base-input-button.vue @@ -26,7 +26,10 @@ import { useField } from "vee-validate"; import { toRef } from "vue"; import { queryStrings } from "@/constants/query-strings"; -import { handleButtonComponentFocus, handleInputComponentBlur } from "@/helpers/button-question-focus-helper"; +import { + handleButtonComponentFocus, + handleInputComponentBlur, +} from "@/helpers/button-question-focus-helper"; import { inputButtonProps } from "@/common-components/base-input-button/button-functionality-props"; export default { @@ -53,9 +56,7 @@ export default { case this.eventTypes.ENTER: case this.eventTypes.CHANGE: this.handleClick(e); - this.handlePushClickEventToGACheck( - this.eventTypes.CLICK - ); + this.handlePushClickEventToGACheck(this.eventTypes.CLICK); break; } } else { @@ -64,9 +65,7 @@ export default { case this.eventTypes.ENTER: case this.eventTypes.SPACE: this.handleClick(e); - this.handlePushClickEventToGACheck( - this.eventTypes.CLICK - ); + this.handlePushClickEventToGACheck(this.eventTypes.CLICK); break; case this.eventTypes.CHANGE: this.selectingInitiatesLoad @@ -114,7 +113,8 @@ export default { // if from a click or click-like event if (source === this.eventTypes.CLICK) { this.pushClickEventToGA(); - } else { // if from tabbing around + } else { + // if from tabbing around if ( this.valueToEmit !== null && !this.isValueSelectedOnClick && diff --git a/src/common-components/base-input-button/button-functionality-props.js b/src/common-components/base-input-button/button-functionality-props.js index b9a63c925..1faa369e2 100644 --- a/src/common-components/base-input-button/button-functionality-props.js +++ b/src/common-components/base-input-button/button-functionality-props.js @@ -27,4 +27,4 @@ export const inputButtonProps = { type: Boolean, default: false, }, -} \ No newline at end of file +}; diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 53094e206..b417248a2 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -177,19 +177,16 @@ export default { return classes; }, buttonsInfo() { - return (Array.isArray(this.answers) ? this.answers : [])?.map( - (answer) => ({ - buttonLabel: answer.buttonLabel ?? answer.Text ?? answer, - altText: - answer.altText ?? (answer.Name ? answer.Name : answer), - buttonLabelSubCopy: - answer.buttonLabelSubCopy ?? answer.SubText, - buttonBodyCopy: answer.buttonBodyCopy ?? answer.buttonBodyCopy, - buttonAuxillaryCopy: answer.buttonAuxillaryCopy ?? answer.buttonAuxillaryCopy, - buttonFooterCopy: answer.buttonFooterCopy ?? answer.buttonFooterCopy, - buttonImage: answer.buttonImage ?? answer.AnswerImageUrl, - buttonImageId: answer.buttonImageId ?? answer.ImageId, - groupName: this.formatString(this.groupName), + return (Array.isArray(this.answers) ? this.answers : [])?.map((answer) => ({ + buttonLabel: answer.buttonLabel ?? answer.Text ?? answer, + altText: answer.altText ?? (answer.Name ? answer.Name : answer), + buttonLabelSubCopy: answer.buttonLabelSubCopy ?? answer.SubText, + buttonBodyCopy: answer.buttonBodyCopy ?? answer.buttonBodyCopy, + buttonAuxillaryCopy: answer.buttonAuxillaryCopy ?? answer.buttonAuxillaryCopy, + buttonFooterCopy: answer.buttonFooterCopy ?? answer.buttonFooterCopy, + buttonImage: answer.buttonImage ?? answer.AnswerImageUrl, + buttonImageId: answer.buttonImageId ?? answer.ImageId, + groupName: this.formatString(this.groupName), value: answer.value ?? (this.useTextForValue && answer.Text ? answer.Text : answer.Name) ?? diff --git a/src/common-components/date-picker/date-picker.vue b/src/common-components/date-picker/date-picker.vue index 548ca8d99..1c83c0a4e 100644 --- a/src/common-components/date-picker/date-picker.vue +++ b/src/common-components/date-picker/date-picker.vue @@ -1,431 +1,439 @@ diff --git a/src/common-components/dropdown-question/dropdown-question.spec.js b/src/common-components/dropdown-question/dropdown-question.spec.js index 381f34890..249b00710 100644 --- a/src/common-components/dropdown-question/dropdown-question.spec.js +++ b/src/common-components/dropdown-question/dropdown-question.spec.js @@ -4,167 +4,156 @@ import dropdownQuestion from "./dropdown-question"; // Mock CMS content const questionText = "Question Text"; const mockMixin = { - methods: { - getCmsContent: jest.fn().mockImplementation(()=> { - return questionText; - }) - } -} + methods: { + getCmsContent: jest.fn().mockImplementation(() => { + return questionText; + }), + }, +}; // TODO: Remove the following from dropdown-question.vue -> :class="(errors && errors.length) || hasError ? 'has-error' : ''" // It is not being used. describe("dropdownQuestion.vue", () => { + it("Should render a select input", async () => { + // Arrange + const wrapper = shallowMount(dropdownQuestion, { + propsData: { + options: {}, + }, + mixins: [mockMixin], + }); - it("Should render a select input", async () => { + wrapper.getCmsContent = jest.fn(); - // Arrange - const wrapper = shallowMount(dropdownQuestion, { - propsData: { - options: {}, - }, - mixins: [mockMixin] + // Act + const select = wrapper.find("select"); + + // Assert + expect(select.exists()).toBe(true); }); - wrapper.getCmsContent = jest.fn(); + it("Should render the 'questionText' data value as the label text.", async () => { + // Arrange + const wrapper = shallowMount(dropdownQuestion, { + propsData: { + options: {}, + }, + mixins: [mockMixin], + }); - // Act - const select = wrapper.find("select"); + // Act + const label = wrapper.find("label"); - // Assert - expect(select.exists()).toBe(true); - - }); - - it("Should render the 'questionText' data value as the label text.", async () => { - // Arrange - const wrapper = shallowMount(dropdownQuestion, { - propsData: { - options: {}, - }, - mixins: [mockMixin] + // Assert + expect(label.text()).toContain(questionText); }); - // Act - const label = wrapper.find("label"); + it("Should render the 'questionText' data value with '⁠' after the first character of each word in the label text when disableAutoFill is true.", async () => { + // Arrange + const wrapper = shallowMount(dropdownQuestion, { + propsData: { + options: {}, + disableAutoFill: true, + }, + mixins: [mockMixin], + }); - // Assert - expect(label.text()).toContain(questionText); + // Mock CMS content ... + // Trust me, the below instance of the string "Q⁠uestion Text" actually has the ⁠ in it. You just can't see it + // Don't believe me? Copy and paste it into Google. Then inspect the search field element in Dev Tools, + // you will see "Q⁠uestion T⁠ext" + const expectedQuestionText = "Q⁠uestion T⁠ext"; - }); + // Act + const label = wrapper.find("label"); - it("Should render the 'questionText' data value with '⁠' after the first character of each word in the label text when disableAutoFill is true.", async () => { - // Arrange - const wrapper = shallowMount(dropdownQuestion, { - propsData: { - options: {}, - disableAutoFill: true, - }, - mixins: [mockMixin] + // Assert + expect(label.text()).toContain(expectedQuestionText); }); - // Mock CMS content ... - // Trust me, the below instance of the string "Q⁠uestion Text" actually has the ⁠ in it. You just can't see it - // Don't believe me? Copy and paste it into Google. Then inspect the search field element in Dev Tools, - // you will see "Q⁠uestion T⁠ext" - const expectedQuestionText = "Q⁠uestion T⁠ext"; + it("Should return input id as the id of the select field", async () => { + // Arrange + const wrapper = shallowMount(dropdownQuestion, { + propsData: { + inputId: "input ID", + options: {}, + }, + mixins: [mockMixin], + }); - // Act - const label = wrapper.find("label"); + // Act + const select = wrapper.find("select"); - // Assert - expect(label.text()).toContain(expectedQuestionText); - - }); - - it("Should return input id as the id of the select field", async () => { - // Arrange - const wrapper = shallowMount(dropdownQuestion, { - propsData: { - inputId: "input ID", - options: {}, - }, - mixins: [mockMixin] + // Assert + expect(select.attributes().id).toEqual("input ID"); }); - // Act - const select = wrapper.find("select"); + it("Should render the 'questionText' data value as the aria-label attribute.", async () => { + // Arrange + const wrapper = shallowMount(dropdownQuestion, { + propsData: { + options: {}, + }, + mixins: [mockMixin], + }); - // Assert - expect(select.attributes().id).toEqual("input ID"); + // Act + const label = wrapper.find("label"); - }); - - it("Should render the 'questionText' data value as the aria-label attribute.", async () => { - // Arrange - const wrapper = shallowMount(dropdownQuestion, { - propsData: { - options: {}, - }, - mixins: [mockMixin] + // Assert + expect(label.attributes("aria-label")).toContain(questionText); }); - // Act - const label = wrapper.find("label"); + it("Should return aria-disabled state as disabled", async () => { + // Arrange + const wrapper = shallowMount(dropdownQuestion, { + propsData: { + options: {}, + isDisabled: true, + }, + mixins: [mockMixin], + }); - // Assert - expect(label.attributes("aria-label")).toContain(questionText); + // Act + const select = wrapper.find("select"); - }); - - it("Should return aria-disabled state as disabled", async () => { - // Arrange - const wrapper = shallowMount(dropdownQuestion, { - propsData: { - options: {}, - isDisabled: true, - }, - mixins: [mockMixin] + // Assert + expect(select.attributes("aria-disabled")).toEqual("true"); }); - // Act - const select = wrapper.find("select"); + it("Should emit new value when modelValue is changed", async () => { + // Arrange + const wrapper = shallowMount(dropdownQuestion, { + propsData: { + options: {}, + modelValue: "val", + }, + mixins: [mockMixin], + }); - // Assert - expect(select.attributes("aria-disabled")).toEqual("true"); + // Act + await wrapper.find("select").setValue("val2"); - }); - - it("Should emit new value when modelValue is changed", async () => { - // Arrange - const wrapper = shallowMount(dropdownQuestion, { - propsData: { - options: {}, - modelValue: "val", - }, - mixins: [mockMixin] + // Assert + expect(wrapper.emitted()).toHaveProperty("change"); }); - // Act - await wrapper.find("select").setValue("val2"); + it("Should call this.handleChange with new value when selectedOption is changed", async () => { + // Arrange + const wrapper = shallowMount(dropdownQuestion, { + propsData: { + options: {}, + modelValue: 0, + }, + mixins: [mockMixin], + }); - // Assert - expect(wrapper.emitted()).toHaveProperty('change') + wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); - }); + // Act + wrapper.vm.$options.watch.selectedOption.call(wrapper.vm, 1); - it("Should call this.handleChange with new value when selectedOption is changed", async () => { - // Arrange - const wrapper = shallowMount(dropdownQuestion, { - propsData: { - options: {}, - modelValue: 0, - }, - mixins: [mockMixin] + // Assert + expect(wrapper.vm.handleChange).toHaveBeenCalled; }); - - wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); - - // Act - wrapper.vm.$options.watch.selectedOption.call(wrapper.vm, 1); - - // Assert - expect(wrapper.vm.handleChange).toHaveBeenCalled; - - }); - }); diff --git a/src/common-components/dropdown-question/dropdown-question.vue b/src/common-components/dropdown-question/dropdown-question.vue index 8e6ae0faa..a940e1df8 100644 --- a/src/common-components/dropdown-question/dropdown-question.vue +++ b/src/common-components/dropdown-question/dropdown-question.vue @@ -1,152 +1,159 @@ - - diff --git a/src/common-components/funnel-footer/funnel-footer.spec.js b/src/common-components/funnel-footer/funnel-footer.spec.js index d246b602b..cd2a06663 100644 --- a/src/common-components/funnel-footer/funnel-footer.spec.js +++ b/src/common-components/funnel-footer/funnel-footer.spec.js @@ -2,43 +2,41 @@ import { mount } from "@vue/test-utils"; import funnelFooter from "./funnel-footer"; describe("funnel-footer.vue", () => { - - it("Should emit ForwardClicked on button click", async () => { - // Act - const wrapper = mount(funnelFooter, { - mixins: [mockMixin] + it("Should emit ForwardClicked on button click", async () => { + // Act + const wrapper = mount(funnelFooter, { + mixins: [mockMixin], + }); + wrapper.vm.buttonClick(); + // Assert + expect(wrapper.emitted()["ForwardClicked"][0]).toHaveBeenCalled; }); - wrapper.vm.buttonClick(); - // Assert - expect(wrapper.emitted()["ForwardClicked"][0]).toHaveBeenCalled; - }); - it("Should emit BackClicked on link click", async () => { - // Act - const wrapper = mount(funnelFooter, { - mixins: [mockMixin] + it("Should emit BackClicked on link click", async () => { + // Act + const wrapper = mount(funnelFooter, { + mixins: [mockMixin], + }); + wrapper.vm.linkClick(); + // Assert + expect(wrapper.emitted()["BackClicked"][0]).toHaveBeenCalled; }); - wrapper.vm.linkClick(); - // Assert - expect(wrapper.emitted()["BackClicked"][0]).toHaveBeenCalled; - }); - it("Should change button text when update button text is called", async () => { - // Act - const wrapper = mount(funnelFooter, { - mixins: [mockMixin] + it("Should change button text when update button text is called", async () => { + // Act + const wrapper = mount(funnelFooter, { + mixins: [mockMixin], + }); + wrapper.vm.updateButtonText("newText"); + + // Assert + expect(wrapper.componentVM.customButtontext).toBe("newText"); }); - wrapper.vm.updateButtonText('newText'); - - // Assert - expect(wrapper.componentVM.customButtontext).toBe('newText'); - }); - }); const mockMixin = { - methods: { - getCmsContent: jest.fn(), - getFooterInfoBoxHeight: jest.fn(()=>80) - } -} + methods: { + getCmsContent: jest.fn(), + getFooterInfoBoxHeight: jest.fn(() => 80), + }, +}; diff --git a/src/common-components/funnel-footer/funnel-footer.vue b/src/common-components/funnel-footer/funnel-footer.vue index 2c4152cfa..2a6119681 100644 --- a/src/common-components/funnel-footer/funnel-footer.vue +++ b/src/common-components/funnel-footer/funnel-footer.vue @@ -1,33 +1,31 @@ diff --git a/src/common-components/funnel-header/funnel-header.spec.js b/src/common-components/funnel-header/funnel-header.spec.js index a9b9535aa..380f5400c 100644 --- a/src/common-components/funnel-header/funnel-header.spec.js +++ b/src/common-components/funnel-header/funnel-header.spec.js @@ -2,25 +2,25 @@ import { shallowMount } from "@vue/test-utils"; import funnelHeader from "./funnel-header"; describe("funnelHeader", () => { - test("renders the logo image", () => { - // Arrange + test("renders the logo image", () => { + // Arrange - // Act - const wrapper = shallowMount(funnelHeader, { - setData: { - imageSrc: "image_url", - }, - mixins: [mockMixin] + // Act + const wrapper = shallowMount(funnelHeader, { + setData: { + imageSrc: "image_url", + }, + mixins: [mockMixin], + }); + + // Assert + expect(wrapper.find("img")).toBeTruthy(); + wrapper.unmount(); }); - - // Assert - expect(wrapper.find("img")).toBeTruthy(); - wrapper.unmount(); - }); }); const mockMixin = { - methods: { - getCmsContent: jest.fn() - } -} + methods: { + getCmsContent: jest.fn(), + }, +}; diff --git a/src/common-components/funnel-header/funnel-header.vue b/src/common-components/funnel-header/funnel-header.vue index dcc997c5f..f952b94a8 100644 --- a/src/common-components/funnel-header/funnel-header.vue +++ b/src/common-components/funnel-header/funnel-header.vue @@ -1,20 +1,18 @@ diff --git a/src/common-components/funnel-header/menu-modal/menu-modal.vue b/src/common-components/funnel-header/menu-modal/menu-modal.vue index 308f0cb0d..55ed98dd9 100644 --- a/src/common-components/funnel-header/menu-modal/menu-modal.vue +++ b/src/common-components/funnel-header/menu-modal/menu-modal.vue @@ -1,169 +1,206 @@ diff --git a/src/common-components/funnel-sub-header/button-back/button-back.spec.js b/src/common-components/funnel-sub-header/button-back/button-back.spec.js index c7bff87f0..403987233 100644 --- a/src/common-components/funnel-sub-header/button-back/button-back.spec.js +++ b/src/common-components/funnel-sub-header/button-back/button-back.spec.js @@ -2,20 +2,20 @@ import { shallowMount } from "@vue/test-utils"; import buttonBack from "./button-back"; describe("back button", () => { - test("renders a button", () => { - // Arrange - const myFunction = () => {}; + test("renders a button", () => { + // Arrange + const myFunction = () => {}; - // Act - const wrapper = shallowMount(buttonBack, { - propsData: { - backButtonAction: myFunction, - backButtonAccessibleText: "something", - }, + // Act + const wrapper = shallowMount(buttonBack, { + propsData: { + backButtonAction: myFunction, + backButtonAccessibleText: "something", + }, + }); + + // Assert + expect(wrapper.find("button").exists()).toBe(true); + wrapper.unmount(); }); - - // Assert - expect(wrapper.find("button").exists()).toBe(true); - wrapper.unmount(); - }); }); diff --git a/src/common-components/funnel-sub-header/button-back/button-back.vue b/src/common-components/funnel-sub-header/button-back/button-back.vue index cf3a630bf..75121a4e7 100644 --- a/src/common-components/funnel-sub-header/button-back/button-back.vue +++ b/src/common-components/funnel-sub-header/button-back/button-back.vue @@ -1,82 +1,71 @@ diff --git a/src/common-components/funnel-sub-header/funnel-sub-header.spec.js b/src/common-components/funnel-sub-header/funnel-sub-header.spec.js index ebcb9ca91..51e622bb6 100644 --- a/src/common-components/funnel-sub-header/funnel-sub-header.spec.js +++ b/src/common-components/funnel-sub-header/funnel-sub-header.spec.js @@ -2,20 +2,20 @@ import { shallowMount } from "@vue/test-utils"; import FunnelSubHeader from "./funnel-sub-header"; describe("FunnelSubHeader.vue", () => { - it("Should render the 'text' data value as a span value for the header span text value.", async () => { - // Act - const wrapper = shallowMount(FunnelSubHeader, { - mixins: [mockMixin] - }); - wrapper.vm.clickEvent(); + it("Should render the 'text' data value as a span value for the header span text value.", async () => { + // Act + const wrapper = shallowMount(FunnelSubHeader, { + mixins: [mockMixin], + }); + wrapper.vm.clickEvent(); - // Assert - expect(wrapper.emitted()).toEqual({"click-event": [[]]}); - }); + // Assert + expect(wrapper.emitted()).toEqual({ "click-event": [[]] }); + }); }); const mockMixin = { - methods: { - getCmsContent: jest.fn() - } -} + methods: { + getCmsContent: jest.fn(), + }, +}; diff --git a/src/common-components/funnel-sub-header/funnel-sub-header.vue b/src/common-components/funnel-sub-header/funnel-sub-header.vue index 3ad7c71d3..ba977c55a 100644 --- a/src/common-components/funnel-sub-header/funnel-sub-header.vue +++ b/src/common-components/funnel-sub-header/funnel-sub-header.vue @@ -1,79 +1,80 @@ diff --git a/src/common-components/loading-modal/loading-modal.spec.js b/src/common-components/loading-modal/loading-modal.spec.js index 7a500ce73..36a6d6e33 100644 --- a/src/common-components/loading-modal/loading-modal.spec.js +++ b/src/common-components/loading-modal/loading-modal.spec.js @@ -4,44 +4,42 @@ import { getMountOptions } from "@/helpers/unit-test-helper.js"; import store from "@/store"; jest.mock( - "@/store", - () => { - return {}; - }, - { virtual: true } + "@/store", + () => { + return {}; + }, + { virtual: true } ); -jest.mock('@/assets/img/loader.gif', () => 'loader.gif') -jest.mock('@/assets/img/windshield.png', () => 'windshield.png') +jest.mock("@/assets/img/loader.gif", () => "loader.gif"); +jest.mock("@/assets/img/windshield.png", () => "windshield.png"); describe("loadingModal", () => { - test("showModal sets modal visible", async () => { - // Arrange - const { wrapper } = setupMocks(); - wrapper.vm.isModalVisible = false; + test("showModal sets modal visible", async () => { + // Arrange + const { wrapper } = setupMocks(); + wrapper.vm.isModalVisible = false; - //Act - wrapper.vm.showModal(); - - // Assert - expect(wrapper.vm.isModalVisible).toEqual(true); - wrapper.unmount(); - }); + //Act + wrapper.vm.showModal(); + + // Assert + expect(wrapper.vm.isModalVisible).toEqual(true); + wrapper.unmount(); + }); }); - function setupMocks() { + //Mock store + store.dispatch = jest.fn(() => {}); + store.getters = {}; + const mountOptions = getMountOptions({ + store: { + dispatch: store.dispatch, + getters: store.getters, + }, + }); - //Mock store - store.dispatch = jest.fn(() => {}); - store.getters = { }; - const mountOptions = getMountOptions({ - store: { - dispatch: store.dispatch, - getters: store.getters, - }, - }); - - const wrapper = shallowMount(loadingModal, mountOptions); - return { wrapper }; -} \ No newline at end of file + const wrapper = shallowMount(loadingModal, mountOptions); + return { wrapper }; +} diff --git a/src/common-components/loading-modal/loading-modal.vue b/src/common-components/loading-modal/loading-modal.vue index 2bcdc9c31..8e4d1d994 100644 --- a/src/common-components/loading-modal/loading-modal.vue +++ b/src/common-components/loading-modal/loading-modal.vue @@ -1,236 +1,250 @@ diff --git a/src/common-components/modal/modal.spec.js b/src/common-components/modal/modal.spec.js index 717b6f895..88d5daf58 100644 --- a/src/common-components/modal/modal.spec.js +++ b/src/common-components/modal/modal.spec.js @@ -1,61 +1,60 @@ import { shallowMount } from "@vue/test-utils"; import Modal from "./modal"; - describe("modal.vue", () => { - it("Should display header text when HeaderText is defined in the CMS", async () => { - // Act - const wrapper = shallowMount(Modal, { - mixins: [mockMixin] + it("Should display header text when HeaderText is defined in the CMS", async () => { + // Act + const wrapper = shallowMount(Modal, { + mixins: [mockMixin], + }); + expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"])); }); - expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['HeaderText'])); - }); - it("Should display subheader text when SubheaderText is defined in the CMS", async () => { - // Act - const wrapper = shallowMount(Modal, { - mixins: [mockMixin] + it("Should display subheader text when SubheaderText is defined in the CMS", async () => { + // Act + const wrapper = shallowMount(Modal, { + mixins: [mockMixin], + }); + expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["SubheaderText"])); }); - expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['SubheaderText'])); - }); - it("Should insert image url when Image is defined in the CMS", async () => { - // Act - const wrapper = shallowMount(Modal, { - mixins: [mockMixin] + it("Should insert image url when Image is defined in the CMS", async () => { + // Act + const wrapper = shallowMount(Modal, { + mixins: [mockMixin], + }); + expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["Image"])); }); - expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['Image'])); - }); - it("Should display body text when BodyText is defined in the CMS", async () => { - // Act - const wrapper = shallowMount(Modal, { - mixins: [mockMixin] + it("Should display body text when BodyText is defined in the CMS", async () => { + // Act + const wrapper = shallowMount(Modal, { + mixins: [mockMixin], + }); + expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["BodyText"])); }); - expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['BodyText'])); - }); - it("Should display footer text when FooterText is defined in the CMS", async () => { - // Act - const wrapper = shallowMount(Modal, { - mixins: [mockMixin] + it("Should display footer text when FooterText is defined in the CMS", async () => { + // Act + const wrapper = shallowMount(Modal, { + mixins: [mockMixin], + }); + expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["FooterText"])); }); - expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['FooterText'])); - }); }); const mockMixin = { - methods: { - getCmsContent: jest.fn((widgetName, cmsFieldName) => { - return mockCmsContent[cmsFieldName]; - }) - } -} + methods: { + getCmsContent: jest.fn((widgetName, cmsFieldName) => { + return mockCmsContent[cmsFieldName]; + }), + }, +}; const mockCmsContent = { - 'HeaderText': "Sample header text here.", - 'SubheaderText': "Sample subheader text here.", - 'Image': "https://www.sampleImage.sample", - 'BodyText': "Sample body text here.", - 'FooterText': "Sample footer text here.", -} + HeaderText: "Sample header text here.", + SubheaderText: "Sample subheader text here.", + Image: "https://www.sampleImage.sample", + BodyText: "Sample body text here.", + FooterText: "Sample footer text here.", +}; diff --git a/src/common-components/question-chain/question-chain.vue b/src/common-components/question-chain/question-chain.vue index 40d8be911..6121cbd25 100644 --- a/src/common-components/question-chain/question-chain.vue +++ b/src/common-components/question-chain/question-chain.vue @@ -2,17 +2,16 @@
+ :validationRules="validationRules" />
@@ -22,61 +21,65 @@ import buttonQuestion from "@/common-components/button-question/button-question" import { useValidateForm } from "vee-validate"; export default { - name: "questionChain", - data() { - return { - currentQuestionNum: 0, - questions: [], - }; - }, - props: { - questionData: Object, - validationRules: String, - modelValue: Array, - glassIndex: Number, - }, - async created() { - // do a test validation check upon create to prevent out of sync / incorrect valid states - await useValidateForm(); // NOTE: needs to have async/await here; tested and won't work without it - - this.questionData.map((q, i) => { - const question = { - questionText: q.questionText, - questionSequence: q.questionSequence, - answers: q.answers.map((a) => { - return { - buttonLabel: a.answerText, - // Name will either be nextQuestionSequence or answerResult - // Name will be used by list-button as the input value. - // It must be a single string or number, so concatenating together a string with - // 4 pieces of data separated by pipe characters: - // question number|type of answer|answer value|answer text - value: a.nextQuestionSequence ? - q.questionSequence + "|nextQuestion|" + a.nextQuestionSequence + "|" + a.answerText : - q.questionSequence + "|answer|" + a.answerResult + "|" + a.answerText, - nextQuestionSequence: a.nextQuestionSequence, - answerResult: a.answerResult, - questionSequence: q.questionSequence, - questionType: a.nextQuestionSequence ? "nextQuestion" : "answer", - } - }), - answerSelected: q.answerSelected || "", + name: "questionChain", + data() { + return { + currentQuestionNum: 0, + questions: [], }; - if (!q.suppressQuestion) { - this.questions.push(question); - } - }); + }, + props: { + questionData: Object, + validationRules: String, + modelValue: Array, + glassIndex: Number, + }, + async created() { + // do a test validation check upon create to prevent out of sync / incorrect valid states + await useValidateForm(); // NOTE: needs to have async/await here; tested and won't work without it - if (!this.modelValue?.length > 0 && this.questions.length > 0) { - // set this.currentQuestionNum to first valid question - this.currentQuestionNum = this.questions[0].questionSequence; - // scroll the next question into view - this.$nextTick(() => { - document.querySelector('.current-question')?.scrollIntoView({behavior: "smooth"}); - }) - } - }, - methods: { + this.questionData.map((q, i) => { + const question = { + questionText: q.questionText, + questionSequence: q.questionSequence, + answers: q.answers.map((a) => { + return { + buttonLabel: a.answerText, + // Name will either be nextQuestionSequence or answerResult + // Name will be used by list-button as the input value. + // It must be a single string or number, so concatenating together a string with + // 4 pieces of data separated by pipe characters: + // question number|type of answer|answer value|answer text + value: a.nextQuestionSequence + ? q.questionSequence + + "|nextQuestion|" + + a.nextQuestionSequence + + "|" + + a.answerText + : q.questionSequence + "|answer|" + a.answerResult + "|" + a.answerText, + nextQuestionSequence: a.nextQuestionSequence, + answerResult: a.answerResult, + questionSequence: q.questionSequence, + questionType: a.nextQuestionSequence ? "nextQuestion" : "answer", + }; + }), + answerSelected: q.answerSelected || "", + }; + if (!q.suppressQuestion) { + this.questions.push(question); + } + }); + + if (!this.modelValue?.length > 0 && this.questions.length > 0) { + // set this.currentQuestionNum to first valid question + this.currentQuestionNum = this.questions[0].questionSequence; + // scroll the next question into view + this.$nextTick(() => { + document.querySelector(".current-question")?.scrollIntoView({ behavior: "smooth" }); + }); + } + }, + methods: { handleAnswer(question, returnedAnswer) { question.answerSelected = returnedAnswer; /* @@ -93,8 +96,11 @@ export default { this.$emit("update:modelValue", isQuestionChainComplete); } }, - getQuestionChainAnswerIfComplete(returnedAnswer) { // this method will return either a final answer or Boolean false - if (!returnedAnswer) { return false } + getQuestionChainAnswerIfComplete(returnedAnswer) { + // this method will return either a final answer or Boolean false + if (!returnedAnswer) { + return false; + } // Example returnedAnswers: // "1|nextQuestion|3|No" @@ -116,7 +122,7 @@ export default { } // remove all answers AFTER this question... // (needed in case user is changing previously answered questions) - if ((q.questionSequence > questionNum)) { + if (q.questionSequence > questionNum) { delete q.answerSelected; } if (q.answerSelected) { @@ -130,27 +136,25 @@ export default { // return false if there's a nextQuestion... or return an object with final answers (truthy) if (questionType === "nextQuestion") { - // update to next question index this.currentQuestionNum = parseInt(questionAnswer); // update count to display next question // scroll the next question into view this.$nextTick(() => { - document.querySelector('.current-question').scrollIntoView({behavior: "smooth"}); - }) + document + .querySelector(".current-question") + .scrollIntoView({ behavior: "smooth" }); + }); return false; - } else { - // reset current question index (removes .current-question class) this.currentQuestionNum = 0; // reset count - + // return an object with the part answer, all the answered questions, and the part index return { answerResult: questionAnswer, answeredQuestions: answeredQuestions, glassIndex: this.glassIndex, }; - } }, }, diff --git a/src/common-components/text-block/text-block.spec.js b/src/common-components/text-block/text-block.spec.js index 138a0340b..3cd229449 100644 --- a/src/common-components/text-block/text-block.spec.js +++ b/src/common-components/text-block/text-block.spec.js @@ -1,60 +1,59 @@ import { shallowMount } from "@vue/test-utils"; import TextBlock from "./text-block"; - describe("modal.vue", () => { - it("Should display 'Text' when 'Text' is defined in the CMS", async () => { - // Act - const wrapper = shallowMount(TextBlock, { - mixins: [mockMixin] + it("Should display 'Text' when 'Text' is defined in the CMS", async () => { + // Act + const wrapper = shallowMount(TextBlock, { + mixins: [mockMixin], + }); + expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["Text"])); }); - expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['Text'])); - }); - it("Should contain the typeStyle class as defined by the prop", async () => { - // Act - const wrapper = shallowMount(TextBlock, { - mixins: [mockMixin], - propsData: mockProps, - }); - expect(wrapper.html()).toEqual(expect.stringContaining(mockProps['typeStyle'])); - }); - it("Should contain the justifyText class as defined by the prop", async () => { - // Act - const wrapper = shallowMount(TextBlock, { - mixins: [mockMixin], - propsData: mockProps, - }); - expect(wrapper.html()).toEqual(expect.stringContaining(mockProps['justifyText'])); - }); - it("Should contain the fontWeight class as defined by the prop", async () => { - // Act - const wrapper = shallowMount(TextBlock, { - mixins: [mockMixin], - propsData: mockProps, - }); - expect(wrapper.html()).toEqual(expect.stringContaining(mockProps['fontWeight'])); - }); + it("Should contain the typeStyle class as defined by the prop", async () => { + // Act + const wrapper = shallowMount(TextBlock, { + mixins: [mockMixin], + propsData: mockProps, + }); + expect(wrapper.html()).toEqual(expect.stringContaining(mockProps["typeStyle"])); + }); + it("Should contain the justifyText class as defined by the prop", async () => { + // Act + const wrapper = shallowMount(TextBlock, { + mixins: [mockMixin], + propsData: mockProps, + }); + expect(wrapper.html()).toEqual(expect.stringContaining(mockProps["justifyText"])); + }); + it("Should contain the fontWeight class as defined by the prop", async () => { + // Act + const wrapper = shallowMount(TextBlock, { + mixins: [mockMixin], + propsData: mockProps, + }); + expect(wrapper.html()).toEqual(expect.stringContaining(mockProps["fontWeight"])); + }); }); - /////////////// - // Constants // - /////////////// +/////////////// +// Constants // +/////////////// const mockMixin = { - methods: { - getCmsContent: jest.fn((widgetName, cmsFieldName) => { - return mockCmsContent[cmsFieldName]; - }) - } -} + methods: { + getCmsContent: jest.fn((widgetName, cmsFieldName) => { + return mockCmsContent[cmsFieldName]; + }), + }, +}; const mockProps = { - fontWeight: 'mockFontWeight', - typeStyle: 'mockTypeStyle', - justifyText: 'mockJustifyText' -} + fontWeight: "mockFontWeight", + typeStyle: "mockTypeStyle", + justifyText: "mockJustifyText", +}; const mockCmsContent = { - 'Text': "Sample text here.", -} + Text: "Sample text here.", +}; diff --git a/src/common-components/text-block/text-block.vue b/src/common-components/text-block/text-block.vue index 88f3bda2a..a12d3f398 100644 --- a/src/common-components/text-block/text-block.vue +++ b/src/common-components/text-block/text-block.vue @@ -1,38 +1,40 @@ diff --git a/src/common-components/textbox-question/textbox-question.spec.js b/src/common-components/textbox-question/textbox-question.spec.js index b830b9783..54e23e869 100644 --- a/src/common-components/textbox-question/textbox-question.spec.js +++ b/src/common-components/textbox-question/textbox-question.spec.js @@ -4,167 +4,157 @@ import textboxQuestion from "./textbox-question"; // Mock CMS content const questionText = "Question Text"; const mockMixin = { - methods: { - getCmsContent: jest.fn().mockImplementation(()=> { - return questionText; - }) - } -} + methods: { + getCmsContent: jest.fn().mockImplementation(() => { + return questionText; + }), + }, +}; const maska = jest.fn(); describe("textboxQuestion.vue", () => { + it("Should render a text input", async () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + mixins: [mockMixin], + }); - it("Should render a text input", async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska: maska, - } - }, - mixins: [mockMixin] + wrapper.getCmsContent = jest.fn(); + + // Act + const input = wrapper.find("input"); + + // Assert + expect(input.exists()).toBe(true); }); - wrapper.getCmsContent = jest.fn(); + it("Should render the 'questionText' data value as the label text when disableAutoFill is false.", async () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + mixins: [mockMixin], + }); - // Act - const input = wrapper.find("input"); + // Act + const label = wrapper.find("label"); - // Assert - expect(input.exists()).toBe(true); - - }); - - it("Should render the 'questionText' data value as the label text when disableAutoFill is false.", async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska: maska, - } - }, - mixins: [mockMixin] + // Assert + expect(label.text()).toContain(questionText); }); - // Act - const label = wrapper.find("label"); + it("Should return input id as the id of the input field", async () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + inputId: "input ID", + }, + mixins: [mockMixin], + }); - // Assert - expect(label.text()).toContain(questionText); + // Act + const input = wrapper.find("input"); - }); - - it("Should return input id as the id of the input field", async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska: maska, - } - }, - propsData: { - inputId: "input ID", - }, - mixins: [mockMixin] + // Assert + expect(input.attributes().id).toEqual("input ID"); }); - // Act - const input = wrapper.find("input"); + it("Should render the 'questionText' data value as the aria-label attribute.", async () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + mixins: [mockMixin], + }); - // Assert - expect(input.attributes().id).toEqual("input ID"); + // Act + const label = wrapper.find("label"); - }); - - it("Should render the 'questionText' data value as the aria-label attribute.", async () => { - - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska: maska, - } - }, - mixins: [mockMixin] + // Assert + expect(label.attributes("aria-label")).toContain(questionText); }); - // Act - const label = wrapper.find("label"); + it("Should return aria-disabled state as disabled", async () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + isDisabled: true, + }, + mixins: [mockMixin], + }); - // Assert - expect(label.attributes("aria-label")).toContain(questionText); + // Assert + const input = wrapper.find("input"); - }); - - it("Should return aria-disabled state as disabled", async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska: maska, - } - }, - propsData: { - isDisabled: true, - }, - mixins: [mockMixin] + // Expect + expect(input.attributes("aria-disabled")).toEqual("true"); }); - // Assert - const input = wrapper.find("input"); + it("Should emit new value when modelValue is changed", async () => { + // Act + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + modelValue: "val", + }, + mixins: [mockMixin], + }); - // Expect - expect(input.attributes("aria-disabled")).toEqual("true"); + await wrapper.find("input").setValue("val2"); - }); - - it("Should emit new value when modelValue is changed", async () => { - // Act - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska: maska, - } - }, - propsData: { - modelValue: "val", - }, - mixins: [mockMixin] + // Assert + expect(wrapper.emitted()).toHaveProperty("change"); }); - await wrapper.find("input").setValue("val2"); + it("Should call this.handleChange with new value when the value is changed and the new value is valid", async () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + options: {}, + modelValue: "foo", + }, + mixins: [mockMixin], + }); - // Assert - expect(wrapper.emitted()).toHaveProperty('change') + wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); + wrapper.vm.validate = jest.fn().mockImplementation(() => { + return true; + }); - }); + // Act + wrapper.vm.$options.watch.value.call(wrapper.vm, "bar"); - it("Should call this.handleChange with new value when the value is changed and the new value is valid", async () => { - // Arrange - const wrapper = shallowMount(textboxQuestion, { - global: { - directives: { - maska: maska, - } - }, - propsData: { - options: {}, - modelValue: "foo", - }, - mixins: [mockMixin] + // Assert + expect(wrapper.vm.handleChange).toHaveBeenCalled; }); - - wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); - wrapper.vm.validate = jest.fn().mockImplementation(() => { - return true; - }); - - // Act - wrapper.vm.$options.watch.value.call(wrapper.vm, "bar"); - - // Assert - expect(wrapper.vm.handleChange).toHaveBeenCalled; - - }); - }); diff --git a/src/common-components/textbox-question/textbox-question.vue b/src/common-components/textbox-question/textbox-question.vue index 34fc614e7..62b605b7b 100644 --- a/src/common-components/textbox-question/textbox-question.vue +++ b/src/common-components/textbox-question/textbox-question.vue @@ -1,189 +1,195 @@ diff --git a/src/common-components/vehicle-banner/vehicle-banner.spec.js b/src/common-components/vehicle-banner/vehicle-banner.spec.js index e97781fc3..b4d723e18 100644 --- a/src/common-components/vehicle-banner/vehicle-banner.spec.js +++ b/src/common-components/vehicle-banner/vehicle-banner.spec.js @@ -5,107 +5,121 @@ import store from "@/store"; import { vehicleCategories } from "@/constants/vehicle-categories.js"; jest.mock( - "@/store", - () => { - return {}; - }, - { virtual: true } + "@/store", + () => { + return {}; + }, + { virtual: true } ); describe("vehicleBanner", () => { - test("renders the blurrycar image", async () => { - // Arrange - const { wrapper } = setupMocks({ displayGenericVehicleImageProp: true, imageUrlValue: "NULL" }); - - // Assert - expect(wrapper.vm.vehicleImageToDisplay).toEqual(wrapper.vm.genericVehicleImage); - expect(wrapper.find("img").attributes("class")).toContain("vehicle-image"); - wrapper.unmount(); - }); + test("renders the blurrycar image", async () => { + // Arrange + const { wrapper } = setupMocks({ + displayGenericVehicleImageProp: true, + imageUrlValue: "NULL", + }); + + // Assert + expect(wrapper.vm.vehicleImageToDisplay).toEqual(wrapper.vm.genericVehicleImage); + expect(wrapper.find("img").attributes("class")).toContain("vehicle-image"); + wrapper.unmount(); + }); }); describe("vehicleBanner", () => { - test("renders expected vehicle image", async () => { - // Arrange - const { wrapper } = setupMocks({ displayGenericVehicleImageProp: false, imageUrlValue: "url_to_vehicle_image" }); + test("renders expected vehicle image", async () => { + // Arrange + const { wrapper } = setupMocks({ + displayGenericVehicleImageProp: false, + imageUrlValue: "url_to_vehicle_image", + }); - // Assert - expect(wrapper.vm.vehicleImageToDisplay).toEqual( "url_to_vehicle_image"); - expect(wrapper.find("img").attributes("class")).toContain("vehicle-image"); - wrapper.unmount(); - }); + // Assert + expect(wrapper.vm.vehicleImageToDisplay).toEqual("url_to_vehicle_image"); + expect(wrapper.find("img").attributes("class")).toContain("vehicle-image"); + wrapper.unmount(); + }); }); describe("vehicleBanner", () => { - test("should render car icon when imageUrl is null and category is default", async () => { - // Arrange - const { wrapper } = setupMocks({ displayGenericVehicleImageProp: false, imageUrlValue: "NULL" }); - - var iconUrl = wrapper.vm.vehicleImageToDisplay; + test("should render car icon when imageUrl is null and category is default", async () => { + // Arrange + const { wrapper } = setupMocks({ + displayGenericVehicleImageProp: false, + imageUrlValue: "NULL", + }); - // Assert - expect(iconUrl).toEqual(undefined); - wrapper.unmount(); - }); + var iconUrl = wrapper.vm.vehicleImageToDisplay; + + // Assert + expect(iconUrl).toEqual(undefined); + wrapper.unmount(); + }); }); -const params = [["CAR", "car_icon_url"], - ["TRUCK", "truck_icon_url"], - ["VAN", "van_icon_url"], - ["COMMERCIAL VAN", "commercial_van_icon_url"], - ["SUV", "suv_icon_url"], - ["OTHER", "car_icon_url"]]; +const params = [ + ["CAR", "car_icon_url"], + ["TRUCK", "truck_icon_url"], + ["VAN", "van_icon_url"], + ["COMMERCIAL VAN", "commercial_van_icon_url"], + ["SUV", "suv_icon_url"], + ["OTHER", "car_icon_url"], +]; describe("vehicleBanner", () => { - test.each(params)("renders an icon instead of an image for %s and %s", async (category, expectedIcon) => { - // Arrange - const { wrapper, cmsContent } = setupMocks({ displayGenericVehicleImageProp: false, imageUrlValue: expectedIcon, categoryValue: category }); - //Act - var vehicleIcon = wrapper.vm.getUnmatchedVehicleIcon(); - // Assert - expect(store.getters.vehicle.category).toEqual(category); - expect(wrapper.find("img").attributes("class")).toContain("vehicle-image"); - wrapper.unmount(); - }); + test.each(params)( + "renders an icon instead of an image for %s and %s", + async (category, expectedIcon) => { + // Arrange + const { wrapper, cmsContent } = setupMocks({ + displayGenericVehicleImageProp: false, + imageUrlValue: expectedIcon, + categoryValue: category, + }); + //Act + var vehicleIcon = wrapper.vm.getUnmatchedVehicleIcon(); + // Assert + expect(store.getters.vehicle.category).toEqual(category); + expect(wrapper.find("img").attributes("class")).toContain("vehicle-image"); + wrapper.unmount(); + } + ); }); -function setupMocks({ - displayGenericVehicleImageProp, - imageUrlValue, - categoryValue = "CAR" -}) { - const mockGetCmsContent = jest.fn(); - mockGetCmsContent((cmsWidget, field) => { - return field - }); - const mockMixin = { - methods: { - getCmsContent: mockGetCmsContent - } - } - //Mock store - store.dispatch = jest.fn(() => {}); - store.getters = { vehicle: { category: categoryValue, imageUrl: imageUrlValue } }; - const mountOptions = getMountOptions({ - store: { - dispatch: store.dispatch, - getters: store.getters, - }, - }); +function setupMocks({ displayGenericVehicleImageProp, imageUrlValue, categoryValue = "CAR" }) { + const mockGetCmsContent = jest.fn(); + mockGetCmsContent((cmsWidget, field) => { + return field; + }); + const mockMixin = { + methods: { + getCmsContent: mockGetCmsContent, + }, + }; + //Mock store + store.dispatch = jest.fn(() => {}); + store.getters = { vehicle: { category: categoryValue, imageUrl: imageUrlValue } }; + const mountOptions = getMountOptions({ + store: { + dispatch: store.dispatch, + getters: store.getters, + }, + }); - //Mock props - mountOptions.propsData = { displayGenericVehicleImage: displayGenericVehicleImageProp }; - mountOptions.mixins = [mockMixin]; - const wrapper = shallowMount(vehicleBanner, mountOptions); + //Mock props + mountOptions.propsData = { displayGenericVehicleImage: displayGenericVehicleImageProp }; + mountOptions.mixins = [mockMixin]; + const wrapper = shallowMount(vehicleBanner, mountOptions); - //Mock CMS content - const cmsContent = { - GenericVehicleImage: "image_url", - CarUnmatchedVehicleIcon: "car_icon_url", - TruckUnmatchedVehicleIcon: "truck_icon_url", - VanUnmatchedVehicleIcon: "van_icon_url", - CommercialVanUnmatchedVehicleIcon: "commercial_van_icon_url", - SuvUnmatchedVehicleIcon: "suv_icon_url", - }; - return { wrapper, cmsContent }; + //Mock CMS content + const cmsContent = { + GenericVehicleImage: "image_url", + CarUnmatchedVehicleIcon: "car_icon_url", + TruckUnmatchedVehicleIcon: "truck_icon_url", + VanUnmatchedVehicleIcon: "van_icon_url", + CommercialVanUnmatchedVehicleIcon: "commercial_van_icon_url", + SuvUnmatchedVehicleIcon: "suv_icon_url", + }; + return { wrapper, cmsContent }; } diff --git a/src/common-components/vehicle-banner/vehicle-banner.vue b/src/common-components/vehicle-banner/vehicle-banner.vue index 3435efab9..8f7be0179 100644 --- a/src/common-components/vehicle-banner/vehicle-banner.vue +++ b/src/common-components/vehicle-banner/vehicle-banner.vue @@ -1,80 +1,79 @@ diff --git a/src/constants/analytics.js b/src/constants/analytics.js index 7ba9ef78c..eed7da2fe 100644 --- a/src/constants/analytics.js +++ b/src/constants/analytics.js @@ -1,36 +1,36 @@ const analyticsPageEvents = { - ENTRY: "ENTRY", - EVENT: "EVENT" + ENTRY: "ENTRY", + EVENT: "EVENT", }; // GA Constants const GaEvents = { - GENERIC_EVENT: 'event', - PAGE_VIEW_EVENT : 'logPageview' + GENERIC_EVENT: "event", + PAGE_VIEW_EVENT: "logPageview", }; const GaCategories = { - API_RESPONSE: 'Api_Response', - EVOX: 'Evox' + API_RESPONSE: "Api_Response", + EVOX: "Evox", }; const GaActions = { - RESULT: 'Result', - CLICKED: 'Clicked', - VIF: 'vif', - SUBMITTED: 'Submitted', + RESULT: "Result", + CLICKED: "Clicked", + VIF: "vif", + SUBMITTED: "Submitted", }; const GaLabels = { - SUCCESS: 'Success', - ERROR: 'Error', - LICENSE_PLATE_LOOKUP: 'License_Plate_Look_Up', - VIN_LOOKUP: 'Vin_Look_Up', - ADDRESS_LOOKUP: 'Address_Look_up', + SUCCESS: "Success", + ERROR: "Error", + LICENSE_PLATE_LOOKUP: "License_Plate_Look_Up", + VIN_LOOKUP: "Vin_Look_Up", + ADDRESS_LOOKUP: "Address_Look_up", }; const ValueToLogTypes = { - LAST_5: "last_5", + LAST_5: "last_5", }; export { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes }; diff --git a/src/constants/application-config.js b/src/constants/application-config.js index 0e185cff4..f4840a91a 100644 --- a/src/constants/application-config.js +++ b/src/constants/application-config.js @@ -1,14 +1,13 @@ - const applicationConfig = { - CONSUMER_CF_DISTRO: process.env.VUE_APP_CONSUMER_CF_DISTRO, - GOOGLE_PLACES_API_KEY: process.env.VUE_APP_GOOGLE_PLACES_API_KEY, - ANALYTICS_SESSION_TIMEOUT_MINUTES: 30, - SAVED_SESSION_TIMEOUT_DAYS: 45, - COOKIE_PATH: "/", - CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod" - APPLICATION_NAME: "FixMyGlass", - APPLICATION_ABBREVIATION: "fmg", - SITE_ENTRY_TRIGGER_VALUE: "FixMyGlass" + CONSUMER_CF_DISTRO: process.env.VUE_APP_CONSUMER_CF_DISTRO, + GOOGLE_PLACES_API_KEY: process.env.VUE_APP_GOOGLE_PLACES_API_KEY, + ANALYTICS_SESSION_TIMEOUT_MINUTES: 30, + SAVED_SESSION_TIMEOUT_DAYS: 45, + COOKIE_PATH: "/", + CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod" + APPLICATION_NAME: "FixMyGlass", + APPLICATION_ABBREVIATION: "fmg", + SITE_ENTRY_TRIGGER_VALUE: "FixMyGlass", }; -export { applicationConfig }; \ No newline at end of file +export { applicationConfig }; diff --git a/src/constants/cookie-names.js b/src/constants/cookie-names.js index f4b4aaa9d..5fab52899 100644 --- a/src/constants/cookie-names.js +++ b/src/constants/cookie-names.js @@ -1,13 +1,12 @@ -import { applicationConfig } from "@/constants/application-config.js" +import { applicationConfig } from "@/constants/application-config.js"; const cookieNames = { FUNNEL_SESSION_INFO: `FunnelSessionInfo-${applicationConfig.CURRENT_ENVIRONMENT}`, - + // Existing Safelite.com cookies DXDEV: "dxdev", SESSION_ID: "sid", - SESSION_KEY: "skey" + SESSION_KEY: "skey", }; - + export { cookieNames }; - \ No newline at end of file diff --git a/src/constants/damage-locations-cms.js b/src/constants/damage-locations-cms.js index c4787a598..82a431756 100644 --- a/src/constants/damage-locations-cms.js +++ b/src/constants/damage-locations-cms.js @@ -4,6 +4,6 @@ const damageLocationsCms = { REARWINDOW: "REARWINDOW", DRIVERSIDE: "DRIVERSIDE", PASSENGERSIDE: "PASSENGERSIDE", - }; - - export { damageLocationsCms }; \ No newline at end of file +}; + +export { damageLocationsCms }; diff --git a/src/constants/damage-locations-selected.js b/src/constants/damage-locations-selected.js index 905d9c8c5..7ede615cb 100644 --- a/src/constants/damage-locations-selected.js +++ b/src/constants/damage-locations-selected.js @@ -15,7 +15,7 @@ const damageLocationsSelected = { DRIVERSIDE: "DriverSide", PASSENGERSIDE: "PassengerSide", STATIONARY: "Stationary", - SLIDER: "Slider" - }; - - export { damageLocationsSelected }; \ No newline at end of file + SLIDER: "Slider", +}; + +export { damageLocationsSelected }; diff --git a/src/constants/dynamic-strings.js b/src/constants/dynamic-strings.js index 1eef7be46..b0ff5e099 100644 --- a/src/constants/dynamic-strings.js +++ b/src/constants/dynamic-strings.js @@ -3,6 +3,6 @@ const dynamicStrings = { CUSTOM: "custom", ROUTER_LINK: "routerLink:", TEXT_LINK: "textLink:", - }; - - export { dynamicStrings }; \ No newline at end of file +}; + +export { dynamicStrings }; diff --git a/src/constants/dynamictext-mapper.js b/src/constants/dynamictext-mapper.js index d47ef9d02..9191c8781 100644 --- a/src/constants/dynamictext-mapper.js +++ b/src/constants/dynamictext-mapper.js @@ -4,29 +4,28 @@ 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' }, - ] -} + { 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. +// 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)]; @@ -39,19 +38,19 @@ export function getCustomTransformValue(dynamicString, key) { // Get the array of possible values based on the key name. const transformArray = customMappings[arrayKey.toLowerCase()]; - - if(transformArray === undefined) { + + 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()); + const mapObject = transformArray.find((map) => map.key.toLowerCase() === key.toLowerCase()); - if(mapObject === undefined){ + if (mapObject === undefined) { return dynamicString; } const finalString = dynamicString.replace(`{custom:${arrayKey}}`, mapObject.transformedValue); - return finalString -} \ No newline at end of file + return finalString; +} diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 26be43031..0fade12fd 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -1,108 +1,111 @@ const endpoints = { - GetRouteInfo: { - url: (applicationAbbreviation) => `/content/api/v1/content/${applicationAbbreviation}/RouteInfo`, - method: "POST", - }, - GetHomepageInfo: { - url: (applicationAbbreviation) => `/content/api/v1/content/${applicationAbbreviation}/HomepageInfo`, - method: "GET", - }, - GetPageData: { - url: (applicationAbbreviation, pageName) => `/content/api/v1/content/${applicationAbbreviation}/${pageName}`, - method: "GET", - }, - GetVehicleYears: { - url: "/vehicle/api/v1/vehicle/years", - method: "GET", - }, - GetVehicleMakes: { - url: "/vehicle/api/v1/vehicle/Makes", - method: "GET", - }, - GetVehicleModels: { - url: "/vehicle/api/v1/vehicle/Models", - method: "GET", - }, - GetVehicleStyles: { - url: "/vehicle/api/v1/vehicle/Styles", - method: "GET", - }, - GetVehicle: { - url: "/vehicle/api/v1/vehicle/lookup", - method: "GET", - }, - GetDamageOptions: { - url: "/parts/api/v1/parts/damage-options", - method: "GET", - }, - LookupVehicleByYmms: { - url: "/vehicle/api/v1/vehicle/Lookup", - method: "GET", - }, - LookupVehicleByVin: { - url: "/vehicle/api/v1/vehicle/Lookup", - method: "POST", - }, - LookupVinByPlate: { - url: "/vehicle/api/v1/vehicle/lookup-vin-by-plate", - method: "POST", - }, - LookupVinByAddress: { - url: "/vehicle/api/v1/vehicle/lookup-vin-by-address", - method: "POST", - }, - GetPartsOrQuestions: { - url: "/parts/api/v1/parts/parts-or-questions", - method: "POST", - }, - GetParts: { - url: "/parts/api/v1/parts/parts", - method: "POST", - }, - GetCapabilityQuestions: { - url: "/parts/api/v1/parts/capability-questions", - method: "GET" - }, - GetPartFromCapabilityAnswer: { - url: "/parts/api/v1/parts/part-from-capability-answer", - method: "POST" - }, - SaveSession: { - url: "/order/api/v1/order/save-session", - method: "POST", - }, - LoadSession: { - url: "/order/api/v1/order/load-session", - method: "POST", - }, - ValidateZip: { - url: "/location/api/v1/location/zip", - method: "GET", - }, - LogExperimentExposureIfAssigned:{ - url: "/experiments/api/v1/experiments/log-exposure", - method: "POST", - }, - LogPageView:{ - url: "/analytics/api/v1/analytics/log-page-view", - method: "POST", - }, - LogCustomEvent:{ - url: "/analytics/api/v1/analytics/log-custom-event", - method: "POST", - }, - InitializeSession:{ - url: "/analytics/api/v1/analytics/initialize", - method: "POST", - }, - GetExperimentsByUser: { - url: "/analytics/api/v1/analytics/get-experiments", - method: "GET", - }, - RunExperimentsForTrigger: { - url: "/experiments/api/v1/experiments/run", - method: "POST" - } + GetRouteInfo: { + url: (applicationAbbreviation) => + `/content/api/v1/content/${applicationAbbreviation}/RouteInfo`, + method: "POST", + }, + GetHomepageInfo: { + url: (applicationAbbreviation) => + `/content/api/v1/content/${applicationAbbreviation}/HomepageInfo`, + method: "GET", + }, + GetPageData: { + url: (applicationAbbreviation, pageName) => + `/content/api/v1/content/${applicationAbbreviation}/${pageName}`, + method: "GET", + }, + GetVehicleYears: { + url: "/vehicle/api/v1/vehicle/years", + method: "GET", + }, + GetVehicleMakes: { + url: "/vehicle/api/v1/vehicle/Makes", + method: "GET", + }, + GetVehicleModels: { + url: "/vehicle/api/v1/vehicle/Models", + method: "GET", + }, + GetVehicleStyles: { + url: "/vehicle/api/v1/vehicle/Styles", + method: "GET", + }, + GetVehicle: { + url: "/vehicle/api/v1/vehicle/lookup", + method: "GET", + }, + GetDamageOptions: { + url: "/parts/api/v1/parts/damage-options", + method: "GET", + }, + LookupVehicleByYmms: { + url: "/vehicle/api/v1/vehicle/Lookup", + method: "GET", + }, + LookupVehicleByVin: { + url: "/vehicle/api/v1/vehicle/Lookup", + method: "POST", + }, + LookupVinByPlate: { + url: "/vehicle/api/v1/vehicle/lookup-vin-by-plate", + method: "POST", + }, + LookupVinByAddress: { + url: "/vehicle/api/v1/vehicle/lookup-vin-by-address", + method: "POST", + }, + GetPartsOrQuestions: { + url: "/parts/api/v1/parts/parts-or-questions", + method: "POST", + }, + GetParts: { + url: "/parts/api/v1/parts/parts", + method: "POST", + }, + GetCapabilityQuestions: { + url: "/parts/api/v1/parts/capability-questions", + method: "GET", + }, + GetPartFromCapabilityAnswer: { + url: "/parts/api/v1/parts/part-from-capability-answer", + method: "POST", + }, + SaveSession: { + url: "/order/api/v1/order/save-session", + method: "POST", + }, + LoadSession: { + url: "/order/api/v1/order/load-session", + method: "POST", + }, + ValidateZip: { + url: "/location/api/v1/location/zip", + method: "GET", + }, + LogExperimentExposureIfAssigned: { + url: "/experiments/api/v1/experiments/log-exposure", + method: "POST", + }, + LogPageView: { + url: "/analytics/api/v1/analytics/log-page-view", + method: "POST", + }, + LogCustomEvent: { + url: "/analytics/api/v1/analytics/log-custom-event", + method: "POST", + }, + InitializeSession: { + url: "/analytics/api/v1/analytics/initialize", + method: "POST", + }, + GetExperimentsByUser: { + url: "/analytics/api/v1/analytics/get-experiments", + method: "GET", + }, + RunExperimentsForTrigger: { + url: "/experiments/api/v1/experiments/run", + method: "POST", + }, }; export { endpoints }; diff --git a/src/constants/error-messages.js b/src/constants/error-messages.js index b4d073496..5631b44c7 100644 --- a/src/constants/error-messages.js +++ b/src/constants/error-messages.js @@ -21,7 +21,8 @@ const errorMessages = { SERVICE_ZIP_REQUIRED: "Please enter your service ZIP", SERVICE_ZIP_FORMAT: "Please enter a valid service ZIP", VIN_REQUIRED: "Please enter your VIN", - VIN_FORMAT: "Invalid VIN. Please make sure that you entered the correct 17-digit, alpha-numeric number. VINs do not contain the letters I, O, or Q", + VIN_FORMAT: + "Invalid VIN. Please make sure that you entered the correct 17-digit, alpha-numeric number. VINs do not contain the letters I, O, or Q", OPTION_REQUIRED: "Please select an option", VEHICLE_REQUIRED: "Please select a vehicle", }; diff --git a/src/constants/events.js b/src/constants/events.js index 066ef6dcd..f416eeff1 100644 --- a/src/constants/events.js +++ b/src/constants/events.js @@ -1,17 +1,17 @@ const globalEvents = { - Categories: { - GLOBAL_ALERT: "GLOBAL_ALERT", - }, - SubCategories: { - PAGE_NOT_FOUND: "PAGE_NOT_FOUND", - }, + Categories: { + GLOBAL_ALERT: "GLOBAL_ALERT", + }, + SubCategories: { + PAGE_NOT_FOUND: "PAGE_NOT_FOUND", + }, }; const globalEventTypes = { - Success: "alert-success", - Warning: "alert-warning", - Info: "alert-info", - Danger: "alert-danger", + Success: "alert-success", + Warning: "alert-warning", + Info: "alert-info", + Danger: "alert-danger", }; export { globalEvents, globalEventTypes }; diff --git a/src/constants/experiments.js b/src/constants/experiments.js index fdfa7acac..f2e5d4928 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -1,15 +1,14 @@ const experimentUniverses = { - CONCEPT_FUNNEL: 'ConceptFunnel' + CONCEPT_FUNNEL: "ConceptFunnel", }; const experimentSettings = { - GOOGLE_CUSTOM_DIMENSION_INDEX: 'Google Custom Dimension Index' -} + GOOGLE_CUSTOM_DIMENSION_INDEX: "Google Custom Dimension Index", +}; const experimentTriggers = { SITE_ENTRY: "SiteEntry", - PAGE_ENTRY: "PageEntry" + PAGE_ENTRY: "PageEntry", }; - + export { experimentUniverses, experimentSettings, experimentTriggers }; - \ No newline at end of file diff --git a/src/constants/header-keys.js b/src/constants/header-keys.js index 895058e48..2cfbba367 100644 --- a/src/constants/header-keys.js +++ b/src/constants/header-keys.js @@ -1,3 +1,3 @@ export const headerKeys = { - EXPERIMENT: "X-Experiment-Data" -} \ No newline at end of file + EXPERIMENT: "X-Experiment-Data", +}; diff --git a/src/constants/part-type-strings.js b/src/constants/part-type-strings.js index 6a2a490ff..2dfab2071 100644 --- a/src/constants/part-type-strings.js +++ b/src/constants/part-type-strings.js @@ -1,8 +1,8 @@ const partTypeStrings = { - FRONT_WIPER : "FRONT WIPER", - REAR_WIPER : "REAR WIPER", + FRONT_WIPER: "FRONT WIPER", + REAR_WIPER: "REAR WIPER", RAIN_DEFENSE: "RAIN DEFENSE", - RECALIBRATION : "RECALIBRATION", - }; + RECALIBRATION: "RECALIBRATION", +}; - export {partTypeStrings}; \ No newline at end of file +export { partTypeStrings }; diff --git a/src/constants/query-strings.js b/src/constants/query-strings.js index 6655f9378..f75e526d6 100644 --- a/src/constants/query-strings.js +++ b/src/constants/query-strings.js @@ -1,7 +1,6 @@ const queryStrings = { - FMG_PAGE: 'fmgPage', - START_TYPE: 'start_type' - }; - - export { queryStrings }; - \ No newline at end of file + FMG_PAGE: "fmgPage", + START_TYPE: "start_type", +}; + +export { queryStrings }; diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 8c498feda..78053b567 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -1,67 +1,66 @@ const storeActions = { - // Content Actions - GET_ROUTE_INFO_ACTION: "getRouteInfo", - GET_HOMEPAGE_NAME: "getHomepageName", - GET_PAGE_DATA: "getPageData", + // Content Actions + GET_ROUTE_INFO_ACTION: "getRouteInfo", + GET_HOMEPAGE_NAME: "getHomepageName", + GET_PAGE_DATA: "getPageData", - // Vehicle Actions - GET_VEHICLE_YEARS: "getVehicleYears", - GET_VEHICLE_MAKES: "getVehicleMakes", - GET_VEHICLE_MODELS: "getVehicleModels", - GET_VEHICLE_STYLES: "getVehicleStyles", - SET_VEHICLE: "setVehicle", - GET_DAMAGE_OPTIONS: "getDamageOptions", - GET_EVOX_IMAGE: "getEvoxImage", + // Vehicle Actions + GET_VEHICLE_YEARS: "getVehicleYears", + GET_VEHICLE_MAKES: "getVehicleMakes", + GET_VEHICLE_MODELS: "getVehicleModels", + GET_VEHICLE_STYLES: "getVehicleStyles", + SET_VEHICLE: "setVehicle", + GET_DAMAGE_OPTIONS: "getDamageOptions", + GET_EVOX_IMAGE: "getEvoxImage", - // Lookup Actions - LOOKUP_VEHICLE_BY_YMMS: "lookupVehicleByYmms", - LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin", - LOOKUP_VIN_BY_PLATE: "lookupVinByPlate", - LOOKUP_VIN_BY_ADDRESS: "lookupVinByAddress", - GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions", - GET_PARTS: "getParts", - GET_CAPABILITY_QUESTIONS: "getCapabilityQuestions", - GET_PART_FROM_CAPABILITY_QUESTION_ANSWER: - "getPartFromCapabilityQuestionAnswer", - GET_MOLDING_QUESTIONS: "getMoldingQuestions", - SAVE_SESSION: "saveSession", - LOAD_SESSION: "loadSession", - UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE: "updateStoreWithSaveSessionResponse", - VALIDATE_ZIP: "validateZip", - LOG_EXPERIMENT_EXPOSURE: "logExperimentExposure", - LOG_PAGE_VIEW: "logPageView", - LOG_CUSTOM_EVENT: "logCustomEvent", - INITIALIZE_SESSION: "initializeSession", - GET_EXPERIMENTS_BY_USER: "GetExperimentsByUser", - RUN_EXPERIMENTS_FOR_TRIGGER: "runExperimentsForTrigger", - CLEAR_VIN: "clearVin", - RESET_SAVE_SESSION_PROMISE: "resetSaveSessionPromise", + // Lookup Actions + LOOKUP_VEHICLE_BY_YMMS: "lookupVehicleByYmms", + LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin", + LOOKUP_VIN_BY_PLATE: "lookupVinByPlate", + LOOKUP_VIN_BY_ADDRESS: "lookupVinByAddress", + GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions", + GET_PARTS: "getParts", + GET_CAPABILITY_QUESTIONS: "getCapabilityQuestions", + GET_PART_FROM_CAPABILITY_QUESTION_ANSWER: "getPartFromCapabilityQuestionAnswer", + GET_MOLDING_QUESTIONS: "getMoldingQuestions", + SAVE_SESSION: "saveSession", + LOAD_SESSION: "loadSession", + UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE: "updateStoreWithSaveSessionResponse", + VALIDATE_ZIP: "validateZip", + LOG_EXPERIMENT_EXPOSURE: "logExperimentExposure", + LOG_PAGE_VIEW: "logPageView", + LOG_CUSTOM_EVENT: "logCustomEvent", + INITIALIZE_SESSION: "initializeSession", + GET_EXPERIMENTS_BY_USER: "GetExperimentsByUser", + RUN_EXPERIMENTS_FOR_TRIGGER: "runExperimentsForTrigger", + CLEAR_VIN: "clearVin", + RESET_SAVE_SESSION_PROMISE: "resetSaveSessionPromise", - // DEPENDENCY MUTATIONS - RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies", - RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies", - RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies", - RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies", - RESET_STATE: "resetState", + // DEPENDENCY MUTATIONS + RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies", + RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies", + RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies", + RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies", + RESET_STATE: "resetState", - // SAVE COMPONENT STATE - SAVE_VEHICLE_YEAR: "saveVehicleYear", - SAVE_VEHICLE_MAKE: "saveVehicleMake", - SAVE_VEHICLE_MODEL: "saveVehicleModel", - SAVE_VEHICLE_STYLE: "saveVehicleStyle", - SAVE_VEHICLE_DAMAGE: "saveVehicleDamage", - SAVE_VIN_LOOKUP: "saveVinLookup", - SAVE_SERVICE_LOCATION: "saveServiceLocation", - SAVE_EMAIL: "saveEmail", - SAVE_REGISTRATION_LICENSE_PLATE_LOOKUP: "saveRegistrationLicensePlateLookup", - SAVE_VIN: "saveVin", - SAVE_REGISTRATION_ADDRESS_LOOKUP: "saveRegistrationAddressLookup", - SAVE_GLASS_PARTS: "saveGlassParts", - SAVE_PART_QUESTION_ANSWERS: "savePartQuestionAnswers", - RESET_MOLDING_AND_CAPABILITY_QUESTIONS_IF_NEEDED: - "resetMoldingAndCapabilityQuestionAnswersIfNeeded", - SAVE_MOLDING_QUESTION_ANSWERS: "saveMoldingQuestionAnswers", - SAVE_CAPABILITY_QUESTION_ANSWERS: "saveCapabilityQuestionAnswers", + // SAVE COMPONENT STATE + SAVE_VEHICLE_YEAR: "saveVehicleYear", + SAVE_VEHICLE_MAKE: "saveVehicleMake", + SAVE_VEHICLE_MODEL: "saveVehicleModel", + SAVE_VEHICLE_STYLE: "saveVehicleStyle", + SAVE_VEHICLE_DAMAGE: "saveVehicleDamage", + SAVE_VIN_LOOKUP: "saveVinLookup", + SAVE_SERVICE_LOCATION: "saveServiceLocation", + SAVE_EMAIL: "saveEmail", + SAVE_REGISTRATION_LICENSE_PLATE_LOOKUP: "saveRegistrationLicensePlateLookup", + SAVE_VIN: "saveVin", + SAVE_REGISTRATION_ADDRESS_LOOKUP: "saveRegistrationAddressLookup", + SAVE_GLASS_PARTS: "saveGlassParts", + SAVE_PART_QUESTION_ANSWERS: "savePartQuestionAnswers", + RESET_MOLDING_AND_CAPABILITY_QUESTIONS_IF_NEEDED: + "resetMoldingAndCapabilityQuestionAnswersIfNeeded", + SAVE_MOLDING_QUESTION_ANSWERS: "saveMoldingQuestionAnswers", + SAVE_CAPABILITY_QUESTION_ANSWERS: "saveCapabilityQuestionAnswers", }; export { storeActions }; diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index d3de68853..dc165eecf 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -1,71 +1,71 @@ const storeMutations = { - // VEHICLE MUTATIONS - UPDATE_YEAR: "updateYear", - UPDATE_MAKE: "updateMake", - UPDATE_MODEL: "updateModel", - UPDATE_STYLE: "updateStyle", - UPDATE_CAR_ID: "updateCarId", - UPDATE_VEHICLE_CATEGORY: "updateVehicleCategory", - UPDATE_VEHICLE_IMAGE_URL: "updateVehicleImageUrl", - UPDATE_VEHICLE_IMAGE_VIF_NUMBER: "updateVehicleImageVifNumber", - UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor", - UPDATE_VEHICLE_VIN: "updateVehicleVin", - UPDATE_VEHICLE: "updateVehicle", + // VEHICLE MUTATIONS + UPDATE_YEAR: "updateYear", + UPDATE_MAKE: "updateMake", + UPDATE_MODEL: "updateModel", + UPDATE_STYLE: "updateStyle", + UPDATE_CAR_ID: "updateCarId", + UPDATE_VEHICLE_CATEGORY: "updateVehicleCategory", + UPDATE_VEHICLE_IMAGE_URL: "updateVehicleImageUrl", + UPDATE_VEHICLE_IMAGE_VIF_NUMBER: "updateVehicleImageVifNumber", + UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor", + UPDATE_VEHICLE_VIN: "updateVehicleVin", + UPDATE_VEHICLE: "updateVehicle", - UPDATE_IS_REPAIR: "updateIsRepair", - UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips", - UPDATE_GLASS_TO_REPLACE: "updateGlassToReplace", - UPDATE_PART_QUESTION_ANSWERS: "updatePartQuestionAnswers", - UPDATE_MOLDING_QUESTION_ANSWERS: "updateMoldingQuestionAnswers", - UPDATE_CAPABILITY_QUESTION_ANSWERS: "updateCapabilityQuestionAnswers", - UPDATE_GLASS_PARTS: "updateGlassParts", - UPDATE_OTHER_PARTS: "updateOtherParts", + UPDATE_IS_REPAIR: "updateIsRepair", + UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips", + UPDATE_GLASS_TO_REPLACE: "updateGlassToReplace", + UPDATE_PART_QUESTION_ANSWERS: "updatePartQuestionAnswers", + UPDATE_MOLDING_QUESTION_ANSWERS: "updateMoldingQuestionAnswers", + UPDATE_CAPABILITY_QUESTION_ANSWERS: "updateCapabilityQuestionAnswers", + UPDATE_GLASS_PARTS: "updateGlassParts", + UPDATE_OTHER_PARTS: "updateOtherParts", - UPDATE_REGISTRATION_LICENSE_PLATE: "updateRegistrationLicensePlate", - UPDATE_REGISTRATION_ADDRESS: "updateRegistrationAddress", - UPDATE_REGISTRATION_CITY: "updateRegistrationCity", - UPDATE_REGISTRATION_STATE: "updateRegistrationState", - UPDATE_REGISTRATION_ZIP_CODE: "updateRegistrationZipCode", - UPDATE_REGISTRATION_FIRST_NAME: "updateRegistrationFirstName", - UPDATE_REGISTRATION_LAST_NAME: "updateRegistrationLastName", - UPDATE_REGISTRATION: "updateRegistration", + UPDATE_REGISTRATION_LICENSE_PLATE: "updateRegistrationLicensePlate", + UPDATE_REGISTRATION_ADDRESS: "updateRegistrationAddress", + UPDATE_REGISTRATION_CITY: "updateRegistrationCity", + UPDATE_REGISTRATION_STATE: "updateRegistrationState", + UPDATE_REGISTRATION_ZIP_CODE: "updateRegistrationZipCode", + UPDATE_REGISTRATION_FIRST_NAME: "updateRegistrationFirstName", + UPDATE_REGISTRATION_LAST_NAME: "updateRegistrationLastName", + UPDATE_REGISTRATION: "updateRegistration", - UPDATE_SERVICE_LOCATION_ZIP_CODE: "updateServiceLocationZipCode", - UPDATE_SERVICE_LOCATION_STATE: "updateServiceLocationState", - UPDATE_SERVICE_LOCATION: "updateServiceLocation", + UPDATE_SERVICE_LOCATION_ZIP_CODE: "updateServiceLocationZipCode", + UPDATE_SERVICE_LOCATION_STATE: "updateServiceLocationState", + UPDATE_SERVICE_LOCATION: "updateServiceLocation", - UPDATE_CUSTOMER_EMAIL_ADDRESS: "updateCustomerEmailAddress", + UPDATE_CUSTOMER_EMAIL_ADDRESS: "updateCustomerEmailAddress", - // ORDER MUTATIONS - UPDATE_REFERRAL_NUMBER: "updateReferralNumber", - UPDATE_REFERRAL_DATE: "updateReferralDate", - UPDATE_REFERRAL_CORRELATION_ID: "updateReferralCorrelationId", - UPDATE_PARENT_ACCT_NUMBER: "updateParentAcctNumber", - UPDATE_EON: "updateEON", - UPDATE_SAVED_SESSION_ID: "updateSavedSessionId", - UPDATE_CRM_CUSTOMER_ID: "updateCrmCustomerId", + // ORDER MUTATIONS + UPDATE_REFERRAL_NUMBER: "updateReferralNumber", + UPDATE_REFERRAL_DATE: "updateReferralDate", + UPDATE_REFERRAL_CORRELATION_ID: "updateReferralCorrelationId", + UPDATE_PARENT_ACCT_NUMBER: "updateParentAcctNumber", + UPDATE_EON: "updateEON", + UPDATE_SAVED_SESSION_ID: "updateSavedSessionId", + UPDATE_CRM_CUSTOMER_ID: "updateCrmCustomerId", - // EVENT BUS MUTATIONS - ADD_EVENT_TO_BUS: "addEventToBus", - REMOVE_EVENT_FROM_BUS: "removeEventFromBus", + // EVENT BUS MUTATIONS + ADD_EVENT_TO_BUS: "addEventToBus", + REMOVE_EVENT_FROM_BUS: "removeEventFromBus", - // DEPENDENCY MUTATIONS - RESET_VEHICLE_STATE: "resetVehicleState", - RESET_DAMAGE_STATE: "resetDamageState", - RESET_REGISTRATION_STATE: "resetRegistrationState", - RESET_GLASS_PARTS_STATE: "resetGlassPartsState", - RESET_STATE: "resetState", - RESET_SAVE_SESSION_PROMISE: "resetSaveSessionPromise", + // DEPENDENCY MUTATIONS + RESET_VEHICLE_STATE: "resetVehicleState", + RESET_DAMAGE_STATE: "resetDamageState", + RESET_REGISTRATION_STATE: "resetRegistrationState", + RESET_GLASS_PARTS_STATE: "resetGlassPartsState", + RESET_STATE: "resetState", + RESET_SAVE_SESSION_PROMISE: "resetSaveSessionPromise", - // OTHER MUTATIONS - UPDATE_PAGE_DATA: "updatePageData", - UPDATE_STATE_WITH_ORDER_INFORMATION: "updateStateWithOrderInformation", - UPDATE_SAVE_SESSION_PROMISE: "updateSaveSessionPromise", - UPDATE_LAST_PAGE_VISITED: "updateLastPageVisited", + // OTHER MUTATIONS + UPDATE_PAGE_DATA: "updatePageData", + UPDATE_STATE_WITH_ORDER_INFORMATION: "updateStateWithOrderInformation", + UPDATE_SAVE_SESSION_PROMISE: "updateSaveSessionPromise", + UPDATE_LAST_PAGE_VISITED: "updateLastPageVisited", - // EXPERIMENT MUTATIONS - UPDATE_EXPERIMENTS: "updateExperiments", - UPDATE_TRIGGERED_SITE_ENTRY: "updateTriggeredSiteEntry", + // EXPERIMENT MUTATIONS + UPDATE_EXPERIMENTS: "updateExperiments", + UPDATE_TRIGGERED_SITE_ENTRY: "updateTriggeredSiteEntry", }; -export { storeMutations }; \ No newline at end of file +export { storeMutations }; diff --git a/src/constants/tint-mapper.js b/src/constants/tint-mapper.js index 32dae95fc..308b9a92d 100644 --- a/src/constants/tint-mapper.js +++ b/src/constants/tint-mapper.js @@ -1,7 +1,6 @@ - // 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. +// See vehicle-parts for implementation example. const tintMap = { other: [ // Blue Shade @@ -35,7 +34,7 @@ const tintMap = { { name: "gray tint privacy", src: "Glass-NoShade-GrayTint.svg" }, // No shade or tint - { name: "clear", src: "Glass-NoShade-NoTint.svg" } + { name: "clear", src: "Glass-NoShade-NoTint.svg" }, ], windshield: [ @@ -71,25 +70,25 @@ const tintMap = { // No shade or tint { name: "clear", 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 (glassLocation.toLowerCase() !== "windshield") { + glassLocation = "other"; } if (tintMap[glassLocation.toLowerCase()] === undefined) { - return ''; + return ""; } - const tintImageSource = tintMap[glassLocation.toLowerCase()].find(item => item.name.toLowerCase() == colorString.toLowerCase()) + const tintImageSource = tintMap[glassLocation.toLowerCase()].find( + (item) => item.name.toLowerCase() == colorString.toLowerCase() + ); return tintImageSource; -} \ No newline at end of file +} diff --git a/src/constants/vehicle-categories.js b/src/constants/vehicle-categories.js index 4fab279d8..37ffcf5e1 100644 --- a/src/constants/vehicle-categories.js +++ b/src/constants/vehicle-categories.js @@ -6,6 +6,6 @@ const vehicleCategories = { SUV: "SUV", MOTORHOME: "MOTOR HOME", SEMI: "SEMI", - }; - - export { vehicleCategories }; \ No newline at end of file +}; + +export { vehicleCategories }; diff --git a/src/constants/vin-lookup-method-selections.js b/src/constants/vin-lookup-method-selections.js index 46f1e2044..8f78ecf68 100644 --- a/src/constants/vin-lookup-method-selections.js +++ b/src/constants/vin-lookup-method-selections.js @@ -1,7 +1,7 @@ const vinLookupMethodSelections = { - MANUALVIN: "ManualVin", - LICENSEPLATE: "LicensePlate", - HOMEADDRESS: "HomeAddress", -}; + MANUALVIN: "ManualVin", + LICENSEPLATE: "LicensePlate", + HOMEADDRESS: "HomeAddress", +}; -export { vinLookupMethodSelections }; \ No newline at end of file +export { vinLookupMethodSelections }; diff --git a/src/global-methods.js b/src/global-methods.js index ffda1e77f..14e5cca0e 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -7,53 +7,69 @@ import { GaCategories, GaActions, GaLabels } from "@/constants/analytics"; import { headerKeys } from "@/constants/header-keys"; export default { - callHttpClient({ method, endpoint, payload, logApiCall = true }) { - return new Promise((resolve, reject) => { - const cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO; - const payloadAndAnalyticsData = Object.assign({}, payload, { AppName: "FixMyGlass" }); - const headers = { - [headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings) - } + callHttpClient({ method, endpoint, payload, logApiCall = true }) { + return new Promise((resolve, reject) => { + const cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO; + const payloadAndAnalyticsData = Object.assign({}, payload, { AppName: "FixMyGlass" }); + const headers = { + [headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings), + }; - axios({ method: method, url: cfDistroUrl + endpoint, data: payloadAndAnalyticsData, crossDomain: true, responseType: {}, headers: headers }) - .then((response) => { + axios({ + method: method, + url: cfDistroUrl + endpoint, + data: payloadAndAnalyticsData, + crossDomain: true, + responseType: {}, + headers: headers, + }).then( + (response) => { + if (logApiCall) { + analyticsMixIn.methods.pushEventToGA( + GaCategories.API_RESPONSE, + GaActions.RESULT, + `${GaLabels.SUCCESS}_${endpoint}`, + true + ); + } - if (logApiCall) { - analyticsMixIn.methods.pushEventToGA(GaCategories.API_RESPONSE, GaActions.RESULT, `${GaLabels.SUCCESS}_${endpoint}`, true); - } + return resolve(response); + }, + (error) => { + console.error(error); - return resolve(response); - }, - error => { - console.error(error); + if (logApiCall) { + analyticsMixIn.methods.pushEventToGA( + GaCategories.API_RESPONSE, + GaActions.RESULT, + `${GaLabels.ERROR}_${endpoint}`, + true + ); + } - if (logApiCall) { - analyticsMixIn.methods.pushEventToGA(GaCategories.API_RESPONSE, GaActions.RESULT, `${GaLabels.ERROR}_${endpoint}`, true); - } + return reject(error.response); + } + ); + }); + }, - return reject(error.response); - } - ); - }); - }, - - /* istanbul ignore next */ - callMockHttpClient({ method, endpoint }) { - // For Mock use only! - return new Promise((resolve, reject) => { - axios({ - method: method, - url: endpoint, - crossDomain: true, - responseType: {}, - }).then( - (response) => { - resolve(response); - }, - (error) => { - return reject(error.response); - } - ); - }); - }, + /* istanbul ignore next */ + callMockHttpClient({ method, endpoint }) { + // For Mock use only! + return new Promise((resolve, reject) => { + axios({ + method: method, + url: endpoint, + crossDomain: true, + responseType: {}, + }).then( + (response) => { + resolve(response); + }, + (error) => { + return reject(error.response); + } + ); + }); + }, }; diff --git a/src/global-methods.spec.js b/src/global-methods.spec.js index 5dcba0ccd..b74b97a8a 100644 --- a/src/global-methods.spec.js +++ b/src/global-methods.spec.js @@ -7,75 +7,71 @@ jest.mock("axios"); jest.mock("@/mixins/analytics-mixin"); it("Global Methods - Call Http Client - Should Resolve Promise", () => { - //Arrange - const endpoint = "https://mock.safelite.com"; - const httpArgs = setupMocksForHttpClient({ endpoint: endpoint }); - analyticsMixIn.methods.pushEventToGA = jest.fn(); + //Arrange + const endpoint = "https://mock.safelite.com"; + const httpArgs = setupMocksForHttpClient({ endpoint: endpoint }); + analyticsMixIn.methods.pushEventToGA = jest.fn(); - //Act - globalMethods.callHttpClient(httpArgs).then((response) => { - //Assert - expect(axios.mock.calls[0][0].url).toContain(endpoint); - expect(response.data.message).toContain("Success"); - expect(response.status).toEqual(200); - }); + //Act + globalMethods.callHttpClient(httpArgs).then((response) => { + //Assert + expect(axios.mock.calls[0][0].url).toContain(endpoint); + expect(response.data.message).toContain("Success"); + expect(response.status).toEqual(200); + }); }); it("Global Methods - Call Http Client - Should Reject Promise", () => { - //Arrange - const endpoint = "https://mock.safelite.com"; - const httpArgs = setupMocksForHttpClient({ - endpoint: endpoint, - isError: true, - }); - analyticsMixIn.methods.pushEventToGA = jest.fn(); + //Arrange + const endpoint = "https://mock.safelite.com"; + const httpArgs = setupMocksForHttpClient({ + endpoint: endpoint, + isError: true, + }); + analyticsMixIn.methods.pushEventToGA = jest.fn(); - //Act - globalMethods.callHttpClient(httpArgs).catch((err) => { - //Assert - expect(axios.mock.calls[0][0].url).toContain(endpoint); - expect(err.data.message).toContain("Error"); - expect(err.status).toEqual(500); - }); + //Act + globalMethods.callHttpClient(httpArgs).catch((err) => { + //Assert + expect(axios.mock.calls[0][0].url).toContain(endpoint); + expect(err.data.message).toContain("Error"); + expect(err.status).toEqual(500); + }); }); -function setupMocksForHttpClient({ - endpoint = null, - isError = false, - additionalData = null, -}) { - //Clear node module - axios.mockClear(); +function setupMocksForHttpClient({ endpoint = null, isError = false, additionalData = null }) { + //Clear node module + axios.mockClear(); - // Success Response - const response = { - status: 200, - data: { - message: "Success", - additionalData: additionalData, - }, - }; + // Success Response + const response = { + status: 200, + data: { + message: "Success", + additionalData: additionalData, + }, + }; - // Error Response - const error = { - response: { - status: 500, - data: { - message: "Error", - additionalData: additionalData, - }, - }, - }; + // Error Response + const error = { + response: { + status: 500, + data: { + message: "Error", + additionalData: additionalData, + }, + }, + }; - // Error interceptor on Axios returns a different object, so we need to mimic that. - if (isError) { - axios.mockRejectedValue(error); - } else { - axios.mockResolvedValue(response); - } + // Error interceptor on Axios returns a different object, so we need to mimic that. + if (isError) { + axios.mockRejectedValue(error); + } else { + axios.mockResolvedValue(response); + } - return { - endpoint: endpoint, - logApiCall: true - }; + return { + endpoint: endpoint, + logApiCall: true, + }; } diff --git a/src/helpers/button-question-focus-helper.js b/src/helpers/button-question-focus-helper.js index f2aac0ba5..52e12aa8c 100644 --- a/src/helpers/button-question-focus-helper.js +++ b/src/helpers/button-question-focus-helper.js @@ -34,8 +34,4 @@ const invokeButtonQuestionLostFocusCallback = () => { } }; -export { - handleAnyComponentFocus, - handleButtonComponentFocus, - handleInputComponentBlur, -}; +export { handleAnyComponentFocus, handleButtonComponentFocus, handleInputComponentBlur }; diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index 953d025a4..6ff18a53e 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -3,129 +3,117 @@ import store from "@/store"; import { dynamicStrings } from "../constants/dynamic-strings"; export function fetchCmsContentForPage(fmgPage) { - return store - .dispatch(storeActions.GET_PAGE_DATA, { pageName: fmgPage }) - .then((response) => { - const pageDataFromCms = {}; + return store.dispatch(storeActions.GET_PAGE_DATA, { pageName: fmgPage }).then((response) => { + const pageDataFromCms = {}; - response.data.Result.forEach((widget) => { - let widgetWithReplacements = findAndReplaceGlobalStateValues( - widget.Model, - widget.Name - ); + response.data.Result.forEach((widget) => { + let widgetWithReplacements = findAndReplaceGlobalStateValues(widget.Model, widget.Name); - // If we already have this widget, push it on the collection - if (widgetWithReplacements.Name in pageDataFromCms) { - pageDataFromCms[widgetWithReplacements.Name].push( - widgetWithReplacements.Model - ); - return; - } + // If we already have this widget, push it on the collection + if (widgetWithReplacements.Name in pageDataFromCms) { + pageDataFromCms[widgetWithReplacements.Name].push(widgetWithReplacements.Model); + return; + } - pageDataFromCms[widgetWithReplacements.Name] = [ - widgetWithReplacements.Model, - ]; - }); + pageDataFromCms[widgetWithReplacements.Name] = [widgetWithReplacements.Model]; + }); - Object.keys(pageDataFromCms).forEach((key) => { - if (pageDataFromCms[key].length === 1) { - pageDataFromCms[key] = pageDataFromCms[key][0]; - } - }); + Object.keys(pageDataFromCms).forEach((key) => { + if (pageDataFromCms[key].length === 1) { + pageDataFromCms[key] = pageDataFromCms[key][0]; + } + }); - return pageDataFromCms; + return pageDataFromCms; }); } // Function to convert a string, into a matching global state item. function mapStringToState(str) { - // Pull all matches out of the string. - const regexExp = new RegExp("{([^{}]*?):([^{}]*?)}", "g"); - const regexMatches = [...str.matchAll(regexExp)]; - const globalStateMatches = regexMatches.filter(match => { - return match[1] === dynamicStrings.GLOBAL_STATE; - }) + // Pull all matches out of the string. + const regexExp = new RegExp("{([^{}]*?):([^{}]*?)}", "g"); + const regexMatches = [...str.matchAll(regexExp)]; + const globalStateMatches = regexMatches.filter((match) => { + return match[1] === dynamicStrings.GLOBAL_STATE; + }); + // Our final string value that will be built from the matches. + let stringBuilder = ""; + for (const match of globalStateMatches) { + const valueFromStore = getStoreValueFromString(match[2]); + if (!valueFromStore) { + return ""; // if we can't map our string to state data, return an empty string. + } - // Our final string value that will be built from the matches. - let stringBuilder = ""; - for (const match of globalStateMatches) { - const valueFromStore = getStoreValueFromString(match[2]); - if (!valueFromStore) { - return ""; // if we can't map our string to state data, return an empty string. + const stringWithReplacement = str.replace(match[0], valueFromStore); + + // If we still have values we need to substitute, call this function again. + if (stringWithReplacement.includes(dynamicStrings.GLOBAL_STATE)) { + return mapStringToState(stringWithReplacement); + } + + // Concatenate the string. + stringBuilder = `${stringBuilder} ${stringWithReplacement}`; } - - const stringWithReplacement = str.replace(match[0], valueFromStore); - - // If we still have values we need to substitute, call this function again. - if (stringWithReplacement.includes(dynamicStrings.GLOBAL_STATE)) { - return mapStringToState(stringWithReplacement); - } - - // Concatenate the string. - stringBuilder = `${stringBuilder} ${stringWithReplacement}`; - } - return stringBuilder.trimStart(); + return stringBuilder.trimStart(); } // Parent function for processWidgetItemForReplacement. This will loop through the parent // object and pass any objects that need additional processing to the processWidgetItemForReplacement function. function findAndReplaceGlobalStateValues(widgetModel, widgetName) { - const objWithReplacements = { - Name: widgetName, - Model: {}, - }; + const objWithReplacements = { + Name: widgetName, + Model: {}, + }; - Object.keys(widgetModel).forEach((key) => { - let modelWithReplacements = processWidgetItemForReplacement( - widgetModel, - key - ); + Object.keys(widgetModel).forEach((key) => { + let modelWithReplacements = processWidgetItemForReplacement(widgetModel, key); - objWithReplacements.Model[key] = modelWithReplacements; - }); + objWithReplacements.Model[key] = modelWithReplacements; + }); - return objWithReplacements; + return objWithReplacements; } // This function will process the widget item and replace any global state variables with their values. // This is a recursive function, it will call itself until it runs out of items to iterate on given the object. function processWidgetItemForReplacement(widgetModel, key) { - // If we have a string, and it needs to be replaced. - if (typeof widgetModel[key] === "string") { - widgetModel[key] = processIfStatements(widgetModel[key], dynamicStrings.GLOBAL_STATE, getStoreValueFromString); - if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) { - widgetModel[key] = mapStringToState(widgetModel[key]); + // If we have a string, and it needs to be replaced. + if (typeof widgetModel[key] === "string") { + widgetModel[key] = processIfStatements( + widgetModel[key], + dynamicStrings.GLOBAL_STATE, + getStoreValueFromString + ); + if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) { + widgetModel[key] = mapStringToState(widgetModel[key]); + } + return widgetModel[key]; } + + // If we have an object. array, etc + if (typeof widgetModel[key] === "object" && Object.keys(widgetModel[key]).length) { + Object.keys(widgetModel[key]).forEach((item) => { + processWidgetItemForReplacement(widgetModel[key], item); + }); + + return widgetModel[key]; + } + + // If we have something else like a number, boolean, etc. just return it return widgetModel[key]; - } - - // If we have an object. array, etc - if ( - typeof widgetModel[key] === "object" && - Object.keys(widgetModel[key]).length - ) { - Object.keys(widgetModel[key]).forEach((item) => { - processWidgetItemForReplacement(widgetModel[key], item); - }); - - return widgetModel[key]; - } - - // If we have something else like a number, boolean, etc. just return it - return widgetModel[key]; } function getStoreValueFromString(str) { - let storeOrStateObject = str.includes('getters') ? store :store.state; - for (const s of str.split(".")) { - if (storeOrStateObject[s] != undefined) { - storeOrStateObject = storeOrStateObject[s]; - } else { - return ""; // if we can't map our string to state data, return an empty string. + let storeOrStateObject = str.includes("getters") ? store : store.state; + for (const s of str.split(".")) { + if (storeOrStateObject[s] != undefined) { + storeOrStateObject = storeOrStateObject[s]; + } else { + return ""; // if we can't map our string to state data, return an empty string. + } } - } - return storeOrStateObject; + return storeOrStateObject; } /////////////////////////////////// @@ -140,128 +128,155 @@ function getStoreValueFromString(str) { * @returns The processed string */ export function processIfStatements(str, ifConditionKeyword, replacePlaceholderCallback) { - const containsRelevantIfStatement = new RegExp("{if:" + ifConditionKeyword +":.+?}", "g").test(str); - if(!containsRelevantIfStatement) { - return str; - } else { - const ifStatementRegexExpression = getIfStatementRegexExpression(); - const ifStatementRegexMatches = [...str.matchAll(ifStatementRegexExpression)]; - const completeIfStatementArray = getAndFlagFirstNonNestedIfStatementWithKeyword(ifStatementRegexMatches, ifConditionKeyword); - executeIfStatementAndSetProcessedStrings(completeIfStatementArray, replacePlaceholderCallback); - const reconstructedPostProcessedString = joinProcessedRegexArray(ifStatementRegexMatches); - return processIfStatements(reconstructedPostProcessedString, ifConditionKeyword, replacePlaceholderCallback); - } + const containsRelevantIfStatement = new RegExp("{if:" + ifConditionKeyword + ":.+?}", "g").test( + str + ); + if (!containsRelevantIfStatement) { + return str; + } else { + const ifStatementRegexExpression = getIfStatementRegexExpression(); + const ifStatementRegexMatches = [...str.matchAll(ifStatementRegexExpression)]; + const completeIfStatementArray = getAndFlagFirstNonNestedIfStatementWithKeyword( + ifStatementRegexMatches, + ifConditionKeyword + ); + executeIfStatementAndSetProcessedStrings( + completeIfStatementArray, + replacePlaceholderCallback + ); + const reconstructedPostProcessedString = joinProcessedRegexArray(ifStatementRegexMatches); + return processIfStatements( + reconstructedPostProcessedString, + ifConditionKeyword, + replacePlaceholderCallback + ); + } } function getAndFlagFirstNonNestedIfStatementWithKeyword(matches, ifConditionKeyword) { - let index = 0; - for(const match of matches) { - if (match.groups.isIfStatement && match.groups.ifConditionType === ifConditionKeyword) { - let interiorIndex = 0; - let nestedLevel = 0; - let elseStatementIndex = null; - for (const interiorMatch of matches.slice(index+1)) { - if (interiorMatch.groups.isIfStatement) { - if(interiorMatch.groups.ifConditionType === ifConditionKeyword) { - break; - } else { - nestedLevel++; - } - } else if(interiorMatch.groups.isElseStatement) { - if (!nestedLevel) { - elseStatementIndex = interiorIndex+1; - } - } else if (interiorMatch.groups.isEndStatement) { - if (nestedLevel) { - nestedLevel--; - } else { - const ifStatementArray = matches.slice(index, index + interiorIndex+2); - flagMatchesForProcessing(ifStatementArray, elseStatementIndex); - return ifStatementArray; - } + let index = 0; + for (const match of matches) { + if (match.groups.isIfStatement && match.groups.ifConditionType === ifConditionKeyword) { + let interiorIndex = 0; + let nestedLevel = 0; + let elseStatementIndex = null; + for (const interiorMatch of matches.slice(index + 1)) { + if (interiorMatch.groups.isIfStatement) { + if (interiorMatch.groups.ifConditionType === ifConditionKeyword) { + break; + } else { + nestedLevel++; + } + } else if (interiorMatch.groups.isElseStatement) { + if (!nestedLevel) { + elseStatementIndex = interiorIndex + 1; + } + } else if (interiorMatch.groups.isEndStatement) { + if (nestedLevel) { + nestedLevel--; + } else { + const ifStatementArray = matches.slice(index, index + interiorIndex + 2); + flagMatchesForProcessing(ifStatementArray, elseStatementIndex); + return ifStatementArray; + } + } + interiorIndex++; + } } - interiorIndex++; - } + index++; } - index++; - } } - - function flagMatchesForProcessing(matches, elseStatementIndex) { - matches[0].isFlaggedForProcessing = true; - matches[matches.length-1].isFlaggedForProcessing = true; - if (elseStatementIndex) { - matches[elseStatementIndex].isFlaggedForProcessing = true; - } + matches[0].isFlaggedForProcessing = true; + matches[matches.length - 1].isFlaggedForProcessing = true; + if (elseStatementIndex) { + matches[elseStatementIndex].isFlaggedForProcessing = true; + } } function joinProcessedRegexArray(regexMatches) { - let processedString = ""; - regexMatches.forEach((match) => { - const rawString = match[0]; - processedString += match.groups.processedString ?? rawString; - }); - return processedString; + let processedString = ""; + regexMatches.forEach((match) => { + const rawString = match[0]; + processedString += match.groups.processedString ?? rawString; + }); + return processedString; } function executeIfStatementAndSetProcessedStrings(ifStatementArray, replacePlaceholderCallback) { - const ifCondition = replacePlaceholderCallback(ifStatementArray[0].groups.ifCondition); - let isInsideDesiredBlock = ifCondition; + const ifCondition = replacePlaceholderCallback(ifStatementArray[0].groups.ifCondition); + let isInsideDesiredBlock = ifCondition; - ifStatementArray.forEach(entry => { - if (entry.groups.isElseStatement && entry.isFlaggedForProcessing) { - isInsideDesiredBlock = !ifCondition; - } else if (entry.groups.isEndStatement && entry.isFlaggedForProcessing) { - isInsideDesiredBlock = true; - } - setProcessedStringOnEntry(entry, isInsideDesiredBlock); - }); + ifStatementArray.forEach((entry) => { + if (entry.groups.isElseStatement && entry.isFlaggedForProcessing) { + isInsideDesiredBlock = !ifCondition; + } else if (entry.groups.isEndStatement && entry.isFlaggedForProcessing) { + isInsideDesiredBlock = true; + } + setProcessedStringOnEntry(entry, isInsideDesiredBlock); + }); } function setProcessedStringOnEntry(entry, isInsideDesiredBlock) { - if (!isInsideDesiredBlock) { - entry.groups.processedString = ""; - } else { - if (entry.groups.isIfStatement) { - entry.groups.processedString = entry.isFlaggedForProcessing ? entry.groups.ifTrailingString : entry[0]; - } else if (entry.groups.isElseStatement) { - entry.groups.processedString = entry.isFlaggedForProcessing ? entry.groups.elseTrailingString : entry[0]; + if (!isInsideDesiredBlock) { + entry.groups.processedString = ""; } else { - entry.groups.processedString = entry.isFlaggedForProcessing ? entry.groups.endTrailingString : entry[0]; + if (entry.groups.isIfStatement) { + entry.groups.processedString = entry.isFlaggedForProcessing + ? entry.groups.ifTrailingString + : entry[0]; + } else if (entry.groups.isElseStatement) { + entry.groups.processedString = entry.isFlaggedForProcessing + ? entry.groups.elseTrailingString + : entry[0]; + } else { + entry.groups.processedString = entry.isFlaggedForProcessing + ? entry.groups.endTrailingString + : entry[0]; + } } - } } function getIfStatementRegexExpression() { - // Matches but does not capture: - // {if:...} or {else} or {end} - const anyLogicOperatorNonCapture = "(?:{(?:end|else|if:.*?)})"; - // NOTE: ? syntax stores the captured match like so: match.groups.variableName - const matchStartOfString = ( - "(?^.+?)" + // Match and Capture all characters (lazy), cannot be empty - "(?=(?:{if))" // Looks ahead but does not capture {if - ); - const matchIfOperator = ( - "(?{if:)" + // Match & Capture {if: - "(?.*?):" + // Match all chars up to and including next ":" - Capture all chars up to ":" - "(?.*?)}" + // Match all chars up to and including next "}" - Capture all chars up to "}" - "(?.*?)" + // Match and Capture all characters (lazy), can be empty - "(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator - ); - const matchElseOperator = ( - "(?{else})" + // Match & Capture {else} - "(?.*?)" + // Match & Capture all characters (lazy), can be empty - "(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator - ); - const matchEndOperator = ( - "(?{end})" + // Match & Capture {end} - "(?.*?)" + // Match & Capture all chracters (lazy), can be empty - "(?="+ anyLogicOperatorNonCapture + "|$)" // Looks ahead but does not capture the next logic operator - ); - // Combine all matching patterns, separated by "or" pipes - return new RegExp(matchStartOfString + "|" + matchIfOperator + "|" + matchElseOperator + "|" + matchEndOperator, "g"); + // Matches but does not capture: + // {if:...} or {else} or {end} + const anyLogicOperatorNonCapture = "(?:{(?:end|else|if:.*?)})"; + // NOTE: ? syntax stores the captured match like so: match.groups.variableName + const matchStartOfString = + "(?^.+?)" + // Match and Capture all characters (lazy), cannot be empty + "(?=(?:{if))"; // Looks ahead but does not capture {if + const matchIfOperator = + "(?{if:)" + // Match & Capture {if: + "(?.*?):" + // Match all chars up to and including next ":" - Capture all chars up to ":" + "(?.*?)}" + // Match all chars up to and including next "}" - Capture all chars up to "}" + "(?.*?)" + // Match and Capture all characters (lazy), can be empty + "(?=" + + anyLogicOperatorNonCapture + + ")"; // Looks ahead but does not capture the next logic operator + const matchElseOperator = + "(?{else})" + // Match & Capture {else} + "(?.*?)" + // Match & Capture all characters (lazy), can be empty + "(?=" + + anyLogicOperatorNonCapture + + ")"; // Looks ahead but does not capture the next logic operator + const matchEndOperator = + "(?{end})" + // Match & Capture {end} + "(?.*?)" + // Match & Capture all chracters (lazy), can be empty + "(?=" + + anyLogicOperatorNonCapture + + "|$)"; // Looks ahead but does not capture the next logic operator + // Combine all matching patterns, separated by "or" pipes + return new RegExp( + matchStartOfString + + "|" + + matchIfOperator + + "|" + + matchElseOperator + + "|" + + matchEndOperator, + "g" + ); } ////////////////////////////////////////// @@ -269,48 +284,48 @@ function getIfStatementRegexExpression() { ////////////////////////////////////////// export function doesCopyContainRouterLink(copy) { - return copy.includes(this.dynamicStrings.ROUTER_LINK); + return copy.includes(this.dynamicStrings.ROUTER_LINK); } export function doesCopyContainTextLink(copy) { - return copy.includes(dynamicStrings.TEXT_LINK); + return copy.includes(dynamicStrings.TEXT_LINK); } /** * splits copy on { ... } such as {routerlink: ...} * @returns array of strings */ -export function splitCopyOnCMSPlaceHolder(copy){ - return copy.split(/{(.*?)}/g); +export function splitCopyOnCMSPlaceHolder(copy) { + return copy.split(/{(.*?)}/g); } /** * Returns string2 of input following this pattern: {string1:string2,string3} * @returns string */ -export function getLinkTargetFromCopy(copy){ - // sample input: {routerLink:estimate,provide your VIN} - // first split would return 'estimate,provide your VIN' - // second split would return 'estimate' - return copy.split(':')[1].split(',')[0]; +export function getLinkTargetFromCopy(copy) { + // sample input: {routerLink:estimate,provide your VIN} + // first split would return 'estimate,provide your VIN' + // second split would return 'estimate' + return copy.split(":")[1].split(",")[0]; } /** * Returns string3 of input following this pattern: {string1:string2,string3} * @returns string */ -export function getLinkDisplayTextFromCopy(copy){ - // sample input: {routerLink:estimate,provide your VIN} - // first split would return 'estimate,provide your VIN' - // second split would return 'provide your VIN' - return copy.split(':')[1].split(',')[1]; +export function getLinkDisplayTextFromCopy(copy) { + // sample input: {routerLink:estimate,provide your VIN} + // first split would return 'estimate,provide your VIN' + // second split would return 'provide your VIN' + return copy.split(":")[1].split(",")[1]; } // Copy returned from the CMS that has newlines will return blocks wrapped in //

...

-// This function returns an array of each paragraph, works with or without html +// This function returns an array of each paragraph, works with or without html // attributes present export function splitCMSCopyOnParagraphTag(copy) { - // filter removes empty strings that are a result of string.split with regex - return copy.split(/(?:)|(?:<\/p>)/g).filter(paragraph => paragraph !== ""); + // filter removes empty strings that are a result of string.split with regex + return copy.split(/(?:)|(?:<\/p>)/g).filter((paragraph) => paragraph !== ""); } diff --git a/src/helpers/cms-helper.spec.js b/src/helpers/cms-helper.spec.js index d7aeb8bf4..3f57b999c 100644 --- a/src/helpers/cms-helper.spec.js +++ b/src/helpers/cms-helper.spec.js @@ -2,135 +2,129 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { dispatch } from "@/store"; jest.mock("@/store", () => ({ - dispatch: jest.fn(), - state: { - order: { vehicle: { year: "2019", make: "Acura" } }, - }, + dispatch: jest.fn(), + state: { + order: { vehicle: { year: "2019", make: "Acura" } }, + }, })); describe("cms-content-helper.js", () => { - it("Should return data from CMS", () => { - // Arrange - const cmsMockData = { - Result: [ - { - Name: "FunnelHeaderWidget", - Model: { - HeaderText: "Select a year to get started", - }, - }, - { - Name: "FunnelSubHeaderWidget", - Model: { - HeaderText: "Select a model", - }, - }, - { - Name: "VehicleYearQuestion", - Model: { - QuestionText: "What year is your vehicle?", - }, - }, - ], - }; + it("Should return data from CMS", () => { + // Arrange + const cmsMockData = { + Result: [ + { + Name: "FunnelHeaderWidget", + Model: { + HeaderText: "Select a year to get started", + }, + }, + { + Name: "FunnelSubHeaderWidget", + Model: { + HeaderText: "Select a model", + }, + }, + { + Name: "VehicleYearQuestion", + Model: { + QuestionText: "What year is your vehicle?", + }, + }, + ], + }; - dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); - // Act - fetchCmsContentForPage("testPage").then((response) => { - // Assert - expect(response.FunnelHeaderWidget.HeaderText).toEqual( - "Select a year to get started" - ); + // Act + fetchCmsContentForPage("testPage").then((response) => { + // Assert + expect(response.FunnelHeaderWidget.HeaderText).toEqual("Select a year to get started"); + }); }); - }); }); describe("cms-content-helper.js", () => { - it("Should replace strings for global state", () => { - const cmsMockData = { - Result: [ - { - Name: "FunnelSubHeaderWidget", - Model: { - HeaderText: "{globalState:order.vehicle.year}", - }, - }, - ], - }; + it("Should replace strings for global state", () => { + const cmsMockData = { + Result: [ + { + Name: "FunnelSubHeaderWidget", + Model: { + HeaderText: "{globalState:order.vehicle.year}", + }, + }, + ], + }; - dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); - fetchCmsContentForPage("testPage").then((response) => { - // Assert - expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019"); + fetchCmsContentForPage("testPage").then((response) => { + // Assert + expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019"); + }); }); - }); }); describe("cms-content-helper.js", () => { - it("Should replace strings for global state, and leave others the same", () => { - const cmsMockData = { - Result: [ - { - Name: "FunnelSubHeaderWidget", - Model: { - HeaderText: "{globalState:order.vehicle.year}", - }, - }, - { - Name: "VehicleYearQuestion", - Model: { - ExampleText: "My widget value!", - }, - }, - ], - }; + it("Should replace strings for global state, and leave others the same", () => { + const cmsMockData = { + Result: [ + { + Name: "FunnelSubHeaderWidget", + Model: { + HeaderText: "{globalState:order.vehicle.year}", + }, + }, + { + Name: "VehicleYearQuestion", + Model: { + ExampleText: "My widget value!", + }, + }, + ], + }; - dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); - fetchCmsContentForPage("testPage").then((response) => { - // Assert - expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019"); - expect(response.VehicleYearQuestion.ExampleText).toEqual( - "My widget value!" - ); + fetchCmsContentForPage("testPage").then((response) => { + // Assert + expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019"); + expect(response.VehicleYearQuestion.ExampleText).toEqual("My widget value!"); + }); }); - }); }); describe("cms-content-helper.js", () => { - it("Should replace strings for global state in nested objects", () => { - const cmsMockData = { - Result: [ - { - Name: "FunnelSubHeaderWidget", - Model: { - HeaderText: "{globalState:order.vehicle.year}", - }, - }, - { - Name: "VehicleMakeQuestion", - Model: { - OtherObjectInside: { - ExampleText: "{globalState:order.vehicle.make}", - }, - }, - }, - ], - }; + it("Should replace strings for global state in nested objects", () => { + const cmsMockData = { + Result: [ + { + Name: "FunnelSubHeaderWidget", + Model: { + HeaderText: "{globalState:order.vehicle.year}", + }, + }, + { + Name: "VehicleMakeQuestion", + Model: { + OtherObjectInside: { + ExampleText: "{globalState:order.vehicle.make}", + }, + }, + }, + ], + }; - dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); - fetchCmsContentForPage("testPage").then((response) => { - // Assert + fetchCmsContentForPage("testPage").then((response) => { + // Assert - expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019"); - expect( - response.VehicleMakeQuestion.OtherObjectInside.ExampleText - ).toEqual("Acura"); + expect(response.FunnelSubHeaderWidget.HeaderText).toEqual("2019"); + expect(response.VehicleMakeQuestion.OtherObjectInside.ExampleText).toEqual("Acura"); + }); }); - }); }); test.todo("String cannot be mapped to global state"); diff --git a/src/helpers/damage-helper.js b/src/helpers/damage-helper.js index 55d10738d..dced8bf01 100644 --- a/src/helpers/damage-helper.js +++ b/src/helpers/damage-helper.js @@ -5,61 +5,68 @@ import { storeActions } from "@/constants/store-actions"; export function getDamageString() { // If it's a repair it's always a windshield. const isRepair = store.getters.damage.isRepair; - if(isRepair){ - return "windshield" + if (isRepair) { + return "windshield"; } const damageLocations = store.getters.damage.glassToReplace; let returnString; if (!damageLocations) { - return; + return; } if (damageLocations.length > 1) { - returnString = "match" + returnString = "match"; } else { - switch(damageLocations[0]?.location) { - case "Windshield": - returnString = "windshield" - break; - case "Driver": - case "Passenger": - returnString = "side window" - break; - case "Rear": - returnString = "rear window" - } + switch (damageLocations[0]?.location) { + case "Windshield": + returnString = "windshield"; + break; + case "Driver": + case "Passenger": + returnString = "side window"; + break; + case "Rear": + returnString = "rear window"; + } } return returnString; } - export function getIsWindshieldOnly () { +export function getIsWindshieldOnly() { const damageLocations = store.getters.damage.glassToReplace; - const returnString = damageLocations.length === 1 && damageLocations[0]?.location === "Windshield" ? "windshield" : "glass"; + const returnString = + damageLocations.length === 1 && damageLocations[0]?.location === "Windshield" + ? "windshield" + : "glass"; return returnString; - } +} -export async function isGlassAvailableForCarId(carId){ +export async function isGlassAvailableForCarId(carId) { const newGlassOptions = await baseMixin.methods.dispatchStoreAction( - storeActions.GET_DAMAGE_OPTIONS, - { carId: carId } + storeActions.GET_DAMAGE_OPTIONS, + { carId: carId } ); const currentGlassOptions = store.getters.damage.glassToReplace; const optionsMap = { - Windshield: "windshieldOptions", - Driver: "driverSideOptions", - Passenger: "passengerSideOptions", - Rear: "backGlassOptions" - } + Windshield: "windshieldOptions", + Driver: "driverSideOptions", + Passenger: "passengerSideOptions", + Rear: "backGlassOptions", + }; - for(const option of currentGlassOptions){ - if(!newGlassOptions.data[optionsMap[option.location]].availableReplacementOptions.includes(option.name)){ - return false; - } + for (const option of currentGlassOptions) { + if ( + !newGlassOptions.data[optionsMap[option.location]].availableReplacementOptions.includes( + option.name + ) + ) { + return false; + } } return true; - } +} diff --git a/src/helpers/damage-helper.spec.js b/src/helpers/damage-helper.spec.js index 2a46978dc..0e6038f31 100644 --- a/src/helpers/damage-helper.spec.js +++ b/src/helpers/damage-helper.spec.js @@ -1,91 +1,91 @@ -import {getDamageString, isGlassAvailableForCarId} from "./damage-helper"; +import { getDamageString, isGlassAvailableForCarId } from "./damage-helper"; import store from "@/store"; // Mock basemixin. jest.mock("@/mixins/base-mixin.js", () => ({ - methods: { - dispatchStoreAction: jest.fn().mockImplementation(() => { return { - data: { - windshieldOptions: {availableReplacementOptions: ["windshield"]} - } - } }), - }, + methods: { + dispatchStoreAction: jest.fn().mockImplementation(() => { + return { + data: { + windshieldOptions: { availableReplacementOptions: ["windshield"] }, + }, + }; + }), + }, })); - describe("damage-helper.js", () => { +describe("damage-helper.js", () => { it("Should return match when multiple selected damage options are in the store", () => { + // Arrange / Act + store.getters.damage.glassToReplace = [ + { location: "Windshield", name: "windshield" }, + { location: "Passenger", name: "sideWindow" }, + ]; - // Arrange / Act - store.getters.damage.glassToReplace = [{location: "Windshield", name: "windshield"}, {location: "Passenger", name: "sideWindow"}]; + const damage = getDamageString(); - const damage = getDamageString(); - - // Assert - expect(damage).toEqual("match"); + // Assert + expect(damage).toEqual("match"); }); - }); +}); - describe("damage-helper.js", () => { +describe("damage-helper.js", () => { it("Should return windshield when Windshield is the only selected damage option in the store", () => { + // Arrange / Act + store.getters.damage.glassToReplace = [{ location: "Windshield", name: "windshield" }]; - // Arrange / Act - store.getters.damage.glassToReplace = [{location: "Windshield", name: "windshield"}]; + const damage = getDamageString(); - const damage = getDamageString(); - - // Assert - expect(damage).toEqual("windshield"); + // Assert + expect(damage).toEqual("windshield"); }); - }); +}); - describe("damage-helper.js", () => { +describe("damage-helper.js", () => { it("Should return side window when Driver or Passenger is the only selected damage option in the store", () => { + // Arrange / Act + store.getters.damage.glassToReplace = [{ location: "Passenger", name: "sideWindow" }]; - // Arrange / Act - store.getters.damage.glassToReplace = [{location: "Passenger", name: "sideWindow"}]; + const damage = getDamageString(); - const damage = getDamageString(); - - // Assert - expect(damage).toEqual("side window"); + // Assert + expect(damage).toEqual("side window"); }); - }); +}); - describe("damage-helper.js", () => { +describe("damage-helper.js", () => { it("Should return rear window when Rear is the only selected damage option in the store", () => { + // Arrange / Act + store.getters.damage.glassToReplace = [{ location: "Rear", name: "rear" }]; - // Arrange / Act - store.getters.damage.glassToReplace = [{location: "Rear", name: "rear"}]; + const damage = getDamageString(); - const damage = getDamageString(); - - // Assert - expect(damage).toEqual("rear window"); + // Assert + expect(damage).toEqual("rear window"); }); - }); +}); - describe("damage-helper.js", () => { +describe("damage-helper.js", () => { it("Should return true if no mismatches between each array exist", async () => { - // Arrange - store.getters.damage.glassToReplace = [{location: "Windshield", name: "windshield"}]; + // Arrange + store.getters.damage.glassToReplace = [{ location: "Windshield", name: "windshield" }]; - // Act - const isGlassAvailable = await isGlassAvailableForCarId(); + // Act + const isGlassAvailable = await isGlassAvailableForCarId(); - // Assert - expect(isGlassAvailable).toEqual(true); + // Assert + expect(isGlassAvailable).toEqual(true); }); - }); +}); - describe("damage-helper.js", () => { +describe("damage-helper.js", () => { it("Should return false if any mismatches between each array exist", async () => { - // Arrange - store.getters.damage.glassToReplace = [{location: "Windshield", name: "sideWindow"}]; + // Arrange + store.getters.damage.glassToReplace = [{ location: "Windshield", name: "sideWindow" }]; - const isGlassAvailable = await isGlassAvailableForCarId(); + const isGlassAvailable = await isGlassAvailableForCarId(); - // Assert - expect(isGlassAvailable).toEqual(false); + // Assert + expect(isGlassAvailable).toEqual(false); }); - }); - +}); diff --git a/src/helpers/event-bus/event-bus.js b/src/helpers/event-bus/event-bus.js index 19b30870c..2b199a699 100644 --- a/src/helpers/event-bus/event-bus.js +++ b/src/helpers/event-bus/event-bus.js @@ -2,31 +2,31 @@ import store from "@/store"; import { storeMutations } from "@/constants/store-mutations.js"; export default { - // Adds event to the bus given its category, subcategory, and eventValue; - addEventToBus(category, subCategory, eventValue) { - store.commit(storeMutations.ADD_EVENT_TO_BUS, { - category: category, - subCategory: subCategory, - eventValue: eventValue, - }); - }, + // Adds event to the bus given its category, subcategory, and eventValue; + addEventToBus(category, subCategory, eventValue) { + store.commit(storeMutations.ADD_EVENT_TO_BUS, { + category: category, + subCategory: subCategory, + eventValue: eventValue, + }); + }, - // Finds event on the bus, removes the item, and returns its value to the caller. - readAndPopEventFromBus(category, subCategory) { - const event = store.getters.eventBusItem(category, subCategory); + // Finds event on the bus, removes the item, and returns its value to the caller. + readAndPopEventFromBus(category, subCategory) { + const event = store.getters.eventBusItem(category, subCategory); - store.commit(storeMutations.REMOVE_EVENT_FROM_BUS, { - category: category, - subCategory: subCategory, - }); + store.commit(storeMutations.REMOVE_EVENT_FROM_BUS, { + category: category, + subCategory: subCategory, + }); - return event; - }, + return event; + }, - // Finds the event on the bus and returns its value to the caller, does not remove it. - readEventFromBus(category, subCategory) { - const event = store.getters.eventBusItem(category, subCategory); + // Finds the event on the bus and returns its value to the caller, does not remove it. + readEventFromBus(category, subCategory) { + const event = store.getters.eventBusItem(category, subCategory); - return event; - }, + return event; + }, }; diff --git a/src/helpers/event-bus/event-bus.spec.js b/src/helpers/event-bus/event-bus.spec.js index fece3fee2..e21212adc 100644 --- a/src/helpers/event-bus/event-bus.spec.js +++ b/src/helpers/event-bus/event-bus.spec.js @@ -3,57 +3,57 @@ import eventBus from "@/helpers/event-bus/event-bus"; import store from "@/store"; describe("event-bus.js", () => { - let event = { - isDismissible: true, - messageCopy: "You can get a quote by starting on this page.", - messageHeadline: "We're sorry, something went wrong.", - type: globalEventTypes.Danger, - }; + let event = { + isDismissible: true, + messageCopy: "You can get a quote by starting on this page.", + messageHeadline: "We're sorry, something went wrong.", + type: globalEventTypes.Danger, + }; - it("Puts item on bus and then take it off", () => { - // Arrange / Act - eventBus.addEventToBus( - globalEvents.Categories.GLOBAL_ALERT, - globalEvents.SubCategories.PAGE_NOT_FOUND, - event - ); + it("Puts item on bus and then take it off", () => { + // Arrange / Act + eventBus.addEventToBus( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND, + event + ); - // Assert - expect( - store.getters.eventBusItem( - globalEvents.Categories.GLOBAL_ALERT, - globalEvents.SubCategories.PAGE_NOT_FOUND - ) - ).toEqual(event); + // Assert + expect( + store.getters.eventBusItem( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND + ) + ).toEqual(event); - expect(store.state.applicationUser.eventBus.length).toEqual(1); + expect(store.state.applicationUser.eventBus.length).toEqual(1); - // Arrange / Act - const eventValue = eventBus.readAndPopEventFromBus( - globalEvents.Categories.GLOBAL_ALERT, - globalEvents.SubCategories.PAGE_NOT_FOUND - ); + // Arrange / Act + const eventValue = eventBus.readAndPopEventFromBus( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND + ); - // Assert - expect(eventValue).toEqual(event); + // Assert + expect(eventValue).toEqual(event); - expect(store.state.applicationUser.eventBus.length).toEqual(0); - }); + expect(store.state.applicationUser.eventBus.length).toEqual(0); + }); - it("Reads event from bus, should have event value.", () => { - // Arrange / Act - eventBus.addEventToBus( - globalEvents.Categories.GLOBAL_ALERT, - globalEvents.SubCategories.PAGE_NOT_FOUND, - event - ); + it("Reads event from bus, should have event value.", () => { + // Arrange / Act + eventBus.addEventToBus( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND, + event + ); - // Assert - expect( - eventBus.readEventFromBus( - globalEvents.Categories.GLOBAL_ALERT, - globalEvents.SubCategories.PAGE_NOT_FOUND - ) - ).toEqual(event); - }); + // Assert + expect( + eventBus.readEventFromBus( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND + ) + ).toEqual(event); + }); }); diff --git a/src/helpers/heritage-integration/cookie-helper.js b/src/helpers/heritage-integration/cookie-helper.js index 3eed5950b..5b6800c86 100644 --- a/src/helpers/heritage-integration/cookie-helper.js +++ b/src/helpers/heritage-integration/cookie-helper.js @@ -8,7 +8,7 @@ import { applicationConfig } from "@/constants/application-config"; export function updateOrCreateFunnelCookie() { const wasClaimRegistrationDelayed = getFunnelCookie()?.HasDelayedClaimRegistration; const shouldSuppressConceptFunnel = getFunnelCookie()?.SuppressConceptFunnel; - + // Set up cookie with all the props. setFunnelCookieProperties({ LastTouched: new Date().toUTCString(), @@ -20,7 +20,7 @@ export function updateOrCreateFunnelCookie() { ReferralCorrelationId: store.getters.order.referralCorrelationId, ReferralParentAccountNumber: store.getters.order.accountNumber, HasDelayedClaimRegistration: wasClaimRegistrationDelayed, - SuppressConceptFunnel: shouldSuppressConceptFunnel + SuppressConceptFunnel: shouldSuppressConceptFunnel, }); } @@ -31,7 +31,7 @@ export function updateOrCreateFunnelCookie() { export function getFunnelCookie() { const cookieJson = document.cookie ?.split("; ") - ?.find(row => row.startsWith(`${cookieNames.FUNNEL_SESSION_INFO}=`)) + ?.find((row) => row.startsWith(`${cookieNames.FUNNEL_SESSION_INFO}=`)) ?.split("=")[1]; try { @@ -59,31 +59,31 @@ export function getCookieDomainValue() { Gets value of dxdev cookie, and then extracts "did" value from it. Returns empty string if cookie not found or "did" string not present. */ -export function getDeviceIdValue(){ +export function getDeviceIdValue() { // Sometimes these cookie contains more than the device ID. const cookieValue = getCookieValueByName(cookieNames.DXDEV); - const cookieValuesSplit = cookieValue.split('='); + const cookieValuesSplit = cookieValue.split("="); // If this is the only value, just use that. - if(cookieValuesSplit.length === 2 && cookieValuesSplit[0] === 'did'){ + if (cookieValuesSplit.length === 2 && cookieValuesSplit[0] === "did") { return cookieValuesSplit[1]; } const cookieValueMatch = cookieValue.match("^did=[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}"); - if(cookieValueMatch){ - return cookieValueMatch[0].split('=')[1]; + if (cookieValueMatch) { + return cookieValueMatch[0].split("=")[1]; } - return '00000000-0000-0000-0000-000000000000'; + return "00000000-0000-0000-0000-000000000000"; } /* Gets value of skey cookie, returns 0 if not found. */ -export function getSessionKeyValue(){ +export function getSessionKeyValue() { const cookieValue = getCookieValueByName(cookieNames.SESSION_KEY); - if(cookieValue){ + if (cookieValue) { return cookieValue; } @@ -93,14 +93,14 @@ export function getSessionKeyValue(){ /* Gets value of skey cookie, returns 0 if not found. */ -export function getSessionIdValue(){ +export function getSessionIdValue() { const cookieValue = getCookieValueByName(cookieNames.SESSION_ID); - if(cookieValue){ + if (cookieValue) { return cookieValue; } - return '00000000-0000-0000-0000-000000000000'; + return "00000000-0000-0000-0000-000000000000"; } /* @@ -110,10 +110,17 @@ export function updateSessionIdCookie() { createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), { maxAge: 60 * 30 }); } -export function setCookieProperties(properties, { useDefaultFunnelCookieAttributes = true, maxAge, isSecure }) { +export function setCookieProperties( + properties, + { useDefaultFunnelCookieAttributes = true, maxAge, isSecure } +) { if (typeof properties == "object") { - Object.keys(properties).forEach(key => { - createOrUpdateCookie(key, properties[key], { useDefaultFunnelCookieAttributes, maxAge, isSecure }); + Object.keys(properties).forEach((key) => { + createOrUpdateCookie(key, properties[key], { + useDefaultFunnelCookieAttributes, + maxAge, + isSecure, + }); }); } } @@ -124,7 +131,6 @@ export function setCookieProperties(properties, { useDefaultFunnelCookieAttribut =========================== */ - /* Used to set properties on the funnel cookie. Takes an object with properties to set. Will overwrite existing properties. @@ -134,12 +140,12 @@ function setFunnelCookieProperties(properties) { let cookie = getFunnelCookie(); if (cookie !== null) { - Object.keys(properties).forEach(key => { + Object.keys(properties).forEach((key) => { cookie[key] = properties[key]; }); } - const cookieValueJson = JSON.stringify(cookie ?? {}); + const cookieValueJson = JSON.stringify(cookie ?? {}); createOrUpdateCookie(cookieNames.FUNNEL_SESSION_INFO, cookieValueJson, {}); } } @@ -148,7 +154,11 @@ function setFunnelCookieProperties(properties) { Used to create a cookie. `useDefaultFunnelCookieAttributes` will set the path and domain to our defaults */ -function createOrUpdateCookie(key, value = "", { useDefaultFunnelCookieAttributes = true, maxAge, isSecure = true }) { +function createOrUpdateCookie( + key, + value = "", + { useDefaultFunnelCookieAttributes = true, maxAge, isSecure = true } +) { let cookieToAdd = `${key}=${value}; `; if (useDefaultFunnelCookieAttributes) { @@ -173,12 +183,12 @@ function getDomainWithoutSubdomain() { return "localhost"; } - const urlParts = url.split('.'); + const urlParts = url.split("."); return `.${urlParts .slice(0) .slice(-(urlParts.length === 4 ? 3 : 2)) - .join('.')}`; + .join(".")}`; } /* @@ -196,4 +206,4 @@ function getCookieValueByName(name) { function isLocalhost() { return location.hostname.includes("localhost"); -} \ No newline at end of file +} diff --git a/src/helpers/heritage-integration/cookie-helper.spec.js b/src/helpers/heritage-integration/cookie-helper.spec.js index a5857fd74..5df2ae925 100644 --- a/src/helpers/heritage-integration/cookie-helper.spec.js +++ b/src/helpers/heritage-integration/cookie-helper.spec.js @@ -1,145 +1,145 @@ -import {getFunnelCookie, getDeviceIdValue, getSessionKeyValue, getSessionIdValue} from "@/helpers/heritage-integration/cookie-helper.js"; +import { + getFunnelCookie, + getDeviceIdValue, + getSessionKeyValue, + getSessionIdValue, +} from "@/helpers/heritage-integration/cookie-helper.js"; import { removeAllTestCookies, setupCookies } from "@/helpers/unit-test-helper"; describe("cookies", () => { - afterEach(() => { - removeAllTestCookies(); - }) - - describe("getFunnelCookie method", () => { - test("gets correct value when cookie is present", () => { - // Arrange - const testReferralNumber = 1566818; - const testReferralDate = "2022-03-15T10:56:24.597"; - const testReferralCorrelationId = "404d2b04-f86e-45c3-b373-127b6217b060"; - const testShouldResetState = false; - const testDidHeritageFunnelUpdateLast = true; - - const testCookieValue = { - ReferralNumber: testReferralNumber, - ReferralDate: testReferralDate, - ReferralCorrelationId: testReferralCorrelationId, - ShouldResetState: testShouldResetState, - DidHeritageFunnelUpdateLast: testDidHeritageFunnelUpdateLast, - SuppressConceptFunnel: true - } + removeAllTestCookies(); + }); - setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); - - // Act - var result = getFunnelCookie(); - - // Assert - expect(result).toEqual(testCookieValue); - expect(typeof result).toEqual("object"); - expect(result.ReferralNumber).toEqual(testReferralNumber); - expect(result.ReferralDate).toEqual(testReferralDate); - expect(result.ReferralCorrelationId).toEqual(testReferralCorrelationId); - expect(result.ShouldResetState).toEqual(testShouldResetState); - expect(result.DidHeritageFunnelUpdateLast).toEqual(testDidHeritageFunnelUpdateLast); - }); - - test("returns empty object when value is empty object", () => { - // Arrange - const testCookieValue = {}; - setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); - - // Act - var result = getFunnelCookie(); - - // Assert - expect(result).toEqual(testCookieValue); - expect(typeof result).toEqual("object"); - expect(Object.keys(result)).toHaveLength(0); - }); - - test("returns null when value is empty string", () => { - // Arrange - const testCookieValue = ""; - setupCookies({ funnelCookieValue: testCookieValue }); - - // Act - var result = getFunnelCookie(); - - // Assert - expect(result).toEqual(null); - }); - - test("returns null when funnel cookie doesn't exist", () => { - // Arrange - setupCookies({ includeHeritageCookie: false }); - - // Act - var result = getFunnelCookie(); - - // Assert - expect(result).toEqual(null); - }); - - test("gets correct cookie value", () => { - // Arrange - const testCookieValue = { test: "testValue" }; - - setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); - - // Act - const actualCookieValue = getFunnelCookie(); - - // Assert - expect(actualCookieValue).toEqual(testCookieValue); - }); - - test("getCookieValue: Gets null cookie value", () => { - // Arrange - setupCookies({ includeHeritageCookie: false }); - - // Act - const actualCookieValue = getFunnelCookie(); - - // Assert - expect(actualCookieValue).toBeNull(); - }); - }) + describe("getFunnelCookie method", () => { + test("gets correct value when cookie is present", () => { + // Arrange + const testReferralNumber = 1566818; + const testReferralDate = "2022-03-15T10:56:24.597"; + const testReferralCorrelationId = "404d2b04-f86e-45c3-b373-127b6217b060"; + const testShouldResetState = false; + const testDidHeritageFunnelUpdateLast = true; + + const testCookieValue = { + ReferralNumber: testReferralNumber, + ReferralDate: testReferralDate, + ReferralCorrelationId: testReferralCorrelationId, + ShouldResetState: testShouldResetState, + DidHeritageFunnelUpdateLast: testDidHeritageFunnelUpdateLast, + SuppressConceptFunnel: true, + }; + + setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); + + // Act + var result = getFunnelCookie(); + + // Assert + expect(result).toEqual(testCookieValue); + expect(typeof result).toEqual("object"); + expect(result.ReferralNumber).toEqual(testReferralNumber); + expect(result.ReferralDate).toEqual(testReferralDate); + expect(result.ReferralCorrelationId).toEqual(testReferralCorrelationId); + expect(result.ShouldResetState).toEqual(testShouldResetState); + expect(result.DidHeritageFunnelUpdateLast).toEqual(testDidHeritageFunnelUpdateLast); + }); + + test("returns empty object when value is empty object", () => { + // Arrange + const testCookieValue = {}; + setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); + + // Act + var result = getFunnelCookie(); + + // Assert + expect(result).toEqual(testCookieValue); + expect(typeof result).toEqual("object"); + expect(Object.keys(result)).toHaveLength(0); + }); + + test("returns null when value is empty string", () => { + // Arrange + const testCookieValue = ""; + setupCookies({ funnelCookieValue: testCookieValue }); + + // Act + var result = getFunnelCookie(); + + // Assert + expect(result).toEqual(null); + }); + + test("returns null when funnel cookie doesn't exist", () => { + // Arrange + setupCookies({ includeHeritageCookie: false }); + + // Act + var result = getFunnelCookie(); + + // Assert + expect(result).toEqual(null); + }); + + test("gets correct cookie value", () => { + // Arrange + const testCookieValue = { test: "testValue" }; + + setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); + + // Act + const actualCookieValue = getFunnelCookie(); + + // Assert + expect(actualCookieValue).toEqual(testCookieValue); + }); + + test("getCookieValue: Gets null cookie value", () => { + // Arrange + setupCookies({ includeHeritageCookie: false }); + + // Act + const actualCookieValue = getFunnelCookie(); + + // Assert + expect(actualCookieValue).toBeNull(); + }); + }); describe("getDeviceIdValue", () => { - test("getDeviceIdValue, should return GUID", () => { - // Arrange - setupCookies({}); + test("getDeviceIdValue, should return GUID", () => { + // Arrange + setupCookies({}); - // Act - const result = getDeviceIdValue(); + // Act + const result = getDeviceIdValue(); - //Assert - expect(result).toBe('21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe'); + //Assert + expect(result).toBe("21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe"); + }); - }); + test("getSessionKeyValue, should return session key int", () => { + // Arrange + setupCookies({}); - test("getSessionKeyValue, should return session key int", () => { - // Arrange - setupCookies({}); + // Act + const result = getSessionKeyValue(); - // Act - const result = getSessionKeyValue(); - - //Assert - expect(result).toBe('12345'); - - }); + //Assert + expect(result).toBe("12345"); + }); }); describe("getSessionIdValue", () => { - test("getSessionIdValue, should return GUID", () => { - // Arrange - setupCookies({}); + test("getSessionIdValue, should return GUID", () => { + // Arrange + setupCookies({}); - // Act - const result = getSessionIdValue(); + // Act + const result = getSessionIdValue(); - //Assert - expect(result).toBe('cba0c3d1-3c1b-4305-bb56-31aa50f58e27'); - - }); - }); - }) - \ No newline at end of file + //Assert + expect(result).toBe("cba0c3d1-3c1b-4305-bb56-31aa50f58e27"); + }); + }); +}); diff --git a/src/helpers/heritage-integration/navigation-helper.js b/src/helpers/heritage-integration/navigation-helper.js index bd7c3dcf4..e51331779 100644 --- a/src/helpers/heritage-integration/navigation-helper.js +++ b/src/helpers/heritage-integration/navigation-helper.js @@ -13,7 +13,7 @@ import router from "@/router"; */ export async function getPageToRouteExistingOrderTo(toRoute = {}, existingHeritageOrder = false) { // If the user is coming in via the Safelite.Com CTA - if (toRoute.query[queryStrings.START_TYPE] === 'fmg') { + if (toRoute.query[queryStrings.START_TYPE] === "fmg") { // If they have an existing order, return 'heritage' for the page name. if (existingHeritageOrder) { return fmgPageValues.HERITAGE; @@ -45,14 +45,11 @@ export async function navigateToHeritageFunnel(shouldSaveSession = true) { await saveSession(); } - router.navigateToExternalUrl( - externalUrls.HERITAGE_FUNNEL, - { - corid: store.getters.order.referralCorrelationId, - src: "concept-funnel", - conceptsqid: store.getters.applicationUser.savedSessionId - } - ); + router.navigateToExternalUrl(externalUrls.HERITAGE_FUNNEL, { + corid: store.getters.order.referralCorrelationId, + src: "concept-funnel", + conceptsqid: store.getters.applicationUser.savedSessionId, + }); } /* @@ -71,7 +68,9 @@ async function getLatestPageForRedirection() { const partQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.PART_QUESTIONS); const vehiclePartsComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_PARTS); const moldingQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.MOLDING_QUESTIONS); - const capabilityQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.CAPABILITY_QUESTIONS); + const capabilityQuestionsComponent = await getLazyLoadedComponent( + fmgPageValues.CAPABILITY_QUESTIONS + ); if (!vehicleMakeComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.VEHICLE_YEAR; @@ -86,23 +85,21 @@ async function getLatestPageForRedirection() { } else { if (capabilityQuestionsComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.CAPABILITY_QUESTIONS; - } - else if (moldingQuestionsComponent.methods.arePagePrerequisitesValid()) { + } else if (moldingQuestionsComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.MOLDING_QUESTIONS; - } - else if (vehiclePartsComponent.methods.arePagePrerequisitesValid()) { + } else if (vehiclePartsComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.VEHICLE_PARTS; - } - else if (partQuestionsComponent.methods.arePagePrerequisitesValid()) { + } else if (partQuestionsComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.PART_QUESTIONS; - } - else if (vinLookupComponent.methods.arePagePrerequisitesValid() && !store.getters.damage.isRepair) { + } else if ( + vinLookupComponent.methods.arePagePrerequisitesValid() && + !store.getters.damage.isRepair + ) { return fmgPageValues.VIN_LOOKUP; - } - else { + } else { return fmgPageValues.ESTIMATE; } - } + } } /* @@ -119,10 +116,9 @@ function overrideYmmsDirectionIfNeeded(toRoute) { case fmgPageValues.VEHICLE_YEAR: case fmgPageValues.VEHICLE_MAKE: case fmgPageValues.VEHICLE_MODEL: - case fmgPageValues.VEHICLE_STYLE: - { - return fmgPageValues.VEHICLE_DAMAGE; - } + case fmgPageValues.VEHICLE_STYLE: { + return fmgPageValues.VEHICLE_DAMAGE; + } default: { return fmgPageValue; } @@ -140,13 +136,15 @@ function overrideYmmsDirectionIfNeeded(toRoute) { function isVinRelatedPage(toRoute) { const fmgPageValue = toRoute.query[queryStrings.FMG_PAGE]; - return fmgPageValue === fmgPageValues.VIN_LOOKUP || + return ( + fmgPageValue === fmgPageValues.VIN_LOOKUP || fmgPageValue === fmgPageValues.LICENSE_PLATE_LOOKUP || fmgPageValue === fmgPageValues.ADDRESS_LOOKUP || fmgPageValue === fmgPageValues.ADDRESS_VEHICLES || - fmgPageValue === fmgPageValues.ESTIMATE; + fmgPageValue === fmgPageValues.ESTIMATE + ); } async function getLazyLoadedComponent(pageName) { return (await lazyLoadComponent(pageName)()).default; -} \ No newline at end of file +} diff --git a/src/helpers/heritage-integration/navigation-helper.spec.js b/src/helpers/heritage-integration/navigation-helper.spec.js index 3281f53c2..7e26f4a54 100644 --- a/src/helpers/heritage-integration/navigation-helper.spec.js +++ b/src/helpers/heritage-integration/navigation-helper.spec.js @@ -1,4 +1,7 @@ -import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; +import { + getPageToRouteExistingOrderTo, + navigateToHeritageFunnel, +} from "@/helpers/heritage-integration/navigation-helper"; import * as orderHelper from "@/helpers/heritage-integration/order-helper"; import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js"; import { storeActions } from "@/constants/store-actions"; @@ -19,13 +22,13 @@ describe("getPageToRouteExistingOrderTo", () => { test("should return vehicle-year", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. mockLazyLoadComponentReturnValues({ - [fmgPageValues.VEHICLE_MAKE]: false - }) + [fmgPageValues.VEHICLE_MAKE]: false, + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -37,14 +40,14 @@ describe("getPageToRouteExistingOrderTo", () => { test("should return vehicle-make", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. mockLazyLoadComponentReturnValues({ [fmgPageValues.VEHICLE_MAKE]: true, - [fmgPageValues.VEHICLE_MODEL]: false - }) + [fmgPageValues.VEHICLE_MODEL]: false, + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -56,7 +59,7 @@ describe("getPageToRouteExistingOrderTo", () => { test("should return vehicle-model", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. @@ -64,7 +67,7 @@ describe("getPageToRouteExistingOrderTo", () => { [fmgPageValues.VEHICLE_MAKE]: true, [fmgPageValues.VEHICLE_MODEL]: true, [fmgPageValues.VEHICLE_STYLE]: false, - }) + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -76,7 +79,7 @@ describe("getPageToRouteExistingOrderTo", () => { test("should return vehicle-style", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. @@ -85,7 +88,7 @@ describe("getPageToRouteExistingOrderTo", () => { [fmgPageValues.VEHICLE_MODEL]: true, [fmgPageValues.VEHICLE_STYLE]: true, [fmgPageValues.VEHICLE_DAMAGE]: false, - }) + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -97,7 +100,7 @@ describe("getPageToRouteExistingOrderTo", () => { test("should return vehicle-damage", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. @@ -106,8 +109,8 @@ describe("getPageToRouteExistingOrderTo", () => { [fmgPageValues.VEHICLE_MODEL]: true, [fmgPageValues.VEHICLE_STYLE]: true, [fmgPageValues.VEHICLE_DAMAGE]: true, - [fmgPageValues.ESTIMATE]: false - }) + [fmgPageValues.ESTIMATE]: false, + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -119,7 +122,7 @@ describe("getPageToRouteExistingOrderTo", () => { test("user has YMMS and no vehicle questions > should return vin-lookup", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. @@ -134,7 +137,7 @@ describe("getPageToRouteExistingOrderTo", () => { [fmgPageValues.VEHICLE_PARTS]: false, [fmgPageValues.PART_QUESTIONS]: false, [fmgPageValues.VIN_LOOKUP]: true, - }) + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -146,7 +149,7 @@ describe("getPageToRouteExistingOrderTo", () => { test("user has YMMS but no questions or carId > should return estimate", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. @@ -161,7 +164,7 @@ describe("getPageToRouteExistingOrderTo", () => { [fmgPageValues.VEHICLE_PARTS]: false, [fmgPageValues.PART_QUESTIONS]: false, [fmgPageValues.VIN_LOOKUP]: false, - }) + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -173,7 +176,7 @@ describe("getPageToRouteExistingOrderTo", () => { test("user has capability questions and molding questions > should return capability questions", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. @@ -188,7 +191,7 @@ describe("getPageToRouteExistingOrderTo", () => { [fmgPageValues.VEHICLE_PARTS]: false, [fmgPageValues.PART_QUESTIONS]: false, [fmgPageValues.VIN_LOOKUP]: false, - }) + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -200,7 +203,7 @@ describe("getPageToRouteExistingOrderTo", () => { test("user has molding questions and part questions > should return molding questions", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. @@ -215,7 +218,7 @@ describe("getPageToRouteExistingOrderTo", () => { [fmgPageValues.VEHICLE_PARTS]: false, [fmgPageValues.PART_QUESTIONS]: true, [fmgPageValues.VIN_LOOKUP]: false, - }) + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -227,7 +230,7 @@ describe("getPageToRouteExistingOrderTo", () => { test("user has vehicle parts questions > should return vehicle-parts", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. @@ -242,7 +245,7 @@ describe("getPageToRouteExistingOrderTo", () => { [fmgPageValues.VEHICLE_PARTS]: true, [fmgPageValues.PART_QUESTIONS]: true, [fmgPageValues.VIN_LOOKUP]: false, - }) + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -254,7 +257,7 @@ describe("getPageToRouteExistingOrderTo", () => { test("user has part questions > should return part-questions", async () => { // Arrange const toRoute = { - query: {} + query: {}, }; // Mock out the lazy load calls for all components. @@ -269,7 +272,7 @@ describe("getPageToRouteExistingOrderTo", () => { [fmgPageValues.VEHICLE_PARTS]: false, [fmgPageValues.PART_QUESTIONS]: true, [fmgPageValues.VIN_LOOKUP]: false, - }) + }); // Act const result = await getPageToRouteExistingOrderTo(toRoute, false); @@ -282,16 +285,16 @@ describe("getPageToRouteExistingOrderTo", () => { // Arrange const toRoute = { query: { - [queryStrings.START_TYPE]: 'fmg' - } - } + [queryStrings.START_TYPE]: "fmg", + }, + }; // Act const result = await getPageToRouteExistingOrderTo(toRoute, true); // Assert expect(result).toBe(fmgPageValues.HERITAGE); - }) + }); }); describe("navigateToHeritageFunnel", () => { @@ -302,16 +305,25 @@ describe("navigateToHeritageFunnel", () => { const mockReferralDate = "2022"; const mockAccountNumber = "167132"; const mockSavedSessionId = "xxx-xxx-xxx"; - const mockCrmCustomerId = "xxx-xxx-xxx" + const mockCrmCustomerId = "xxx-xxx-xxx"; - const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, mockAccountNumber, mockSavedSessionId, mockCrmCustomerId); + const mockOrderInfo = getMockOrderInfo( + mockReferralNumber, + mockCorrelationId, + mockReferralDate, + mockAccountNumber, + mockSavedSessionId, + mockCrmCustomerId + ); const mockData = { - actionList: [{ - actionName: storeActions.SAVE_SESSION, - data: mockOrderInfo, - }] - } + actionList: [ + { + actionName: storeActions.SAVE_SESSION, + data: mockOrderInfo, + }, + ], + }; setupMocksForJsFiles(mockData); const saveSessionFunction = jest.spyOn(orderHelper, "saveSession"); @@ -325,7 +337,8 @@ describe("navigateToHeritageFunnel", () => { // Should save before we navigate to heritage by default const saveSessionFunctionCallOrder = saveSessionFunction.mock.invocationCallOrder[0]; - const routerNavigateFunctionCallOrder = router.navigateToExternalUrl.mock.invocationCallOrder[0]; + const routerNavigateFunctionCallOrder = + router.navigateToExternalUrl.mock.invocationCallOrder[0]; expect(saveSessionFunctionCallOrder).toBeLessThan(routerNavigateFunctionCallOrder); saveSessionFunction.mockRestore(); }); @@ -337,20 +350,29 @@ describe("navigateToHeritageFunnel", () => { const mockReferralDate = "2022"; const mockAccountNumber = "167132"; const mockSavedSessionId = "xxx-xxx-xxx"; - const mockCrmCustomerId = "xxx-xxx-xxx" + const mockCrmCustomerId = "xxx-xxx-xxx"; - const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, mockAccountNumber, mockSavedSessionId, mockCrmCustomerId); + const mockOrderInfo = getMockOrderInfo( + mockReferralNumber, + mockCorrelationId, + mockReferralDate, + mockAccountNumber, + mockSavedSessionId, + mockCrmCustomerId + ); const mockData = { - actionList: [{ - actionName: storeActions.SAVE_SESSION, - data: mockOrderInfo, - }] - } + actionList: [ + { + actionName: storeActions.SAVE_SESSION, + data: mockOrderInfo, + }, + ], + }; setupMocksForJsFiles(mockData); - store.getters.order.referralCorrelationId = mockCorrelationId + store.getters.order.referralCorrelationId = mockCorrelationId; router.navigateToExternalUrl = jest.fn(); @@ -359,9 +381,10 @@ describe("navigateToHeritageFunnel", () => { // Assert expect(router.navigateToExternalUrl).toHaveBeenCalled(); - expect(router.navigateToExternalUrl).toHaveBeenCalledWith(externalUrls.HERITAGE_FUNNEL, + expect(router.navigateToExternalUrl).toHaveBeenCalledWith( + externalUrls.HERITAGE_FUNNEL, expect.objectContaining({ - corid: mockCorrelationId + corid: mockCorrelationId, }) ); }); @@ -372,14 +395,20 @@ describe("navigateToHeritageFunnel", () => { const mockCorrelationId = "55"; const mockReferralDate = "2022"; - const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate); + const mockOrderInfo = getMockOrderInfo( + mockReferralNumber, + mockCorrelationId, + mockReferralDate + ); const mockData = { - actionList: [{ - actionName: storeActions.SAVE_SESSION, - data: mockOrderInfo, - }] - } + actionList: [ + { + actionName: storeActions.SAVE_SESSION, + data: mockOrderInfo, + }, + ], + }; setupMocksForJsFiles(mockData); const saveSessionFunction = jest.spyOn(orderHelper, "saveSession"); @@ -395,7 +424,7 @@ describe("navigateToHeritageFunnel", () => { }); }); -/** +/** * `arePagePrerequisitesValidObject` is an object where the keys are fmgPageValue names and the values are booleans that indicate * whether arePagePrerequisitesValid is true or false */ @@ -405,10 +434,12 @@ function mockLazyLoadComponentReturnValues(arePagePrerequisitesValidObject = {}) return Promise.resolve({ default: { methods: { - arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(arePagePrerequisitesValidObject[pageName]) - } - } - }) - } - }) + arePagePrerequisitesValid: jest + .fn() + .mockReturnValueOnce(arePagePrerequisitesValidObject[pageName]), + }, + }, + }); + }; + }); } diff --git a/src/helpers/heritage-integration/order-helper.js b/src/helpers/heritage-integration/order-helper.js index 733402cdd..db95e0703 100644 --- a/src/helpers/heritage-integration/order-helper.js +++ b/src/helpers/heritage-integration/order-helper.js @@ -1,5 +1,9 @@ import { storeActions } from "@/constants/store-actions.js"; -import { getFunnelCookie, updateOrCreateFunnelCookie, deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js"; +import { + getFunnelCookie, + updateOrCreateFunnelCookie, + deleteFunnelCookie, +} from "@/helpers/heritage-integration/cookie-helper.js"; import baseMixin from "@/mixins/base-mixin"; import store from "@/store"; import { storeMutations } from "@/constants/store-mutations"; @@ -12,9 +16,13 @@ import { storeMutations } from "@/constants/store-mutations"; */ export async function loadSessionIfPresent() { const funnelCookie = getFunnelCookie(); - + // Do nothing if there is no cookie, correlation id, or referral number. - if (funnelCookie == null || funnelCookie.ReferralCorrelationId == null || !funnelCookie.ReferralNumber) { + if ( + funnelCookie == null || + funnelCookie.ReferralCorrelationId == null || + !funnelCookie.ReferralNumber + ) { return null; } @@ -26,7 +34,14 @@ export async function loadSessionIfPresent() { } // Load referral if there is a cookie, and it doesn't indicate it needs a state reset. - return (await loadSession(funnelCookie.ReferralNumber, funnelCookie.ReferralDate, funnelCookie.ReferralCorrelationId, funnelCookie.ReferralParentAccountNumber)).data; + return ( + await loadSession( + funnelCookie.ReferralNumber, + funnelCookie.ReferralDate, + funnelCookie.ReferralCorrelationId, + funnelCookie.ReferralParentAccountNumber + ) + ).data; } /* @@ -37,7 +52,7 @@ export async function loadSessionIfPresent() { export async function saveSession() { var saveSessionPromise; if (store.getters.applicationUser.saveSessionPromise) { - // queue newest request after current saveSessionPromise resolves + // queue newest request after current saveSessionPromise resolves saveSessionPromise = store.getters.applicationUser.saveSessionPromise.then(() => { // get a new saveSessionPromise return saveSessionHelper(); @@ -51,7 +66,6 @@ export async function saveSession() { await saveSessionPromise; } - // PRIVATE FUNCTIONS // /* @@ -61,13 +75,16 @@ export async function saveSession() { async function loadSession(referralNumber, referralDate, referralCorrelationId, accountNumber) { // await the saveSessionPromise in the store to make sure we're loading up to date information await store.getters.applicationUser.saveSessionPromise; - const response = await baseMixin.methods.dispatchStoreAction(storeActions.LOAD_SESSION, + const response = await baseMixin.methods.dispatchStoreAction( + storeActions.LOAD_SESSION, { referralNumber: referralNumber.toString(), referralDate: referralDate, referralCorrelationId: referralCorrelationId, - accountNumber: accountNumber?.toString() - }, false); + accountNumber: accountNumber?.toString(), + }, + false + ); return response; } @@ -78,15 +95,19 @@ async function loadSession(referralNumber, referralDate, referralCorrelationId, async function saveSessionHelper() { const savedSessionInfo = await baseMixin.methods.dispatchStoreAction(storeActions.SAVE_SESSION); // Update the store with information received from the saveSession response - await baseMixin.methods.dispatchStoreAction(storeActions.UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE, { - referralNumber: savedSessionInfo.data.referralNumber.toString(), - referralCorrelationId: savedSessionInfo.data.referralCorrelationId, - referralDate: savedSessionInfo.data.referralDate, - accountNumber: savedSessionInfo.data.accountNumber.toString(), - savedSessionId: savedSessionInfo.data.savedSessionId, - crmCustomerId: savedSessionInfo.data.crmCustomerId.toString(), - }, false); + await baseMixin.methods.dispatchStoreAction( + storeActions.UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE, + { + referralNumber: savedSessionInfo.data.referralNumber.toString(), + referralCorrelationId: savedSessionInfo.data.referralCorrelationId, + referralDate: savedSessionInfo.data.referralDate, + accountNumber: savedSessionInfo.data.accountNumber.toString(), + savedSessionId: savedSessionInfo.data.savedSessionId, + crmCustomerId: savedSessionInfo.data.crmCustomerId.toString(), + }, + false + ); // Update the cookie with the referral information when saved. updateOrCreateFunnelCookie(); -} \ No newline at end of file +} diff --git a/src/helpers/heritage-integration/order-helper.spec.js b/src/helpers/heritage-integration/order-helper.spec.js index 5e181a402..5ec0f4f69 100644 --- a/src/helpers/heritage-integration/order-helper.spec.js +++ b/src/helpers/heritage-integration/order-helper.spec.js @@ -1,12 +1,16 @@ import * as cookieHelper from "@/helpers/heritage-integration/cookie-helper"; import { loadSessionIfPresent, saveSession } from "@/helpers/heritage-integration/order-helper"; import { cookieNames } from "@/constants/cookie-names"; -import { setupMocksForJsFiles, removeAllTestCookies, getMockOrderInfo, setupCookies } from "@/helpers/unit-test-helper.js"; +import { + setupMocksForJsFiles, + removeAllTestCookies, + getMockOrderInfo, + setupCookies, +} from "@/helpers/unit-test-helper.js"; import { storeActions } from "@/constants/store-actions"; import router from "@/router"; describe("loadSessionIfPresent", () => { - afterEach(() => { removeAllTestCookies(); }); @@ -19,10 +23,12 @@ describe("loadSessionIfPresent", () => { const testCookieValue = { ShouldResetState: testShouldResetState, ReferralCorrelationId: "xxx", - ReferralNumber: "12345" - } + ReferralNumber: "12345", + }; - document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=${JSON.stringify(testCookieValue)}; path=/; ${cookieHelper.getCookieDomainValue()}`; + document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=${JSON.stringify( + testCookieValue + )}; path=/; ${cookieHelper.getCookieDomainValue()}`; // Act loadSessionIfPresent(); @@ -34,17 +40,21 @@ describe("loadSessionIfPresent", () => { test("ShouldResetState == true => reset store", () => { // Arrange - cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie").mockReturnValueOnce({ - ShouldResetState: true, - ReferralCorrelationId: "xxx-xxx-xxx", - ReferralNumber: "12345" - }); + cookieHelper.getFunnelCookie = jest + .spyOn(cookieHelper, "getFunnelCookie") + .mockReturnValueOnce({ + ShouldResetState: true, + ReferralCorrelationId: "xxx-xxx-xxx", + ReferralNumber: "12345", + }); const mockData = { - actionList: [{ - actionName: storeActions.RESET_STATE - }], - } + actionList: [ + { + actionName: storeActions.RESET_STATE, + }, + ], + }; var mocks = setupMocksForJsFiles(mockData); @@ -53,18 +63,24 @@ describe("loadSessionIfPresent", () => { // Assert expect(cookieHelper.getFunnelCookie).toHaveBeenCalled(); - expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(storeActions.RESET_STATE); + expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith( + storeActions.RESET_STATE + ); }); test("Funnel cookie is null => store is unchanged", () => { // Arrange - cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie").mockReturnValueOnce(null); + cookieHelper.getFunnelCookie = jest + .spyOn(cookieHelper, "getFunnelCookie") + .mockReturnValueOnce(null); const mockData = { - actionList: [{ - actionName: storeActions.RESET_STATE - }], - } + actionList: [ + { + actionName: storeActions.RESET_STATE, + }, + ], + }; var mocks = setupMocksForJsFiles(mockData); @@ -73,20 +89,30 @@ describe("loadSessionIfPresent", () => { // Assert expect(cookieHelper.getFunnelCookie).toHaveBeenCalled(); - expect(mocks.baseMixin.methods.dispatchStoreAction).not.toHaveBeenCalledWith(storeActions.RESET_STATE); + expect(mocks.baseMixin.methods.dispatchStoreAction).not.toHaveBeenCalledWith( + storeActions.RESET_STATE + ); }); test("Funnel cookie valid, should call loadSession", async () => { // Arrange - cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie") - .mockReturnValueOnce({ ShouldResetState: false, ReferralNumber: 123456, ReferralCorrelationId: "yyy-yyy-yyyy", ReferralDate: new Date()}); + cookieHelper.getFunnelCookie = jest + .spyOn(cookieHelper, "getFunnelCookie") + .mockReturnValueOnce({ + ShouldResetState: false, + ReferralNumber: 123456, + ReferralCorrelationId: "yyy-yyy-yyyy", + ReferralDate: new Date(), + }); const mockData = { - actionList: [{ - actionName: storeActions.LOAD_SESSION, - data: { ReferralNumber: 123456, vehicle: { year: 2010 } } - }], - } + actionList: [ + { + actionName: storeActions.LOAD_SESSION, + data: { ReferralNumber: 123456, vehicle: { year: 2010 } }, + }, + ], + }; var mocks = setupMocksForJsFiles(mockData); @@ -95,7 +121,9 @@ describe("loadSessionIfPresent", () => { // Assert expect(cookieHelper.getFunnelCookie).toHaveBeenCalled(); - expect(mocks.baseMixin.methods.dispatchStoreAction).not.toHaveBeenCalledWith(storeActions.LOAD_SESSION); + expect(mocks.baseMixin.methods.dispatchStoreAction).not.toHaveBeenCalledWith( + storeActions.LOAD_SESSION + ); expect(result.ReferralNumber).toBe(123456); expect(result.vehicle.year).toBe(2010); }); @@ -113,9 +141,16 @@ describe("saveSession", () => { const mockReferralDate = "2022"; const mockAccountNumber = "167132"; const mockSavedSessionId = "xxx-xxx-xxx"; - const mockCrmCustomerId = "xxx-xxx-xxx" + const mockCrmCustomerId = "xxx-xxx-xxx"; - const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, mockAccountNumber, mockSavedSessionId, mockCrmCustomerId); + const mockOrderInfo = getMockOrderInfo( + mockReferralNumber, + mockCorrelationId, + mockReferralDate, + mockAccountNumber, + mockSavedSessionId, + mockCrmCustomerId + ); const mockData = { actionList: [ @@ -124,10 +159,10 @@ describe("saveSession", () => { data: mockOrderInfo, }, { - actionName: storeActions.UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE - } - ] - } + actionName: storeActions.UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE, + }, + ], + }; const mocks = setupMocksForJsFiles(mockData); @@ -135,15 +170,21 @@ describe("saveSession", () => { await saveSession(); // Assert - expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(storeActions.SAVE_SESSION); - expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(storeActions.UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE, { - referralNumber: mockReferralNumber, - referralDate: mockReferralDate, - referralCorrelationId: mockCorrelationId, - accountNumber: mockAccountNumber, - savedSessionId: mockSavedSessionId, - crmCustomerId: mockCrmCustomerId, - }, false); + expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith( + storeActions.SAVE_SESSION + ); + expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith( + storeActions.UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE, + { + referralNumber: mockReferralNumber, + referralDate: mockReferralDate, + referralCorrelationId: mockCorrelationId, + accountNumber: mockAccountNumber, + savedSessionId: mockSavedSessionId, + crmCustomerId: mockCrmCustomerId, + }, + false + ); }); test("saveSession => should update DidHeritageFunnelUpdateLast cookie value to false", async () => { @@ -153,16 +194,25 @@ describe("saveSession", () => { const mockReferralCorrelationId = "404d2b04-f86e-45c3-b373-127b6217b060"; const mockAccountNumber = "167132"; const mockSavedSessionId = "xxx-xxx-xxx"; - const mockCrmCustomerId = "xxx-xxx-xxx" + const mockCrmCustomerId = "xxx-xxx-xxx"; - const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockReferralCorrelationId, mockReferralDate, mockAccountNumber, mockSavedSessionId, mockCrmCustomerId); + const mockOrderInfo = getMockOrderInfo( + mockReferralNumber, + mockReferralCorrelationId, + mockReferralDate, + mockAccountNumber, + mockSavedSessionId, + mockCrmCustomerId + ); const mockData = { - actionList: [{ - actionName: storeActions.SAVE_SESSION, - data: mockOrderInfo, - }], - router: router + actionList: [ + { + actionName: storeActions.SAVE_SESSION, + data: mockOrderInfo, + }, + ], + router: router, }; setupMocksForJsFiles(mockData); @@ -172,8 +222,8 @@ describe("saveSession", () => { ReferralDate: mockReferralDate, ReferralCorrelationId: mockReferralCorrelationId, ShouldResetState: false, - DidHeritageFunnelUpdateLast: true - } + DidHeritageFunnelUpdateLast: true, + }; setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); diff --git a/src/helpers/heritage-integration/session-helper.js b/src/helpers/heritage-integration/session-helper.js index b71602f3c..291ebfaf8 100644 --- a/src/helpers/heritage-integration/session-helper.js +++ b/src/helpers/heritage-integration/session-helper.js @@ -1,5 +1,5 @@ import { applicationConfig } from "@/constants/application-config"; -import { getFunnelCookie} from "@/helpers/heritage-integration/cookie-helper.js"; +import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js"; /* Method to determine if our analytics session has timed out or not. @@ -9,7 +9,8 @@ export function isAnalyticsSessionStillActive() { if (getFunnelCookie() !== null) { const lastTouchedValue = getFunnelCookie().LastTouched; const timeoutAmount = applicationConfig.ANALYTICS_SESSION_TIMEOUT_MINUTES; - const isMoreThanHalfHourAgo = ((new Date() - new Date(lastTouchedValue)) / 60000) > timeoutAmount; + const isMoreThanHalfHourAgo = + (new Date() - new Date(lastTouchedValue)) / 60000 > timeoutAmount; if (isMoreThanHalfHourAgo) { return false; @@ -29,7 +30,7 @@ export function isAnalyticsSessionStillActive() { export function isSavedSessionStillActive() { if (getFunnelCookie() !== null) { const savedSessionTimeStamp = new Date(getFunnelCookie().SavedSessionTimeoutDate); - const isSavedSessionTimedOut = (new Date(new Date().toUTCString()) > savedSessionTimeStamp); + const isSavedSessionTimedOut = new Date(new Date().toUTCString()) > savedSessionTimeStamp; return !isSavedSessionTimedOut; } @@ -40,7 +41,7 @@ Function to calculate the date for the saved session timeout. */ export function getDateForSavedSessionTimeout() { - const currentDate = new Date(new Date().toUTCString()) - currentDate.setDate(currentDate.getDate() + applicationConfig.SAVED_SESSION_TIMEOUT_DAYS) + const currentDate = new Date(new Date().toUTCString()); + currentDate.setDate(currentDate.getDate() + applicationConfig.SAVED_SESSION_TIMEOUT_DAYS); return currentDate.toUTCString(); -} \ No newline at end of file +} diff --git a/src/helpers/heritage-integration/session-helper.spec.js b/src/helpers/heritage-integration/session-helper.spec.js index b41827bf7..fe3e308da 100644 --- a/src/helpers/heritage-integration/session-helper.spec.js +++ b/src/helpers/heritage-integration/session-helper.spec.js @@ -1,14 +1,19 @@ import * as cookieHelper from "@/helpers/heritage-integration/cookie-helper"; -import { isAnalyticsSessionStillActive, isSavedSessionStillActive, getDateForSavedSessionTimeout} from "@/helpers/heritage-integration/session-helper"; +import { + isAnalyticsSessionStillActive, + isSavedSessionStillActive, + getDateForSavedSessionTimeout, +} from "@/helpers/heritage-integration/session-helper"; import { applicationConfig } from "@/constants/application-config"; describe("isAnalyticsSessionStillActive", () => { test("isAnalyticsSessionStillActive, should return true", () => { // Arrange - const mockDate = new Date(new Date().toUTCString()) - mockDate.setDate(mockDate.getDate() + 1) + const mockDate = new Date(new Date().toUTCString()); + mockDate.setDate(mockDate.getDate() + 1); - cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie") + cookieHelper.getFunnelCookie = jest + .spyOn(cookieHelper, "getFunnelCookie") .mockReturnValue({ LastTouched: mockDate }); // Act @@ -20,10 +25,11 @@ describe("isAnalyticsSessionStillActive", () => { test("isAnalyticsSessionStillActive, should return false", () => { // Arrange - const mockDate = new Date(new Date().toUTCString()) - mockDate.setDate(mockDate.getDate() - 1) + const mockDate = new Date(new Date().toUTCString()); + mockDate.setDate(mockDate.getDate() - 1); - cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie") + cookieHelper.getFunnelCookie = jest + .spyOn(cookieHelper, "getFunnelCookie") .mockReturnValue({ LastTouched: mockDate }); // Act @@ -37,10 +43,11 @@ describe("isAnalyticsSessionStillActive", () => { describe("isSavedSessionStillActive", () => { test("isSavedSessionStillActive, should return true", () => { // Arrange - const mockDate = new Date(new Date().toUTCString()) + const mockDate = new Date(new Date().toUTCString()); mockDate.setDate(mockDate.getDate() + 1); - - cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie") + + cookieHelper.getFunnelCookie = jest + .spyOn(cookieHelper, "getFunnelCookie") .mockReturnValue({ SavedSessionTimeoutDate: mockDate }); // Act @@ -52,10 +59,11 @@ describe("isSavedSessionStillActive", () => { test("isSavedSessionStillActive, should return false", () => { // Arrange - const mockDate = new Date(new Date().toUTCString()) - mockDate.setDate(mockDate.getDate() - 1) + const mockDate = new Date(new Date().toUTCString()); + mockDate.setDate(mockDate.getDate() - 1); - cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie") + cookieHelper.getFunnelCookie = jest + .spyOn(cookieHelper, "getFunnelCookie") .mockReturnValue({ SavedSessionTimeoutDate: mockDate }); // Act @@ -63,20 +71,19 @@ describe("isSavedSessionStillActive", () => { // Assert expect(result).toBe(false); - }); }); describe("getDateForSavedSessionTimeout", () => { - test("getDateForSavedSessionTimeout, should equal application config setting", () =>{ + test("getDateForSavedSessionTimeout, should equal application config setting", () => { // Arrange - const currentDate = new Date(new Date().toUTCString()) - currentDate.setDate(currentDate.getDate() + applicationConfig.SAVED_SESSION_TIMEOUT_DAYS) - + const currentDate = new Date(new Date().toUTCString()); + currentDate.setDate(currentDate.getDate() + applicationConfig.SAVED_SESSION_TIMEOUT_DAYS); + // Act const result = getDateForSavedSessionTimeout(); // Assert expect(result).toEqual(currentDate.toUTCString()); }); -}) \ No newline at end of file +}); diff --git a/src/helpers/layout-helper.js b/src/helpers/layout-helper.js index 0a8177e25..a07da4ff6 100644 --- a/src/helpers/layout-helper.js +++ b/src/helpers/layout-helper.js @@ -1,27 +1,27 @@ export function settleAllPromises(promiseResultMap) { - // Pull our keys out of the promise 'table' - const promiseNames = Object.entries(promiseResultMap); + // Pull our keys out of the promise 'table' + const promiseNames = Object.entries(promiseResultMap); - return Promise.allSettled( - promiseNames.map((e) => e[1]).map((n) => n.promise) - ).then((results) => { - const resultMap = {}; + return Promise.allSettled(promiseNames.map((e) => e[1]).map((n) => n.promise)).then( + (results) => { + const resultMap = {}; - // Build a map of the results - for (let i = 0; i < results.length; ++i) { - const promiseName = promiseNames[i][1].resultKey; + // Build a map of the results + for (let i = 0; i < results.length; ++i) { + const promiseName = promiseNames[i][1].resultKey; - // Some Promises like the cms content call don't have a 'data' field - // when returned, so other promises do. Map the results to the object - // so that the object is the return data. + // Some Promises like the cms content call don't have a 'data' field + // when returned, so other promises do. Map the results to the object + // so that the object is the return data. - if (results[i]?.value?.data === undefined) { - resultMap[promiseName] = results[i]?.value; - } else { - resultMap[promiseName] = results[i]?.value?.data; - } - } + if (results[i]?.value?.data === undefined) { + resultMap[promiseName] = results[i]?.value; + } else { + resultMap[promiseName] = results[i]?.value?.data; + } + } - return resultMap; - }); + return resultMap; + } + ); } diff --git a/src/helpers/layout-helper.spec.js b/src/helpers/layout-helper.spec.js index d71810756..696a5d907 100644 --- a/src/helpers/layout-helper.spec.js +++ b/src/helpers/layout-helper.spec.js @@ -1,25 +1,25 @@ import { settleAllPromises } from "@/helpers/layout-helper"; it("layout-helper: Should settle all promises and return mapped promise results", () => { - // Arrange - const mockPromiseOne = Promise.resolve({ data: "test-data" }); - const mockPromiseTwo = Promise.resolve({ data: "test-data-two" }); + // Arrange + const mockPromiseOne = Promise.resolve({ data: "test-data" }); + const mockPromiseTwo = Promise.resolve({ data: "test-data-two" }); - const promiseResultMap = [ - { - resultKey: "MockResultOne", - promise: mockPromiseOne, - }, - { - resultKey: "MockResultTwo", - promise: mockPromiseTwo, - }, - ]; + const promiseResultMap = [ + { + resultKey: "MockResultOne", + promise: mockPromiseOne, + }, + { + resultKey: "MockResultTwo", + promise: mockPromiseTwo, + }, + ]; - // Act - settleAllPromises(promiseResultMap).then((results) => { - // Assert - expect(results.MockResultOne).toEqual("test-data"); - expect(results.MockResultTwo).toEqual("test-data-two"); - }); + // Act + settleAllPromises(promiseResultMap).then((results) => { + // Assert + expect(results.MockResultOne).toEqual("test-data"); + expect(results.MockResultTwo).toEqual("test-data-two"); + }); }); diff --git a/src/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js index cc00e49d9..5e03d4c99 100644 --- a/src/helpers/unit-test-helper.js +++ b/src/helpers/unit-test-helper.js @@ -6,124 +6,138 @@ import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { cookieNames } from "@/constants/cookie-names"; import { Form } from "vee-validate"; import baseMixin from "@/mixins/base-mixin"; -import { getCookieDomainValue, setCookieProperties } from "@/helpers/heritage-integration/cookie-helper"; -import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes } from "@/constants/analytics"; +import { + getCookieDomainValue, + setCookieProperties, +} from "@/helpers/heritage-integration/cookie-helper"; +import { + analyticsPageEvents, + GaCategories, + GaActions, + GaLabels, + GaEvents, + ValueToLogTypes, +} from "@/constants/analytics"; import { queryStrings } from "@/constants/query-strings"; import { routerParams } from "@/router/router-constants/router-params"; // Common methods export function getMountOptions(mockData) { - // Define our mocks to attached to the 'global' object for Vue/Jest. - const mocks = {}; + // 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.dispatchStoreAction) vs this.dispatchStoreAction - setupBaseMixinDispatchStoreAction(mockData); + //this is mocking if you use the mixin directly(baseMixin.methods.dispatchStoreAction) vs this.dispatchStoreAction + setupBaseMixinDispatchStoreAction(mockData); - mocks.pushEventToGA = jest.fn(); - mocks.pushPageViewToGA = jest.fn(); - mocks.logEvent = jest.fn(); - mocks.pushExperimentsToDataLayer = jest.fn(); - mocks.prependActionToMethod = jest.fn(); - mocks.dispatchStoreAction = jest.fn(); - mocks.dispatchStoreAction.mockImplementation((actionName) => { - let actionFilterResult = mockData.actionList?.filter( - (x) => x.actionName == actionName - ); + mocks.pushEventToGA = jest.fn(); + mocks.pushPageViewToGA = jest.fn(); + mocks.logEvent = jest.fn(); + mocks.pushExperimentsToDataLayer = jest.fn(); + mocks.prependActionToMethod = jest.fn(); + mocks.dispatchStoreAction = jest.fn(); + mocks.dispatchStoreAction.mockImplementation((actionName) => { + let actionFilterResult = mockData.actionList?.filter((x) => x.actionName == actionName); - if (actionFilterResult?.length === 1) { - return Promise.resolve({ - data: actionFilterResult[0].data, - }); - } - }); + if (actionFilterResult?.length === 1) { + return Promise.resolve({ + data: actionFilterResult[0].data, + }); + } + }); - // Mock const files - mocks.storeActions = storeActions; - mocks.storeMutations = storeMutations; - mocks.navigationScenarios = navigationScenarios; - mocks.vehicleCategories = vehicleCategories; - mocks.fmgPageValues = fmgPageValues; - mocks.analyticsPageEvents = analyticsPageEvents; - mocks.GaCategories = GaCategories; - mocks.GaActions = GaActions; - mocks.GaLabels = GaLabels; - mocks.GaEvents = GaEvents; - mocks.ValueToLogTypes = ValueToLogTypes; - mocks.queryStrings = queryStrings; - mocks.routerParams = routerParams; + // Mock const files + mocks.storeActions = storeActions; + mocks.storeMutations = storeMutations; + mocks.navigationScenarios = navigationScenarios; + mocks.vehicleCategories = vehicleCategories; + mocks.fmgPageValues = fmgPageValues; + mocks.analyticsPageEvents = analyticsPageEvents; + mocks.GaCategories = GaCategories; + mocks.GaActions = GaActions; + mocks.GaLabels = GaLabels; + mocks.GaEvents = GaEvents; + mocks.ValueToLogTypes = ValueToLogTypes; + mocks.queryStrings = queryStrings; + mocks.routerParams = routerParams; - // Mock $store and $router when accessing this.$store/$router - mocks.$store = mockData.store; - mocks.$router = mockData.router; - mocks.$route = mockData.route; - mocks.$loadScript = mockData.loadScript; + // Mock $store and $router when accessing this.$store/$router + mocks.$store = mockData.store; + mocks.$router = mockData.router; + mocks.$route = mockData.route; + mocks.$loadScript = mockData.loadScript; - const global = { - mocks: mocks, - mixins: mockData.mixins, - stubs: { Form } - }; + const global = { + mocks: mocks, + mixins: mockData.mixins, + stubs: { Form }, + }; - return { global }; + return { global }; } export function setupMocksForJsFiles(mockData = {}) { - setupBaseMixinDispatchStoreAction(mockData); + setupBaseMixinDispatchStoreAction(mockData); - return { baseMixin }; + return { baseMixin }; } // Heritage integration common methods export const cookies = { - [cookieNames.FUNNEL_SESSION_INFO]: `{"ReferralNumber":"1566818","ReferralDate":"2022-03-15T10:56:24.597","ReferralCorrelationId":"404d2b04-f86e-45c3-b373-127b6217b060","ShouldResetState":false,"DidHeritageFunnelUpdateLast":true}`, - "UNIQUE_SESSION_ID": "33756020-b58e-4ec7-b8b8-3f1576719c40", - "anotherCookie": "{}", - "someOtherCookie": "{}", - "dxdev": "did=21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe", - "sid": "cba0c3d1-3c1b-4305-bb56-31aa50f58e27", - "skey": "12345" + [cookieNames.FUNNEL_SESSION_INFO]: `{"ReferralNumber":"1566818","ReferralDate":"2022-03-15T10:56:24.597","ReferralCorrelationId":"404d2b04-f86e-45c3-b373-127b6217b060","ShouldResetState":false,"DidHeritageFunnelUpdateLast":true}`, + UNIQUE_SESSION_ID: "33756020-b58e-4ec7-b8b8-3f1576719c40", + anotherCookie: "{}", + someOtherCookie: "{}", + dxdev: "did=21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe", + sid: "cba0c3d1-3c1b-4305-bb56-31aa50f58e27", + skey: "12345", }; export function removeAllTestCookies() { - Object.keys(cookies).forEach(key => { - document.cookie = `${key}=;Max-Age=0;`; - document.cookie = `${key}=;Max-Age=0;path=/;${getCookieDomainValue()}`; - }); + Object.keys(cookies).forEach((key) => { + document.cookie = `${key}=;Max-Age=0;`; + document.cookie = `${key}=;Max-Age=0;path=/;${getCookieDomainValue()}`; + }); } -export function getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, accountNumber = "0", savedSessionId, crmCustomerId) { - return { - referralNumber: mockReferralNumber, - referralCorrelationId: mockCorrelationId, - referralDate: mockReferralDate, - accountNumber: accountNumber, - savedSessionId: savedSessionId, - crmCustomerId: crmCustomerId, - } +export function getMockOrderInfo( + mockReferralNumber, + mockCorrelationId, + mockReferralDate, + accountNumber = "0", + savedSessionId, + crmCustomerId +) { + return { + referralNumber: mockReferralNumber, + referralCorrelationId: mockCorrelationId, + referralDate: mockReferralDate, + accountNumber: accountNumber, + savedSessionId: savedSessionId, + crmCustomerId: crmCustomerId, + }; } export function setupCookies({ funnelCookieValue = "", includeHeritageCookie = true }) { - Object.keys(cookies).forEach(key => { - const cookieValue = key == cookieNames.FUNNEL_SESSION_INFO ? funnelCookieValue : cookies[key]; - if (includeHeritageCookie || key != cookieNames.FUNNEL_SESSION_INFO) - setCookieProperties({ [key]: cookieValue }, { isSecure: false }); - }); + Object.keys(cookies).forEach((key) => { + const cookieValue = + key == cookieNames.FUNNEL_SESSION_INFO ? funnelCookieValue : cookies[key]; + if (includeHeritageCookie || key != cookieNames.FUNNEL_SESSION_INFO) + setCookieProperties({ [key]: cookieValue }, { isSecure: false }); + }); } // Private methods function setupBaseMixinDispatchStoreAction(mockData) { - if (mockData.actionList !== undefined) { - baseMixin.methods.dispatchStoreAction = jest.fn(); - baseMixin.methods.dispatchStoreAction.mockImplementation((actionName) => { - let actionFilterResult = mockData.actionList.filter( - (x) => x.actionName == actionName - ); + if (mockData.actionList !== undefined) { + baseMixin.methods.dispatchStoreAction = jest.fn(); + baseMixin.methods.dispatchStoreAction.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, + if (actionFilterResult.length > 0 && actionFilterResult.length === 1) { + return Promise.resolve({ + data: actionFilterResult[0].data, + }); + } }); - } - }); - } -} \ No newline at end of file + } +} diff --git a/src/helpers/validation-rules.js b/src/helpers/validation-rules.js index 9b2e14d58..0ef03cbe3 100644 --- a/src/helpers/validation-rules.js +++ b/src/helpers/validation-rules.js @@ -4,7 +4,7 @@ export function required(errorMessage) { return errorMessage; } return true; - }; + }; } export function regex(expression, errorMessage) { @@ -20,6 +20,5 @@ export function regex(expression, errorMessage) { } return true; - } - -} \ No newline at end of file + }; +} diff --git a/src/helpers/validation-rules.spec.js b/src/helpers/validation-rules.spec.js index 7e0c6304e..223727d37 100644 --- a/src/helpers/validation-rules.spec.js +++ b/src/helpers/validation-rules.spec.js @@ -2,71 +2,66 @@ import { required } from "@/helpers/validation-rules"; import { regex } from "@/helpers/validation-rules"; describe("validation-rules.vue", () => { - test("required rules should return error if value missing", () => { + test("required rules should return error if value missing", () => { + //Arrange + const testFn = required("an error"); - //Arrange - const testFn = required("an error"); - - //Act - const testResponse = testFn(); + //Act + const testResponse = testFn(); - //Assert - expect(testResponse).toBe("an error"); - }); + //Assert + expect(testResponse).toBe("an error"); + }); }); describe("validation-rules.vue", () => { - test("required rules should return true if value present", () => { + test("required rules should return true if value present", () => { + //Arrange + const testFn = required("an error"); - //Arrange - const testFn = required("an error"); - - //Act - const testResponse = testFn('some value'); - - //Assert - expect(testResponse).toBe(true); - }); + //Act + const testResponse = testFn("some value"); + + //Assert + expect(testResponse).toBe(true); + }); }); describe("validation-rules.vue", () => { - test("regex rules should return true if value is not present", () => { + test("regex rules should return true if value is not present", () => { + //Arrange + const testFn = regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, "an error"); // Using Zip regex - //Arrange - const testFn = regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, "an error"); // Using Zip regex - - //Act - const testResponse = testFn(); - - //Assert - expect(testResponse).toBe(true); - }); + //Act + const testResponse = testFn(); + + //Assert + expect(testResponse).toBe(true); + }); }); describe("validation-rules.vue", () => { - test("regex rules should return false if value is present but does not match regular expression", () => { + test("regex rules should return false if value is present but does not match regular expression", () => { + //Arrange + const testFn = regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, "an error"); // Using Zip regex - //Arrange - const testFn = regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, "an error"); // Using Zip regex - - //Act - const testResponse = testFn('4321'); // needs to be 5 numbers - - //Assert - expect(testResponse).toBe("an error"); - }); + //Act + const testResponse = testFn("4321"); // needs to be 5 numbers + + //Assert + expect(testResponse).toBe("an error"); + }); }); describe("validation-rules.vue", () => { - test("regex rules should return true if value is present and does match regular expression", () => { + test("regex rules should return true if value is present and does match regular expression", () => { + //Arrange + const testFn = regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, "an error"); // Using Zip regex - //Arrange - const testFn = regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, "an error"); // Using Zip regex - - //Act - const testResponse = testFn('43213'); // needs to be 5 numbers - - //Assert - expect(testResponse).toBe(true); - }); -}); \ No newline at end of file + //Act + const testResponse = testFn("43213"); // needs to be 5 numbers + + //Assert + expect(testResponse).toBe(true); + }); +}); diff --git a/src/layouts/address-lookup/address-lookup.spec.js b/src/layouts/address-lookup/address-lookup.spec.js index 2f17cd220..bd8689298 100644 --- a/src/layouts/address-lookup/address-lookup.spec.js +++ b/src/layouts/address-lookup/address-lookup.spec.js @@ -11,665 +11,720 @@ import { navigationScenarios } from "@/router/router-constants/navigation-scenar import store from "@/store"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; - jest.mock("@/helpers/damage-helper", () => ({ - isGlassAvailableForCarId: jest.fn().mockImplementation(() => true), - getDamageString: jest.fn() + isGlassAvailableForCarId: jest.fn().mockImplementation(() => true), + getDamageString: jest.fn(), })); jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({ - navigateToHeritageFunnel: jest.fn() + navigateToHeritageFunnel: jest.fn(), })); // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ - settleAllPromises: jest.fn(), + settleAllPromises: jest.fn(), })); describe("address-lookup.vue", () => { - describe("page level alerts", () => { - test("if the address is not serviceable display the Non-Serviceable Zip Alert", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipValid: true, - isZipServiceable: false, - vinVehicles: [{ - vin: "TEST_VIN", - vehicle: { - carId: "C0000" - } - }] - }); - - store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); - - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - }, - }) - - // Act - await wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe(true); - - }); - - test("if the address matches a different vehicle display the Matched Different VehicleAlert", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipServiceable: true, - vinVehicles: [ - { - vehicle: { - carId: "C00000" - } - } - ] - }); - - store.commit(storeMutations.UPDATE_CAR_ID, "CARID2"); - - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - }, - }) - - // Act - await wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.findComponent({ ref: "alertMatchedDifferentVehicle" }).isVisible()).toBe(true); - }); - - test("if the looking up VIN by address is not allowed in the state selected display the Vin Lookup By HomeAddress Not Allowed Alert", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipServiceable: true, - isStatePermissible: false, - lookupVinbyAddressResponse: { - isStatePermissible: false, - vinVehicles: [{ - vin: "TEST_VIN", - vehicle: { - carId: "CARID" - } - }, - { - vin: "TEST_VIN2", - vehicle: { - carId: "CARID2" - } - }] - } - }); - - store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); - - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - }, - }) - - // Act - await wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.findComponent({ ref: "alertVinLookupsByHomeAddressNotAllowed" }).isVisible()).toBe(true); - - }); - - test("if no vehicles found, display Vin Not Found alert", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipServiceable: true, - lookupVinbyAddressResponse: { - isStatePermissible: true, - vinVehicles: [] // Return no vehicles - } - }); - - store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); - - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - }, - }) - - wrapper.vm.navigateForward = jest.fn(); - - // Act - await wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.findComponent({ ref: "alertVinNotFound" }).isVisible()).toBe(true); - - }); - }); - - describe("navigation", () => { - - test("if the back button is clicked, navigate back", async () => { - // Arrange - const { wrapper } = setupMocks({ - isZipServiceable: true - }); - - // Act - await wrapper.vm.backButtonAction(); - - // Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); - - }); - - test("if the car entered matches one of the vehicles found and the zip is serviceable, navigate forward", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipServiceable: true, - vinVehicles: [ - { - vehicle: { - carId: "C11111" - } - } - ] - }); - - await wrapper.setData({ - previouslyEnteredCarId: "C11111", - customerQuestions: { - addressQuestions: mockRegistrationAddress - }, - }) - - wrapper.vm.navigateForward = jest.fn(); - - // Act - await wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.navigateForward).toHaveBeenCalled(); - }); - - test("if the car entered does not match any of the multiple vehicles found, navigate to address-vehicles page", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipServiceable: true, - isStatePermissible: true, - vinVehicles: [{ - vin: "TEST_VIN", - vehicle: { - carId: "CARID" - } - }, - { - vin: "TEST_VIN2", - vehicle: { - carId: "CARID2" - } - }] - }); - - store.commit(storeMutations.UPDATE_CAR_ID, "CARID_A"); - - const carsFound = [{ - vin: "TEST_VIN", - vehicle: { - carId: "CARID" - } - }, - { - vin: "TEST_VIN2", - vehicle: { - carId: "CARID2" - } - }] - - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - }, - }) - - wrapper.vm.updateVehicleInfo = jest.fn(); - - // Act - await wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES, undefined, {}, {}, carsFound); - }); - - test("if the car entered matches one of the vehicles found but the zip is NOT serviceable, do not navigate forward", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipServiceable: false, - isStatePermissible: true, - vinVehicles: [{ - vin: "TEST_VIN", - vehicle: { - carId: "CARID" - } - }, - { - vin: "TEST_VIN2", - vehicle: { - carId: "CARID2" - } - }] - }); - - store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); - - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - }, - }) - - wrapper.vm.navigateForward = jest.fn(); - - // Act - await wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.navigateForward).toHaveBeenCalledTimes(0); - - }); - - test("if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipServiceable: true, - isStatePermissible: true - }); - - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - }, - isCarIdDifferent: true, - isSelectedGlassAvailableForVehicle: false, - }) - - let carsFound = [{ - vin: "TEST_VIN2", - vehicle: { - carId: "C0000" - } - }]; - - // Act - await wrapper.vm.navigateForward(carsFound); - - // Assert - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, undefined, {}, { "displayVehicleChangeAlert": true }); - - }); - - test("single car was found and matches entered vehicle => navigateForwardWithSingleCarMatch", async () => { - // Arrange - - const carsFound = [ - { - vin: "TEST_VIN_2", - vehicle: { - carId: "C0000" - } - } - ]; - - const { wrapper } = setupMocks({}, {}); - wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); - - // Act - wrapper.vm.navigateForward(carsFound); - - // Assert - expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1); - }); - - test("multiple cars were found and one matches entered vehicle => navigateForwardWithSingleCarMatch", async () => { - // Arrange - const carsFound = [ - { - vin: "TEST_VIN_1", - vehicle: { - carId: "C0000" - } - }, - { - vin: "TEST_VIN_2", - vehicle: { - carId: "CARID2" - } - }, - { - vin: "TEST_VIN_3", - vehicle: { - carId: "CARID3" - } - } - ]; - - const { wrapper } = setupMocks({}); - wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); - - // Act - wrapper.vm.navigateForward(carsFound); - - // Assert - expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1); - }); - }); - - describe("registration and service zips", () => { - describe("if registration zip is serviceable", () => { - test("if registration address is provided => update service address on successful continue", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipServiceable: true + describe("page level alerts", () => { + test("if the address is not serviceable display the Non-Serviceable Zip Alert", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; + + const { wrapper } = setupMocks({ + isZipValid: true, + isZipServiceable: false, + vinVehicles: [ + { + vin: "TEST_VIN", + vehicle: { + carId: "C0000", + }, + }, + ], + }); + + store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); + + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe(true); }); - store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); + test("if the address matches a different vehicle display the Matched Different VehicleAlert", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - } - }) + const { wrapper } = setupMocks({ + isZipServiceable: true, + vinVehicles: [ + { + vehicle: { + carId: "C00000", + }, + }, + ], + }); - // Act - await wrapper.vm.forwardButtonAction(); + store.commit(storeMutations.UPDATE_CAR_ID, "CARID2"); - // Assert - expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalledWith("lookupVinByAddress", {"licenseLastName": undefined, "licenseState": "OH", "licenseStreetAddress": "1234 Main St", "licenseZip": "43215"}, false); + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); - expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalledWith("validateZip", {"zip": "43215"}); - }); - }); + // Act + await wrapper.vm.forwardButtonAction(); - describe("if registration zip is not serviceable", () => { - test("if registration address is provided and user clicks continue => show non-serviceable zip alert", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } - - const { wrapper } = setupMocks({ - isZipValid: true, - isZipServiceable: false, - vinVehicles: [{ - vin: "TEST_VIN", - vehicle: { - carId: "C0000" - } - }] + // Assert + expect(wrapper.findComponent({ ref: "alertMatchedDifferentVehicle" }).isVisible()).toBe( + true + ); }); - store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); + test("if the looking up VIN by address is not allowed in the state selected display the Vin Lookup By HomeAddress Not Allowed Alert", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - } - }) + const { wrapper } = setupMocks({ + isZipServiceable: true, + isStatePermissible: false, + lookupVinbyAddressResponse: { + isStatePermissible: false, + vinVehicles: [ + { + vin: "TEST_VIN", + vehicle: { + carId: "CARID", + }, + }, + { + vin: "TEST_VIN2", + vehicle: { + carId: "CARID2", + }, + }, + ], + }, + }); - expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).exists()).toBe(false); + store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); - // Act - await wrapper.vm.forwardButtonAction(); + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); - // Assert - expect(wrapper.vm.displayNonServiceableZipAlert).toBe(true); - expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).exists()).toBe(true); - expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe(true); - }); + // Act + await wrapper.vm.forwardButtonAction(); - test("if registration address, service zip are provided, and user clicks continue => don't update service address", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } + // Assert + expect( + wrapper.findComponent({ ref: "alertVinLookupsByHomeAddressNotAllowed" }).isVisible() + ).toBe(true); + }); - const { wrapper } = setupMocks({ - isZipServiceable: false - } - ); + test("if no vehicles found, display Vin Not Found alert", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; - store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); + const { wrapper } = setupMocks({ + isZipServiceable: true, + lookupVinbyAddressResponse: { + isStatePermissible: true, + vinVehicles: [], // Return no vehicles + }, + }); - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - } - }) + store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); - // Act - await wrapper.vm.forwardButtonAction(); + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); - // Assert - expect(wrapper.vm.dispatchStoreAction).not.toHaveBeenCalledWith(storeActions.UPDATE_SERVICE_LOCATION_WITH_VEHICLE_REGISTRATION); - }); + wrapper.vm.navigateForward = jest.fn(); - test("if registration address, service zip are provided, and user clicks continue => both zips are saved and are different", async () => { - // Arrange - const mockRegistrationAddress = { - streetAddress: "1234 Main St", - city: "Columbus", - state: "OH", - zipCode: "43215" - } + // Act + await wrapper.vm.forwardButtonAction(); - const { wrapper } = setupMocks({}); - wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); - - store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); - - wrapper.vm.dispatchStoreAction = jest.fn(); - wrapper.vm.dispatchStoreAction.mockImplementation((actionName, value) => { - let data = {}; - if (actionName == storeActions.VALIDATE_ZIP) { - if (value == "43215") { - data = { - isServiceable: false - }; - } - else { - data = { - isServiceable: true - } - } - } - else if (actionName == storeActions.LOOKUP_VIN_BY_ADDRESS) { - data = { - isStatePermissible: true, - vinVehicles: [{ - vin: "TEST_VIN", - vehicle: { - carId: "CARID" - } - }] - } - } - - return Promise.resolve({ data }); - }) - - await wrapper.setData({ - customerQuestions: { - addressQuestions: mockRegistrationAddress - } - }) - - await wrapper.vm.forwardButtonAction(); - await wrapper.setData({ - serviceZipCode: "12345" - }) - - // // Act - await wrapper.vm.forwardButtonAction(); - - // // Assert - expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).not.toEqual(wrapper.vm.$store.getters.vehicle.registration.zipCode); - expect(wrapper.vm.$store.getters.vehicle.registration.zipCode).toEqual("12345"); - expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual("11111"); - }); + // Assert + expect(wrapper.findComponent({ ref: "alertVinNotFound" }).isVisible()).toBe(true); + }); + }); + + describe("navigation", () => { + test("if the back button is clicked, navigate back", async () => { + // Arrange + const { wrapper } = setupMocks({ + isZipServiceable: true, + }); + + // Act + await wrapper.vm.backButtonAction(); + + // Assert + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); + }); + + test("if the car entered matches one of the vehicles found and the zip is serviceable, navigate forward", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; + + const { wrapper } = setupMocks({ + isZipServiceable: true, + vinVehicles: [ + { + vehicle: { + carId: "C11111", + }, + }, + ], + }); + + await wrapper.setData({ + previouslyEnteredCarId: "C11111", + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); + + wrapper.vm.navigateForward = jest.fn(); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.navigateForward).toHaveBeenCalled(); + }); + + test("if the car entered does not match any of the multiple vehicles found, navigate to address-vehicles page", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; + + const { wrapper } = setupMocks({ + isZipServiceable: true, + isStatePermissible: true, + vinVehicles: [ + { + vin: "TEST_VIN", + vehicle: { + carId: "CARID", + }, + }, + { + vin: "TEST_VIN2", + vehicle: { + carId: "CARID2", + }, + }, + ], + }); + + store.commit(storeMutations.UPDATE_CAR_ID, "CARID_A"); + + const carsFound = [ + { + vin: "TEST_VIN", + vehicle: { + carId: "CARID", + }, + }, + { + vin: "TEST_VIN2", + vehicle: { + carId: "CARID2", + }, + }, + ]; + + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); + + wrapper.vm.updateVehicleInfo = jest.fn(); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES, + undefined, + {}, + {}, + carsFound + ); + }); + + test("if the car entered matches one of the vehicles found but the zip is NOT serviceable, do not navigate forward", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; + + const { wrapper } = setupMocks({ + isZipServiceable: false, + isStatePermissible: true, + vinVehicles: [ + { + vin: "TEST_VIN", + vehicle: { + carId: "CARID", + }, + }, + { + vin: "TEST_VIN2", + vehicle: { + carId: "CARID2", + }, + }, + ], + }); + + store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); + + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); + + wrapper.vm.navigateForward = jest.fn(); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.navigateForward).toHaveBeenCalledTimes(0); + }); + + test("if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; + + const { wrapper } = setupMocks({ + isZipServiceable: true, + isStatePermissible: true, + }); + + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + isCarIdDifferent: true, + isSelectedGlassAvailableForVehicle: false, + }); + + let carsFound = [ + { + vin: "TEST_VIN2", + vehicle: { + carId: "C0000", + }, + }, + ]; + + // Act + await wrapper.vm.navigateForward(carsFound); + + // Assert + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, + undefined, + {}, + { displayVehicleChangeAlert: true } + ); + }); + + test("single car was found and matches entered vehicle => navigateForwardWithSingleCarMatch", async () => { + // Arrange + + const carsFound = [ + { + vin: "TEST_VIN_2", + vehicle: { + carId: "C0000", + }, + }, + ]; + + const { wrapper } = setupMocks({}, {}); + wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); + + // Act + wrapper.vm.navigateForward(carsFound); + + // Assert + expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1); + }); + + test("multiple cars were found and one matches entered vehicle => navigateForwardWithSingleCarMatch", async () => { + // Arrange + const carsFound = [ + { + vin: "TEST_VIN_1", + vehicle: { + carId: "C0000", + }, + }, + { + vin: "TEST_VIN_2", + vehicle: { + carId: "CARID2", + }, + }, + { + vin: "TEST_VIN_3", + vehicle: { + carId: "CARID3", + }, + }, + ]; + + const { wrapper } = setupMocks({}); + wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); + + // Act + wrapper.vm.navigateForward(carsFound); + + // Assert + expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1); + }); + }); + + describe("registration and service zips", () => { + describe("if registration zip is serviceable", () => { + test("if registration address is provided => update service address on successful continue", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; + + const { wrapper } = setupMocks({ + isZipServiceable: true, + }); + + store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); + + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalledWith( + "lookupVinByAddress", + { + licenseLastName: undefined, + licenseState: "OH", + licenseStreetAddress: "1234 Main St", + licenseZip: "43215", + }, + false + ); + + expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalledWith("validateZip", { + zip: "43215", + }); + }); + }); + + describe("if registration zip is not serviceable", () => { + test("if registration address is provided and user clicks continue => show non-serviceable zip alert", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; + + const { wrapper } = setupMocks({ + isZipValid: true, + isZipServiceable: false, + vinVehicles: [ + { + vin: "TEST_VIN", + vehicle: { + carId: "C0000", + }, + }, + ], + }); + + store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); + + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); + + expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).exists()).toBe( + false + ); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.displayNonServiceableZipAlert).toBe(true); + expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).exists()).toBe( + true + ); + expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe( + true + ); + }); + + test("if registration address, service zip are provided, and user clicks continue => don't update service address", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; + + const { wrapper } = setupMocks({ + isZipServiceable: false, + }); + + store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); + + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.dispatchStoreAction).not.toHaveBeenCalledWith( + storeActions.UPDATE_SERVICE_LOCATION_WITH_VEHICLE_REGISTRATION + ); + }); + + test("if registration address, service zip are provided, and user clicks continue => both zips are saved and are different", async () => { + // Arrange + const mockRegistrationAddress = { + streetAddress: "1234 Main St", + city: "Columbus", + state: "OH", + zipCode: "43215", + }; + + const { wrapper } = setupMocks({}); + wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); + + store.commit(storeMutations.UPDATE_CAR_ID, "CARID"); + + wrapper.vm.dispatchStoreAction = jest.fn(); + wrapper.vm.dispatchStoreAction.mockImplementation((actionName, value) => { + let data = {}; + if (actionName == storeActions.VALIDATE_ZIP) { + if (value == "43215") { + data = { + isServiceable: false, + }; + } else { + data = { + isServiceable: true, + }; + } + } else if (actionName == storeActions.LOOKUP_VIN_BY_ADDRESS) { + data = { + isStatePermissible: true, + vinVehicles: [ + { + vin: "TEST_VIN", + vehicle: { + carId: "CARID", + }, + }, + ], + }; + } + + return Promise.resolve({ data }); + }); + + await wrapper.setData({ + customerQuestions: { + addressQuestions: mockRegistrationAddress, + }, + }); + + await wrapper.vm.forwardButtonAction(); + await wrapper.setData({ + serviceZipCode: "12345", + }); + + // // Act + await wrapper.vm.forwardButtonAction(); + + // // Assert + expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).not.toEqual( + wrapper.vm.$store.getters.vehicle.registration.zipCode + ); + expect(wrapper.vm.$store.getters.vehicle.registration.zipCode).toEqual("12345"); + expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual("11111"); + }); + }); }); - }); }); -function setupMocks({ isZipValid = true, isZipServiceable = true, lookupVinbyAddressResponse, partsOrQuestions = [], isStatePermissible = true, vinVehicles =[], carId = 'C0000' }) { - store.commit(storeMutations.RESET_STATE); - const wrapper = shallowMount(addressLookup, getMountOptions({ - actionList: [ - { - actionName: storeActions.VALIDATE_ZIP, - data: { - isValid: isZipValid, - isServiceable: isZipServiceable - } - }, - { - actionName: storeActions.LOOKUP_VIN_BY_ADDRESS, - data: lookupVinbyAddressResponse ? lookupVinbyAddressResponse : { - isStatePermissible: true, - vinVehicles: [{ - vin: "TEST_VIN", - vehicle: { - carId: "CARID" - } - }] - } - }, - { - actionName: storeActions.GET_PARTS_OR_QUESTIONS, - data: { - partsOrQuestions: partsOrQuestions - } - }, - ], - router: { - navigate: jest.fn(), - navigate: jest.fn(), - navigateWithSaving: jest.fn(), - navigateWithoutSaving: jest.fn(), - }, - store: { - getters: { - vehicle: { - carId: carId, - registration: { - licensePlate: "TESTPLATE", - zipCode: "12345" - } +function setupMocks({ + isZipValid = true, + isZipServiceable = true, + lookupVinbyAddressResponse, + partsOrQuestions = [], + isStatePermissible = true, + vinVehicles = [], + carId = "C0000", +}) { + store.commit(storeMutations.RESET_STATE); + const wrapper = shallowMount( + addressLookup, + getMountOptions({ + actionList: [ + { + actionName: storeActions.VALIDATE_ZIP, + data: { + isValid: isZipValid, + isServiceable: isZipServiceable, + }, + }, + { + actionName: storeActions.LOOKUP_VIN_BY_ADDRESS, + data: lookupVinbyAddressResponse + ? lookupVinbyAddressResponse + : { + isStatePermissible: true, + vinVehicles: [ + { + vin: "TEST_VIN", + vehicle: { + carId: "CARID", + }, + }, + ], + }, + }, + { + actionName: storeActions.GET_PARTS_OR_QUESTIONS, + data: { + partsOrQuestions: partsOrQuestions, + }, + }, + ], + router: { + navigate: jest.fn(), + navigate: jest.fn(), + navigateWithSaving: jest.fn(), + navigateWithoutSaving: jest.fn(), + }, + store: { + getters: { + vehicle: { + carId: carId, + registration: { + licensePlate: "TESTPLATE", + zipCode: "12345", + }, + }, + order: { + customer: { + emailAddress: "test@test.com", + }, + serviceLocation: { + zipCode: "11111", + }, + }, + }, + }, + }) + ); + + const apiResponses = { + serviceZipValidationResponse: { + isValid: isZipValid, + isServiceable: isZipServiceable, }, - order: { - customer: { - emailAddress: "test@test.com" - }, - serviceLocation: { - zipCode: "11111" - } - } - } - }, - })); + vinLookupResponse: { + isStatePermissible: isStatePermissible, + vinVehicles: vinVehicles, + }, + }; - const apiResponses = { - serviceZipValidationResponse: { - isValid: isZipValid, - isServiceable: isZipServiceable - }, - vinLookupResponse: { - isStatePermissible: isStatePermissible, - vinVehicles: vinVehicles - }, - }; + settleAllPromises.mockImplementation(() => apiResponses); - settleAllPromises.mockImplementation(() => apiResponses); + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); + wrapper.vm.setCmsContent = jest.fn(); + wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); + wrapper.vm.$refs.loadingModal.showModal = jest.fn(); - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); - wrapper.vm.setCmsContent = jest.fn(); - wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); - wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); - wrapper.vm.$refs.loadingModal.showModal = jest.fn(); - - return { wrapper }; + return { wrapper }; } - diff --git a/src/layouts/address-lookup/address-lookup.vue b/src/layouts/address-lookup/address-lookup.vue index a68012e17..f04797d08 100644 --- a/src/layouts/address-lookup/address-lookup.vue +++ b/src/layouts/address-lookup/address-lookup.vue @@ -1,61 +1,83 @@ diff --git a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js index 5802adf6e..4a8e87feb 100644 --- a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js +++ b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js @@ -11,620 +11,649 @@ import store from "@/store"; let autocompleteElement; describe("address-questions.vue", () => { - beforeEach(() => { - // Create the `addressField1` element (autocomplete's input) - autocompleteElement = document.createElement("input") - autocompleteElement.getPlace = jest.fn(); - document.getElementById = jest.fn().mockReturnValue(autocompleteElement); - }) - - describe("initial state", () => { - test("only street address field is shown", () => { - // Arrange - const { wrapper } = setupMocks({}); - - // Assert - const streetAddressField = wrapper.findComponent({ ref: "autocomplete" }); - const cityField = wrapper.findComponent({ ref: "city" }); - const stateField = wrapper.findComponent({ ref: "state" }); - const zipCodeField = wrapper.findComponent({ ref: "zipCode" }); - - expect(streetAddressField.exists()).toBe(true); - expect(streetAddressField.isVisible()).toBe(true); - expect(cityField.exists()).toBe(true); - expect(cityField.isVisible()).toBe(false); - expect(stateField.exists()).toBe(true); - expect(stateField.isVisible()).toBe(false); - expect(zipCodeField.exists()).toBe(true); - expect(zipCodeField.isVisible()).toBe(false); - - const alerts = wrapper.findAllComponents(alert); - expect(alerts.length).toEqual(0); - }) - - test("Should render addressQuestions sub-components (textbox-questions and dropdown-questions)", async () => { - // Arrange - const { wrapper } = setupMocks({}); - - // Act - const streetAddress = wrapper.findComponent({ ref: 'autocomplete' }); - const city = wrapper.findComponent({ ref: 'city' }); - const state = wrapper.findComponent({ ref: 'state' }); - const zipCode = wrapper.findComponent({ ref: 'zipCode' }); - - // Assert - expect(streetAddress.exists()).toBe(true); - expect(city.exists()).toBe(true); - expect(state.exists()).toBe(true); - expect(zipCode.exists()).toBe(true); + beforeEach(() => { + // Create the `addressField1` element (autocomplete's input) + autocompleteElement = document.createElement("input"); + autocompleteElement.getPlace = jest.fn(); + document.getElementById = jest.fn().mockReturnValue(autocompleteElement); }); - test("Should set this.displayNoMatchWarning to false when it is set to true, if the model if prepopulated", async () => { - // Arrange - const newAddressModel = { - streetAddress: "foo", - city: "foo", - state: "foo", - zipCode: "55555", - }; - const wrapper = shallowMount(addressQuestions, { - propsData: { - modelValue: newAddressModel, - }, - }); + describe("initial state", () => { + test("only street address field is shown", () => { + // Arrange + const { wrapper } = setupMocks({}); - await wrapper.setData({ - displayNoMatchWarning: true - }) - expect(wrapper.vm.displayNoMatchWarning).toBeTruthy(); + // Assert + const streetAddressField = wrapper.findComponent({ ref: "autocomplete" }); + const cityField = wrapper.findComponent({ ref: "city" }); + const stateField = wrapper.findComponent({ ref: "state" }); + const zipCodeField = wrapper.findComponent({ ref: "zipCode" }); - // Act - wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, wrapper.vm.addressModel); + expect(streetAddressField.exists()).toBe(true); + expect(streetAddressField.isVisible()).toBe(true); + expect(cityField.exists()).toBe(true); + expect(cityField.isVisible()).toBe(false); + expect(stateField.exists()).toBe(true); + expect(stateField.isVisible()).toBe(false); + expect(zipCodeField.exists()).toBe(true); + expect(zipCodeField.isVisible()).toBe(false); - // Assert - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - }); - - test("Should it set this.showAddressFields to true when the model is prepopulated", async () => { - // Arrange - // Act - const newAddressModel = { - streetAddress: "foo", - city: "foo", - state: "foo", - zipCode: "55555", - }; - const wrapper = shallowMount(addressQuestions, { - propsData: { - modelValue: newAddressModel, - }, - }); - - // Act - wrapper.vm.setupAddressLookup(); - - // Assert - expect(wrapper.vm.showAddressFields).toBe(true); - - }); - }); - - describe("happy paths", () => { - test("full street address is passed in => address fields are displayed", async () => { - // Arrange/Act - const { wrapper } = setupMocks({ - props: { - modelValue: { - streetAddress: "12345 Test Road", - city: "Tests", - state: "OH", - zipCode: "12312" - } - } - }); - - await wrapper.vm.$nextTick(); - - // Assert - const cityField = wrapper.findComponent({ ref: "city" }); - const stateField = wrapper.findComponent({ ref: "state" }); - const zipField = wrapper.findComponent({ ref: "zipCode" }); - expect(cityField.exists()).toBeTruthy(); - expect(cityField.isVisible()).toBeTruthy(); - expect(stateField.exists()).toBeTruthy(); - expect(cityField.isVisible()).toBeTruthy(); - expect(zipField.exists()).toBeTruthy(); - expect(cityField.isVisible()).toBeTruthy(); - }); - - test("full street address is passed in => don't load Google Autocomplete script", async () => { - // Arrange/Act - const { wrapper } = setupMocks({ - props: { - modelValue: { - streetAddress: "12345 Test Road", - city: "Tests", - state: "OH", - zipCode: "12312" - } - } - }); - - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.$loadScript).not.toHaveBeenCalled(); - }); - - test("address field is focused => disable autocomplete", async () => { - // Arrange - let focusEventCallbackFunction; - autocompleteElement.addEventListener = jest.fn().mockImplementation((eventName, callbackFunction) => { - if (eventName == "focus") { - focusEventCallbackFunction = callbackFunction; - } - }); - const { wrapper } = setupMocks({}); - await wrapper.vm.$nextTick(); - - // Act - focusEventCallbackFunction(); - await wrapper.vm.$nextTick(); - - // Assert - expect(autocompleteElement.getAttribute("autocomplete")).toEqual("do-not-autofill"); - }); - - test("street address is entered, user chooses good result from autocomplete results => other fields are filled in", async () => { - // Arrange - const { wrapper } = setupMocks({}); - await wrapper.setData({ - addressModel: { - streetAddress: "123 Test Street" - } - }) - - const selectedPlace = { - address_components: [ - { - long_name: "1234", - short_name: "1234", - types: ["street_number"] - }, - { - long_name: "Test Road", - short_name: "Test Road", - types: ["route"] - }, - { - long_name: "East Columbus", - short_name: "Columbus", - types: ["neighborhood", "political"] - }, - { - long_name: "Columbus", - short_name: "Columbus", - types: ["locality", "political"] - }, - { - long_name: "Franklin County", - short_name: "Franklin County", - types: ["administrative_area_level_2", "political"] - }, - { - long_name: "Ohio", - short_name: "OH", - types: ["administrative_area_level_1", "political"] - }, - { - long_name: "United States", - short_name: "US", - types: ["country", "political"] - }, - { - long_name: "43215", - short_name: "43215", - types: ["postal_code"] - }, - ] - } - - // Act - autocompleteElement.dispatchEvent(new CustomEvent("place_changed", { detail: selectedPlace })); - - // Assert - const addressModel = wrapper.vm.addressModel; - expect(addressModel.streetAddress).toEqual("1234 Test Road"); - expect(addressModel.city).toEqual("Columbus"); - expect(addressModel.state).toEqual("OH"); - expect(addressModel.zipCode).toEqual("43215"); - }) - - test("street address is entered, user chooses good result from autocomplete results => alerts are cleared", async () => { - // Arrange - const { wrapper } = setupMocks({}); - await wrapper.setData({ - addressModel: { - streetAddress: "123 Test Street" - }, - displayVerificationWarning: true, - displayNoMatchWarning: true - }) - - let alerts = wrapper.findAllComponents(alert); - alerts.forEach(alert => expect(alert.isVisible()).toBeTruthy()); - - const selectedPlace = { - address_components: [ - { - long_name: "1234", - short_name: "1234", - types: ["street_number"] - }, - { - long_name: "Test Road", - short_name: "Test Road", - types: ["route"] - }, - { - long_name: "East Columbus", - short_name: "Columbus", - types: ["neighborhood", "political"] - }, - { - long_name: "Columbus", - short_name: "Columbus", - types: ["locality", "political"] - }, - { - long_name: "Franklin County", - short_name: "Franklin County", - types: ["administrative_area_level_2", "political"] - }, - { - long_name: "Ohio", - short_name: "OH", - types: ["administrative_area_level_1", "political"] - }, - { - long_name: "United States", - short_name: "US", - types: ["country", "political"] - }, - { - long_name: "43215", - short_name: "43215", - types: ["postal_code"] - }, - ] - } - - // Act - autocompleteElement.dispatchEvent(new CustomEvent("place_changed", { detail: selectedPlace })); - await wrapper.vm.$nextTick(); - - // Assert - alerts = wrapper.findAllComponents(alert); - alerts.forEach(alert => expect(alert.exists()).toBeFalsy()); - }); - - test("street address is entered, but user clicks away => first result is selected and other fields are filled in", async () => { - // Arrange - let changeEventCallbackFunction; - autocompleteElement.addEventListener = jest.fn().mockImplementation((eventName, callbackFunction) => { - if (eventName == "change") { - changeEventCallbackFunction = callbackFunction; - } - }); - - const { wrapper } = setupMocks({ - querySelectorFunction: function (query) { - if (query == ".pac-container .pac-item") { - let element = document.createElement("div"); - element.textContent = "123 Test Street" - return element; - } - }, - geocoderResult: { - address_components: [ - { - long_name: "1234", - short_name: "1234", - types: ["street_number"] - }, - { - long_name: "Test Road", - short_name: "Test Road", - types: ["route"] - }, - { - long_name: "East Columbus", - short_name: "Columbus", - types: ["neighborhood", "political"] - }, - { - long_name: "Columbus", - short_name: "Columbus", - types: ["locality", "political"] - }, - { - long_name: "Franklin County", - short_name: "Franklin County", - types: ["administrative_area_level_2", "political"] - }, - { - long_name: "Ohio", - short_name: "OH", - types: ["administrative_area_level_1", "political"] - }, - { - long_name: "United States", - short_name: "US", - types: ["country", "political"] - }, - { - long_name: "43215", - short_name: "43215", - types: ["postal_code"] - }, - ] - } - }); - - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - let verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - expect(verificationAlert.exists()).toBeFalsy(); - - await wrapper.vm.$nextTick(); - - // Act - changeEventCallbackFunction(); - - await wrapper.vm.$nextTick(); - - // Assert - const addressModel = wrapper.vm.addressModel; - expect(addressModel.streetAddress).toEqual("1234 Test Road"); - expect(addressModel.city).toEqual("Columbus"); - expect(addressModel.state).toEqual("OH"); - expect(addressModel.zipCode).toEqual("43215"); - }); - }); - - describe("alerts", () => { - const places = [null, { address_components: null }, undefined, {}]; - test.each(places)("selected place/place properties is null => display verification alert", async (place) => { - // Arrange - const { wrapper } = setupMocks({}); - await wrapper.setData({ - addressModel: { - streetAddress: "123 Test Street" - }, - displayVerificationWarning: true, - displayNoMatchWarning: true - }) - - let alerts = wrapper.findAllComponents(alert); - alerts.forEach(alert => expect(alert.isVisible()).toBeTruthy()); - - const selectedPlace = place; - - // Act - autocompleteElement.dispatchEvent(new CustomEvent("place_changed", { detail: selectedPlace })); - await wrapper.vm.$nextTick(); - - // Assert - const verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); - expect(verificationAlert.exists()).toBe(true); - expect(verificationAlert.isVisible()).toBe(true); - const noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBe(false); - }); - - test("user enters address that yields no autocomplete results => show noMatch alert", async () => { - // Arrange - let changeEventCallbackFunction; - autocompleteElement.addEventListener = jest.fn().mockImplementation((eventName, callbackFunction) => { - if (eventName == "change") { - changeEventCallbackFunction = callbackFunction; - } - }); - - const { wrapper } = setupMocks({}); - - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - - await wrapper.vm.$nextTick(); - - // Act - changeEventCallbackFunction(); - - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.displayNoMatchWarning).toBeTruthy(); - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeTruthy(); - expect(noMatchAlert.isVisible()).toBeTruthy(); - }); - - test("user enters address that yields autocomplete results, but doesn't select => show verification alert", async () => { - // Arrange - let changeEventCallbackFunction; - autocompleteElement.addEventListener = jest.fn().mockImplementation((eventName, callbackFunction) => { - if (eventName == "change") { - changeEventCallbackFunction = callbackFunction; - } - }); - - const { wrapper } = setupMocks({ - querySelectorFunction: function (query) { - if (query == ".pac-container .pac-item") { - let element = document.createElement("div"); - element.textContent = "123 Test Street" - return element; - } - } - }); - - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - let verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - expect(verificationAlert.exists()).toBeFalsy(); - - await wrapper.vm.$nextTick(); - - // Act - changeEventCallbackFunction(); - - await wrapper.vm.$nextTick(); - - // Assert - verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); - expect(wrapper.vm.displayVerificationWarning).toBeTruthy(); - expect(verificationAlert.exists()).toBeTruthy(); - expect(verificationAlert.isVisible()).toBeTruthy(); - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - expect(noMatchAlert.exists()).toBeFalsy(); - }); - - describe("noMatch alert is cleared on address change", () => { - test("user sees noMatch warning and enters city => noMatch warning is removed", async () => { - // Arrange - const { wrapper } = setupMocks({}); - - wrapper.setData({ - displayNoMatchWarning: true + const alerts = wrapper.findAllComponents(alert); + expect(alerts.length).toEqual(0); }); - await wrapper.vm.$nextTick(); - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeTruthy(); - expect(noMatchAlert.isVisible()).toBeTruthy(); + test("Should render addressQuestions sub-components (textbox-questions and dropdown-questions)", async () => { + // Arrange + const { wrapper } = setupMocks({}); - // Act - wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { - city: "Somewhere" - }) - await wrapper.vm.$nextTick(); + // Act + const streetAddress = wrapper.findComponent({ ref: "autocomplete" }); + const city = wrapper.findComponent({ ref: "city" }); + const state = wrapper.findComponent({ ref: "state" }); + const zipCode = wrapper.findComponent({ ref: "zipCode" }); - // Assert - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - }); - - test("user sees noMatch warning and enters state => noMatch warning is removed", async () => { - // Arrange - const { wrapper } = setupMocks({}); - - wrapper.setData({ - displayNoMatchWarning: true + // Assert + expect(streetAddress.exists()).toBe(true); + expect(city.exists()).toBe(true); + expect(state.exists()).toBe(true); + expect(zipCode.exists()).toBe(true); }); - await wrapper.vm.$nextTick(); - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeTruthy(); - expect(noMatchAlert.isVisible()).toBeTruthy(); + test("Should set this.displayNoMatchWarning to false when it is set to true, if the model if prepopulated", async () => { + // Arrange + const newAddressModel = { + streetAddress: "foo", + city: "foo", + state: "foo", + zipCode: "55555", + }; + const wrapper = shallowMount(addressQuestions, { + propsData: { + modelValue: newAddressModel, + }, + }); - // Act - wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { - state: "KO" - }) - await wrapper.vm.$nextTick(); + await wrapper.setData({ + displayNoMatchWarning: true, + }); + expect(wrapper.vm.displayNoMatchWarning).toBeTruthy(); - // Assert - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - }); + // Act + wrapper.vm.$options.watch.addressModel.handler.call( + wrapper.vm, + wrapper.vm.addressModel + ); - test("user sees noMatch warning and enters zip code => noMatch warning is removed", async () => { - // Arrange - const { wrapper } = setupMocks({}); - - wrapper.setData({ - displayNoMatchWarning: true + // Assert + expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); }); - await wrapper.vm.$nextTick(); - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeTruthy(); - expect(noMatchAlert.isVisible()).toBeTruthy(); + test("Should it set this.showAddressFields to true when the model is prepopulated", async () => { + // Arrange + // Act + const newAddressModel = { + streetAddress: "foo", + city: "foo", + state: "foo", + zipCode: "55555", + }; + const wrapper = shallowMount(addressQuestions, { + propsData: { + modelValue: newAddressModel, + }, + }); - // Act - wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { - zipCode: "12345" - }) - await wrapper.vm.$nextTick(); + // Act + wrapper.vm.setupAddressLookup(); - // Assert - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - }); + // Assert + expect(wrapper.vm.showAddressFields).toBe(true); + }); + }); + + describe("happy paths", () => { + test("full street address is passed in => address fields are displayed", async () => { + // Arrange/Act + const { wrapper } = setupMocks({ + props: { + modelValue: { + streetAddress: "12345 Test Road", + city: "Tests", + state: "OH", + zipCode: "12312", + }, + }, + }); + + await wrapper.vm.$nextTick(); + + // Assert + const cityField = wrapper.findComponent({ ref: "city" }); + const stateField = wrapper.findComponent({ ref: "state" }); + const zipField = wrapper.findComponent({ ref: "zipCode" }); + expect(cityField.exists()).toBeTruthy(); + expect(cityField.isVisible()).toBeTruthy(); + expect(stateField.exists()).toBeTruthy(); + expect(cityField.isVisible()).toBeTruthy(); + expect(zipField.exists()).toBeTruthy(); + expect(cityField.isVisible()).toBeTruthy(); + }); + + test("full street address is passed in => don't load Google Autocomplete script", async () => { + // Arrange/Act + const { wrapper } = setupMocks({ + props: { + modelValue: { + streetAddress: "12345 Test Road", + city: "Tests", + state: "OH", + zipCode: "12312", + }, + }, + }); + + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.$loadScript).not.toHaveBeenCalled(); + }); + + test("address field is focused => disable autocomplete", async () => { + // Arrange + let focusEventCallbackFunction; + autocompleteElement.addEventListener = jest + .fn() + .mockImplementation((eventName, callbackFunction) => { + if (eventName == "focus") { + focusEventCallbackFunction = callbackFunction; + } + }); + const { wrapper } = setupMocks({}); + await wrapper.vm.$nextTick(); + + // Act + focusEventCallbackFunction(); + await wrapper.vm.$nextTick(); + + // Assert + expect(autocompleteElement.getAttribute("autocomplete")).toEqual("do-not-autofill"); + }); + + test("street address is entered, user chooses good result from autocomplete results => other fields are filled in", async () => { + // Arrange + const { wrapper } = setupMocks({}); + await wrapper.setData({ + addressModel: { + streetAddress: "123 Test Street", + }, + }); + + const selectedPlace = { + address_components: [ + { + long_name: "1234", + short_name: "1234", + types: ["street_number"], + }, + { + long_name: "Test Road", + short_name: "Test Road", + types: ["route"], + }, + { + long_name: "East Columbus", + short_name: "Columbus", + types: ["neighborhood", "political"], + }, + { + long_name: "Columbus", + short_name: "Columbus", + types: ["locality", "political"], + }, + { + long_name: "Franklin County", + short_name: "Franklin County", + types: ["administrative_area_level_2", "political"], + }, + { + long_name: "Ohio", + short_name: "OH", + types: ["administrative_area_level_1", "political"], + }, + { + long_name: "United States", + short_name: "US", + types: ["country", "political"], + }, + { + long_name: "43215", + short_name: "43215", + types: ["postal_code"], + }, + ], + }; + + // Act + autocompleteElement.dispatchEvent( + new CustomEvent("place_changed", { detail: selectedPlace }) + ); + + // Assert + const addressModel = wrapper.vm.addressModel; + expect(addressModel.streetAddress).toEqual("1234 Test Road"); + expect(addressModel.city).toEqual("Columbus"); + expect(addressModel.state).toEqual("OH"); + expect(addressModel.zipCode).toEqual("43215"); + }); + + test("street address is entered, user chooses good result from autocomplete results => alerts are cleared", async () => { + // Arrange + const { wrapper } = setupMocks({}); + await wrapper.setData({ + addressModel: { + streetAddress: "123 Test Street", + }, + displayVerificationWarning: true, + displayNoMatchWarning: true, + }); + + let alerts = wrapper.findAllComponents(alert); + alerts.forEach((alert) => expect(alert.isVisible()).toBeTruthy()); + + const selectedPlace = { + address_components: [ + { + long_name: "1234", + short_name: "1234", + types: ["street_number"], + }, + { + long_name: "Test Road", + short_name: "Test Road", + types: ["route"], + }, + { + long_name: "East Columbus", + short_name: "Columbus", + types: ["neighborhood", "political"], + }, + { + long_name: "Columbus", + short_name: "Columbus", + types: ["locality", "political"], + }, + { + long_name: "Franklin County", + short_name: "Franklin County", + types: ["administrative_area_level_2", "political"], + }, + { + long_name: "Ohio", + short_name: "OH", + types: ["administrative_area_level_1", "political"], + }, + { + long_name: "United States", + short_name: "US", + types: ["country", "political"], + }, + { + long_name: "43215", + short_name: "43215", + types: ["postal_code"], + }, + ], + }; + + // Act + autocompleteElement.dispatchEvent( + new CustomEvent("place_changed", { detail: selectedPlace }) + ); + await wrapper.vm.$nextTick(); + + // Assert + alerts = wrapper.findAllComponents(alert); + alerts.forEach((alert) => expect(alert.exists()).toBeFalsy()); + }); + + test("street address is entered, but user clicks away => first result is selected and other fields are filled in", async () => { + // Arrange + let changeEventCallbackFunction; + autocompleteElement.addEventListener = jest + .fn() + .mockImplementation((eventName, callbackFunction) => { + if (eventName == "change") { + changeEventCallbackFunction = callbackFunction; + } + }); + + const { wrapper } = setupMocks({ + querySelectorFunction: function (query) { + if (query == ".pac-container .pac-item") { + let element = document.createElement("div"); + element.textContent = "123 Test Street"; + return element; + } + }, + geocoderResult: { + address_components: [ + { + long_name: "1234", + short_name: "1234", + types: ["street_number"], + }, + { + long_name: "Test Road", + short_name: "Test Road", + types: ["route"], + }, + { + long_name: "East Columbus", + short_name: "Columbus", + types: ["neighborhood", "political"], + }, + { + long_name: "Columbus", + short_name: "Columbus", + types: ["locality", "political"], + }, + { + long_name: "Franklin County", + short_name: "Franklin County", + types: ["administrative_area_level_2", "political"], + }, + { + long_name: "Ohio", + short_name: "OH", + types: ["administrative_area_level_1", "political"], + }, + { + long_name: "United States", + short_name: "US", + types: ["country", "political"], + }, + { + long_name: "43215", + short_name: "43215", + types: ["postal_code"], + }, + ], + }, + }); + + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + let verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); + expect(verificationAlert.exists()).toBeFalsy(); + + await wrapper.vm.$nextTick(); + + // Act + changeEventCallbackFunction(); + + await wrapper.vm.$nextTick(); + + // Assert + const addressModel = wrapper.vm.addressModel; + expect(addressModel.streetAddress).toEqual("1234 Test Road"); + expect(addressModel.city).toEqual("Columbus"); + expect(addressModel.state).toEqual("OH"); + expect(addressModel.zipCode).toEqual("43215"); + }); + }); + + describe("alerts", () => { + const places = [null, { address_components: null }, undefined, {}]; + test.each(places)( + "selected place/place properties is null => display verification alert", + async (place) => { + // Arrange + const { wrapper } = setupMocks({}); + await wrapper.setData({ + addressModel: { + streetAddress: "123 Test Street", + }, + displayVerificationWarning: true, + displayNoMatchWarning: true, + }); + + let alerts = wrapper.findAllComponents(alert); + alerts.forEach((alert) => expect(alert.isVisible()).toBeTruthy()); + + const selectedPlace = place; + + // Act + autocompleteElement.dispatchEvent( + new CustomEvent("place_changed", { detail: selectedPlace }) + ); + await wrapper.vm.$nextTick(); + + // Assert + const verificationAlert = wrapper.findComponent({ + ref: "alertVerificationWarning", + }); + expect(verificationAlert.exists()).toBe(true); + expect(verificationAlert.isVisible()).toBe(true); + const noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBe(false); + } + ); + + test("user enters address that yields no autocomplete results => show noMatch alert", async () => { + // Arrange + let changeEventCallbackFunction; + autocompleteElement.addEventListener = jest + .fn() + .mockImplementation((eventName, callbackFunction) => { + if (eventName == "change") { + changeEventCallbackFunction = callbackFunction; + } + }); + + const { wrapper } = setupMocks({}); + + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); + + await wrapper.vm.$nextTick(); + + // Act + changeEventCallbackFunction(); + + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.displayNoMatchWarning).toBeTruthy(); + noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeTruthy(); + expect(noMatchAlert.isVisible()).toBeTruthy(); + }); + + test("user enters address that yields autocomplete results, but doesn't select => show verification alert", async () => { + // Arrange + let changeEventCallbackFunction; + autocompleteElement.addEventListener = jest + .fn() + .mockImplementation((eventName, callbackFunction) => { + if (eventName == "change") { + changeEventCallbackFunction = callbackFunction; + } + }); + + const { wrapper } = setupMocks({ + querySelectorFunction: function (query) { + if (query == ".pac-container .pac-item") { + let element = document.createElement("div"); + element.textContent = "123 Test Street"; + return element; + } + }, + }); + + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + let verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); + expect(verificationAlert.exists()).toBeFalsy(); + + await wrapper.vm.$nextTick(); + + // Act + changeEventCallbackFunction(); + + await wrapper.vm.$nextTick(); + + // Assert + verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); + expect(wrapper.vm.displayVerificationWarning).toBeTruthy(); + expect(verificationAlert.exists()).toBeTruthy(); + expect(verificationAlert.isVisible()).toBeTruthy(); + noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); + expect(noMatchAlert.exists()).toBeFalsy(); + }); + + describe("noMatch alert is cleared on address change", () => { + test("user sees noMatch warning and enters city => noMatch warning is removed", async () => { + // Arrange + const { wrapper } = setupMocks({}); + + wrapper.setData({ + displayNoMatchWarning: true, + }); + await wrapper.vm.$nextTick(); + + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeTruthy(); + expect(noMatchAlert.isVisible()).toBeTruthy(); + + // Act + wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { + city: "Somewhere", + }); + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); + noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); + }); + + test("user sees noMatch warning and enters state => noMatch warning is removed", async () => { + // Arrange + const { wrapper } = setupMocks({}); + + wrapper.setData({ + displayNoMatchWarning: true, + }); + await wrapper.vm.$nextTick(); + + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeTruthy(); + expect(noMatchAlert.isVisible()).toBeTruthy(); + + // Act + wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { + state: "KO", + }); + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); + noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); + }); + + test("user sees noMatch warning and enters zip code => noMatch warning is removed", async () => { + // Arrange + const { wrapper } = setupMocks({}); + + wrapper.setData({ + displayNoMatchWarning: true, + }); + await wrapper.vm.$nextTick(); + + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeTruthy(); + expect(noMatchAlert.isVisible()).toBeTruthy(); + + // Act + wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { + zipCode: "12345", + }); + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); + noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); + }); + }); }); - }); }); -function setupMocks({ mountOptions, props, isShallowMount = true, querySelectorFunction, geocoderResult = ["1234 Test Street"] }) { - store.commit(storeMutations.RESET_STATE); +function setupMocks({ + mountOptions, + props, + isShallowMount = true, + querySelectorFunction, + geocoderResult = ["1234 Test Street"], +}) { + store.commit(storeMutations.RESET_STATE); - const resultingMountOptions = getMountOptions({ - ...mountOptions, - router: { - navigate: jest.fn(), - navigate: jest.fn() - }, - loadScript: jest.fn().mockResolvedValue() - }); + const resultingMountOptions = getMountOptions({ + ...mountOptions, + router: { + navigate: jest.fn(), + navigate: jest.fn(), + }, + loadScript: jest.fn().mockResolvedValue(), + }); - window.google = { - maps: { - event: { - addListener: jest.fn().mockImplementation((element, eventName, callbackFunction) => { - function interceptedCallbackFunction(e) { - callbackFunction(e.detail); - } - // selectedPlace = "Woogly"; - element.addEventListener(eventName, interceptedCallbackFunction); - }), - removeListener: jest.fn(), - clearInstanceListeners: jest.fn() - }, - places: { - Autocomplete: jest.fn().mockImplementation((el) => el) - }, - Geocoder: class Geocoder { - // constructor(); + window.google = { + maps: { + event: { + addListener: jest + .fn() + .mockImplementation((element, eventName, callbackFunction) => { + function interceptedCallbackFunction(e) { + callbackFunction(e.detail); + } + // selectedPlace = "Woogly"; + element.addEventListener(eventName, interceptedCallbackFunction); + }), + removeListener: jest.fn(), + clearInstanceListeners: jest.fn(), + }, + places: { + Autocomplete: jest.fn().mockImplementation((el) => el), + }, + Geocoder: class Geocoder { + // constructor(); - geocode(request, callback) { - callback([geocoderResult], true) + geocode(request, callback) { + callback([geocoderResult], true); + } + }, + GeocoderStatus: { + OK: true, + }, + }, + }; + + if (props) resultingMountOptions.propsData = props; + + const wrapper = isShallowMount + ? shallowMount(addressQuestions, resultingMountOptions) + : mount(addressQuestions, resultingMountOptions); + document.querySelector = jest.fn().mockImplementation((query) => { + let result = null; + if (query == ".pac-container") result = document.createElement("div"); + else if (querySelectorFunction) { + result = querySelectorFunction(query); } - }, - GeocoderStatus: { - OK: true - } - } - }; - if (props) - resultingMountOptions.propsData = props; + return result ?? null; + }); - const wrapper = isShallowMount ? shallowMount(addressQuestions, resultingMountOptions) : mount(addressQuestions, resultingMountOptions); - document.querySelector = jest.fn().mockImplementation(query => { - let result = null; - if (query == ".pac-container") - result = document.createElement("div"); - else if (querySelectorFunction) { - result = querySelectorFunction(query); - } - - return result ?? null; - }); - - return { wrapper }; -} \ No newline at end of file + return { wrapper }; +} diff --git a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue index 90fa2bba9..74c1866f1 100644 --- a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue +++ b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue @@ -1,76 +1,72 @@ diff --git a/src/layouts/address-lookup/customer-questions/customer-questions.spec.js b/src/layouts/address-lookup/customer-questions/customer-questions.spec.js index 949d3dde0..11c7fb3a9 100644 --- a/src/layouts/address-lookup/customer-questions/customer-questions.spec.js +++ b/src/layouts/address-lookup/customer-questions/customer-questions.spec.js @@ -11,26 +11,23 @@ const customerModel = { firstName: "", lastName: "", emailAddress: "", -} +}; describe("customerQuestions.vue", () => { - it("Should render customerQuestions sub-components (addressQuestions, first name, last name, and email textbox-questions)", async () => { - // Arrange + // Arrange const wrapper = shallowMount(customerQuestions); // Act - const addressQuestions = wrapper.findComponent({ ref: 'addressQuestions' }); - const firstName = wrapper.findComponent({ ref: 'firstName' }); - const lastName = wrapper.findComponent({ ref: 'lastName' }); - const emailAddress = wrapper.findComponent({ ref: 'emailAddress' }); + const addressQuestions = wrapper.findComponent({ ref: "addressQuestions" }); + const firstName = wrapper.findComponent({ ref: "firstName" }); + const lastName = wrapper.findComponent({ ref: "lastName" }); + const emailAddress = wrapper.findComponent({ ref: "emailAddress" }); // Assert expect(addressQuestions.exists()).toBe(true); expect(firstName.exists()).toBe(true); expect(lastName.exists()).toBe(true); expect(emailAddress.exists()).toBe(true); - }); - -}) \ No newline at end of file +}); diff --git a/src/layouts/address-lookup/customer-questions/customer-questions.vue b/src/layouts/address-lookup/customer-questions/customer-questions.vue index e837f26fc..d10812df2 100644 --- a/src/layouts/address-lookup/customer-questions/customer-questions.vue +++ b/src/layouts/address-lookup/customer-questions/customer-questions.vue @@ -1,49 +1,43 @@ \ No newline at end of file +export default { + name: "customer-questions", + emits: ["update:modelValue"], // The component emits an event + props: { + modelValue: { + type: Object, + default: () => ({ + customerQuestions: { + addressQuestions: { + streetAddress: "", + city: "", + state: "", + zipCode: "", + }, + firstName: "", + lastName: "", + emailAddress: "", + }, + }), + }, + validationRules: String, + }, + computed: { + customerModel: { + get: function () { + return this.modelValue; + }, + set: function (newValue) { + this.$emit("update:modelValue", newValue); + }, + }, + }, + components: { + addressQuestions, + textboxQuestion, + textBlock, + }, +}; + diff --git a/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.spec.js b/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.spec.js index 16451b219..4828d00c5 100644 --- a/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.spec.js +++ b/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.spec.js @@ -1,70 +1,66 @@ import { shallowMount } from "@vue/test-utils"; import addressVehiclesQuestion from "@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question"; import { ValueToLogTypes } from "@/constants/analytics"; - describe("addressVehiclesQuestion.vue", () => { - it("Should return content for differentVehicleAlertHeader", () => { - // Arrange - const wrapper = shallowMount(addressVehiclesQuestion, { - mixins: [mockMixin], - }); - - // Assert - expect(wrapper.vm.differentVehicleAlertHeader).toEqual('FoundWindshieldTestReturn'); + // Arrange + const wrapper = shallowMount(addressVehiclesQuestion, { + mixins: [mockMixin], + }); + + // Assert + expect(wrapper.vm.differentVehicleAlertHeader).toEqual("FoundWindshieldTestReturn"); }); it("Should return content for differentVehicleAlertBody", () => { - // Arrange - const wrapper = shallowMount(addressVehiclesQuestion, { - mixins: [mockMixin], - propsData: { - vehicles: ["1", "2"], - modelValue: ["1", "2"], - } - }); - - // Assert - expect(wrapper.vm.differentVehicleAlertBody).toEqual('FoundWindshieldTestReturn'); + // Arrange + const wrapper = shallowMount(addressVehiclesQuestion, { + mixins: [mockMixin], + propsData: { + vehicles: ["1", "2"], + modelValue: ["1", "2"], + }, + }); + + // Assert + expect(wrapper.vm.differentVehicleAlertBody).toEqual("FoundWindshieldTestReturn"); }); it("Should emit a modelValue change when setting selectedVehicleVin", async () => { - // Arrange - const wrapper = shallowMount(addressVehiclesQuestion, { - mixins: [mockMixin], - propsData: { - vehicles: ["1", "2", "newValue"], - modelValue: "2", - } - }); + // Arrange + const wrapper = shallowMount(addressVehiclesQuestion, { + mixins: [mockMixin], + propsData: { + vehicles: ["1", "2", "newValue"], + modelValue: "2", + }, + }); - // Act - const localThis = { $emit: jest.fn() } - addressVehiclesQuestion.computed.selectedVehicleVin.set.call(localThis, 'newValue'); + // Act + const localThis = { $emit: jest.fn() }; + addressVehiclesQuestion.computed.selectedVehicleVin.set.call(localThis, "newValue"); - // Assert - expect(localThis.$emit).toBeCalledWith("update:modelValue", "newValue"); + // Assert + expect(localThis.$emit).toBeCalledWith("update:modelValue", "newValue"); }); - }); const mockMixin = { methods: { - getCmsContent: jest.fn((contentName) => { - if (contentName === "FoundWindshield") { - return 'FoundWindshieldTestReturn'; - } - return null; - }), - vehicles: jest.fn(() => { - return [{ vehicle: "test" }]; - }) + getCmsContent: jest.fn((contentName) => { + if (contentName === "FoundWindshield") { + return "FoundWindshieldTestReturn"; + } + return null; + }), + vehicles: jest.fn(() => { + return [{ vehicle: "test" }]; + }), }, computed: { - ValueToLogTypes() { - return ValueToLogTypes; - } + ValueToLogTypes() { + return ValueToLogTypes; + }, }, - - } +}; diff --git a/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue b/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue index 1f26d9459..003556727 100644 --- a/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue +++ b/src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue @@ -1,24 +1,23 @@ \ No newline at end of file +} + diff --git a/src/layouts/address-vehicles/address-vehicles.spec.js b/src/layouts/address-vehicles/address-vehicles.spec.js index 8b1893765..e3ce48a32 100644 --- a/src/layouts/address-vehicles/address-vehicles.spec.js +++ b/src/layouts/address-vehicles/address-vehicles.spec.js @@ -11,221 +11,222 @@ import * as navigateToHeritage from "@/helpers/heritage-integration/navigation-h // Mock our module for promises. jest.mock("@/helpers/damage-helper", () => ({ - isGlassAvailableForCarId: () => { - return false; - }, + isGlassAvailableForCarId: () => { + return false; + }, })); - describe("addressVehicles.vue", () => { test("Should return true for valid page requisites if carId / zipCode / emailAddress / pageData exists", async () => { - // Arrange - const { wrapper } = setupMocks({}); - store.commit(storeMutations.UPDATE_CAR_ID, "NOT NULL"); - store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP_CODE, "12345"); - store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, "test@test.com"); - - // Act - const result = wrapper.vm.arePagePrerequisitesValid(); + // Arrange + const { wrapper } = setupMocks({}); + store.commit(storeMutations.UPDATE_CAR_ID, "NOT NULL"); + store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP_CODE, "12345"); + store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, "test@test.com"); - //Assert - expect(result).toBe(true); + // Act + const result = wrapper.vm.arePagePrerequisitesValid(); - wrapper.unmount(); + //Assert + expect(result).toBe(true); + + wrapper.unmount(); }); test("Should return false for valid page requisites if carId is missing", async () => { - // Arrange - const { wrapper } = setupMocks({}); - - // Act - store.commit(storeMutations.UPDATE_CAR_ID, null); - const result = wrapper.vm.arePagePrerequisitesValid(); + // Arrange + const { wrapper } = setupMocks({}); - //Assert - expect(result).toBe(false); + // Act + store.commit(storeMutations.UPDATE_CAR_ID, null); + const result = wrapper.vm.arePagePrerequisitesValid(); - wrapper.unmount(); + //Assert + expect(result).toBe(false); + + wrapper.unmount(); }); - // NOTE: this test is only here to meet code coverage; it does not test any logic in the original function test("Should navigate to CLICKED_BACK if backButtonAction is run", async () => { - // Arrange - const { wrapper } = setupMocks({}); - wrapper.vm.$router.navigateWithoutSaving = jest.fn(); - - // Act - await wrapper.setData({ - selectedVehicleVin: '5NMS3CADXLH233004', - }); - wrapper.vm.backButtonAction(); + // Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.$router.navigateWithoutSaving = jest.fn(); - //Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalled(); + // Act + await wrapper.setData({ + selectedVehicleVin: "5NMS3CADXLH233004", + }); + wrapper.vm.backButtonAction(); - wrapper.unmount(); + //Assert + expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalled(); + + wrapper.unmount(); }); // NOTE: this test is only here to meet code coverage; it does not test any logic in the original function test("Should run several related methods if forwardButtonAction is run", async () => { - // Arrange - const { wrapper } = setupMocks({}); - const lookupVinResponse = { - data: { - carId: "456" - } - } - - // the following has to be set BEFORE changing the data which is being watched, and requires updateButtonText to be mocked - wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); - wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); - wrapper.vm.lookupVin = jest.fn(() => Promise.resolve(lookupVinResponse)); - wrapper.vm.$router.navigate = jest.fn(); - wrapper.vm.updateCustomerInfo = jest.fn().mockImplementation(()=> {}); - wrapper.vm.navigateForward = jest.fn().mockImplementation(()=> {}); + // Arrange + const { wrapper } = setupMocks({}); + const lookupVinResponse = { + data: { + carId: "456", + }, + }; - // Act - await wrapper.setData({ - selectedVehicleVin: '5NMS3CADXLH233004', - }); - - await wrapper.vm.forwardButtonAction(); - wrapper.vm.$nextTick(); + // the following has to be set BEFORE changing the data which is being watched, and requires updateButtonText to be mocked + wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); + wrapper.vm.lookupVin = jest.fn(() => Promise.resolve(lookupVinResponse)); + wrapper.vm.$router.navigate = jest.fn(); + wrapper.vm.updateCustomerInfo = jest.fn().mockImplementation(() => {}); + wrapper.vm.navigateForward = jest.fn().mockImplementation(() => {}); - //Assert - expect(wrapper.vm.navigateForward).toBeCalled(); + // Act + await wrapper.setData({ + selectedVehicleVin: "5NMS3CADXLH233004", + }); - wrapper.unmount(); + await wrapper.vm.forwardButtonAction(); + wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.vm.navigateForward).toBeCalled(); + + wrapper.unmount(); }); test("Should return out of forwardButtonAction if lookupVin returns with an error", async () => { - // Arrange - const { wrapper } = setupMocks({}); + // Arrange + const { wrapper } = setupMocks({}); - const lookupVinResponse = { - error: "there is an error" - } - - // the following has to be set BEFORE changing the data which is being watched, and requires updateButtonText to be mocked - wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); - wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); - wrapper.vm.lookupVin = jest.fn(() => Promise.reject(lookupVinResponse)); - wrapper.vm.$router.navigateWithSaving = jest.fn(); - wrapper.vm.updateCustomerInfo = jest.fn().mockImplementation(()=> {}); + const lookupVinResponse = { + error: "there is an error", + }; - // Act - await wrapper.setData({ - selectedVehicleVin: '5NMS3CADXLH233004', - }); - await wrapper.vm.forwardButtonAction(); - wrapper.vm.$nextTick(); + // the following has to be set BEFORE changing the data which is being watched, and requires updateButtonText to be mocked + wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); + wrapper.vm.lookupVin = jest.fn(() => Promise.reject(lookupVinResponse)); + wrapper.vm.$router.navigateWithSaving = jest.fn(); + wrapper.vm.updateCustomerInfo = jest.fn().mockImplementation(() => {}); - //Assert - expect(wrapper.vm.forwardButtonAction).toReturn; + // Act + await wrapper.setData({ + selectedVehicleVin: "5NMS3CADXLH233004", + }); + await wrapper.vm.forwardButtonAction(); + wrapper.vm.$nextTick(); - wrapper.unmount(); + //Assert + expect(wrapper.vm.forwardButtonAction).toReturn; + + wrapper.unmount(); }); test("Should navigate to CLICKED_FORWARD scenario if carId is different and selected glass not available for vehicle on navigateForward", async () => { - // Arrange - const { wrapper } = setupMocks({}); - wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); - wrapper.vm.$router.navigateWithSaving = jest.fn(); - - // Act - await wrapper.setData({ - selectedVehicleVin: '5NMS3CADXLH233004', - isSelectedGlassAvailableForVehicle: false, - isCarIdDifferent: true, - }); - await wrapper.vm.navigateForward(); + // Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + wrapper.vm.$router.navigateWithSaving = jest.fn(); - //Assert - expect(wrapper.vm.$router.navigateWithSaving).toBeCalledTimes(1); + // Act + await wrapper.setData({ + selectedVehicleVin: "5NMS3CADXLH233004", + isSelectedGlassAvailableForVehicle: false, + isCarIdDifferent: true, + }); + await wrapper.vm.navigateForward(); - wrapper.unmount(); + //Assert + expect(wrapper.vm.$router.navigateWithSaving).toBeCalledTimes(1); + + wrapper.unmount(); }); test("carId is not different on navigateForward (car was found) => Should handle navigating forward with car match", async () => { - // Arrange - const { wrapper } = setupMocks({}); - wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); - wrapper.vm.$refs.loadingModal.showModal = jest.fn(); - navigateToHeritage.navigateToHeritageFunnel = jest.fn(); - - // Act - await wrapper.setData({ - isCarIdDifferent: false, - }); - await wrapper.vm.navigateForward(); + // Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + wrapper.vm.$refs.loadingModal.showModal = jest.fn(); + navigateToHeritage.navigateToHeritageFunnel = jest.fn(); - //Assert - expect(wrapper.vm.navigateForwardWithSingleCarMatch).toBeCalledTimes(1); + // Act + await wrapper.setData({ + isCarIdDifferent: false, + }); + await wrapper.vm.navigateForward(); - wrapper.unmount(); + //Assert + expect(wrapper.vm.navigateForwardWithSingleCarMatch).toBeCalledTimes(1); + + wrapper.unmount(); }); }); function setupMocks({}) { - //Mock store - store.commit(storeMutations.RESET_STATE); - store.commit(storeMutations.UPDATE_PAGE_DATA, { - page: "address-vehicles", - data: [{ - vehicle: { - "carId": "CR00069309", - "category": "SUV", - "year": 2020, - "make": "Hyundai", - "model": "Santa Fe", - "style": "4 door utility", - "imageUrl": "https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13769/13769_cc0320_032_WW8.jpg", - "imageVifNumber": "13769", - "imageVifColor": "white" - }, - vin: "5NMS3CADXLH233004" - }], - }) + //Mock store + store.commit(storeMutations.RESET_STATE); + store.commit(storeMutations.UPDATE_PAGE_DATA, { + page: "address-vehicles", + data: [ + { + vehicle: { + carId: "CR00069309", + category: "SUV", + year: 2020, + make: "Hyundai", + model: "Santa Fe", + style: "4 door utility", + imageUrl: + "https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13769/13769_cc0320_032_WW8.jpg", + imageVifNumber: "13769", + imageVifColor: "white", + }, + vin: "5NMS3CADXLH233004", + }, + ], + }); - const mountOptions = getMountOptions({ - router: { - navigate: jest.fn(), - }, - actionList: [ - { - actionName: storeActions.LOOKUP_VEHICLE_BY_VIN, - data: {} - } - ] - }); + const mountOptions = getMountOptions({ + router: { + navigate: jest.fn(), + }, + actionList: [ + { + actionName: storeActions.LOOKUP_VEHICLE_BY_VIN, + data: {}, + }, + ], + }); - //Mock props - const mockMixin = { - methods: { - getCmsContent: jest.fn((contentName) => { - if (contentName === "FoundMultipleVehicles") { - return 'FoundMultipleVehiclesTestReturn'; - } - if (contentName === "ProvideVinAlert") { - return 'ProvideVinAlertTestReturn'; - } - return null; - }), - }, - computed: { - dynamicStrings() { - return {ROUTER_LINK: "routerLink:"} - } - } - } + //Mock props + const mockMixin = { + methods: { + getCmsContent: jest.fn((contentName) => { + if (contentName === "FoundMultipleVehicles") { + return "FoundMultipleVehiclesTestReturn"; + } + if (contentName === "ProvideVinAlert") { + return "ProvideVinAlertTestReturn"; + } + return null; + }), + }, + computed: { + dynamicStrings() { + return { ROUTER_LINK: "routerLink:" }; + }, + }, + }; - mountOptions.mixins = [mockMixin]; - - const wrapper = shallowMount(addressVehicles, mountOptions); + mountOptions.mixins = [mockMixin]; - wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); - wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); + const wrapper = shallowMount(addressVehicles, mountOptions); - return { wrapper }; + wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); + + return { wrapper }; } diff --git a/src/layouts/address-vehicles/address-vehicles.vue b/src/layouts/address-vehicles/address-vehicles.vue index 2249ba2ab..8ae84430e 100644 --- a/src/layouts/address-vehicles/address-vehicles.vue +++ b/src/layouts/address-vehicles/address-vehicles.vue @@ -1,48 +1,48 @@ diff --git a/src/layouts/capability-questions/capability-questions.vue b/src/layouts/capability-questions/capability-questions.vue index 6491a76df..7cf03dace 100644 --- a/src/layouts/capability-questions/capability-questions.vue +++ b/src/layouts/capability-questions/capability-questions.vue @@ -3,21 +3,32 @@
- +
-
-
-
@@ -32,7 +43,7 @@ import alert from "@/ux-components/alert/alert"; import questionChain from "@/common-components/question-chain/question-chain"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; -import loadingModal from '@/common-components/loading-modal/loading-modal.vue'; +import loadingModal from "@/common-components/loading-modal/loading-modal.vue"; // Supporting Files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; @@ -73,7 +84,8 @@ export default { }, data() { return { - capabilityQuestionsData: store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS).partsOrQuestions, + capabilityQuestionsData: store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS) + .partsOrQuestions, selectedAnswers: {}, currentQuestionChainIndex: 0, }; @@ -86,40 +98,58 @@ export default { return this.getCmsContent("AdditionalPartsQuestionsAlert", "BodyText"); }, windshieldPart() { - return this.pageData.partsOrQuestions.find(x => x.location === damageLocationsSelected.WINDSHIELD); + return this.pageData.partsOrQuestions.find( + (x) => x.location === damageLocationsSelected.WINDSHIELD + ); }, windshieldPartInfo() { return this.windshieldPart.parts[0]; }, pageData() { return this.$store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS); - } + }, }, mounted() { // are there alreadyAnsweredQuestions? const alreadyAnsweredQuestions = store.getters.damage.capabilityQuestionAnswers; - this.capabilityQuestionsData = this.pageData.partsOrQuestions.filter(x => x.capabilityQuestions).map((glass, i) => { - // NOTE: questions for property "questions" can differ between layouts - glass.questions = glass.capabilityQuestions; - // reset selectedAnswers for this glass - this.selectedAnswers[glass.key] = []; - // pass in: glass, i, alreadyAnsweredQuestions - return this.setupInitialData(glass, i, alreadyAnsweredQuestions); - }); + this.capabilityQuestionsData = this.pageData.partsOrQuestions + .filter((x) => x.capabilityQuestions) + .map((glass, i) => { + // NOTE: questions for property "questions" can differ between layouts + glass.questions = glass.capabilityQuestions; + // reset selectedAnswers for this glass + this.selectedAnswers[glass.key] = []; + // pass in: glass, i, alreadyAnsweredQuestions + return this.setupInitialData(glass, i, alreadyAnsweredQuestions); + }); }, methods: { showThisQuestionChain(glass, i) { - if (!glass.capabilityQuestions || glass.capabilityQuestions?.length < 1 || glass.isSuppressedPart) { return false; } // return false if no capabilityQuestions or if suppressed - return this.currentQuestionChainIndex === i || glass.answerData?.answerResult?.length > 0; + if ( + !glass.capabilityQuestions || + glass.capabilityQuestions?.length < 1 || + glass.isSuppressedPart + ) { + return false; + } // return false if no capabilityQuestions or if suppressed + return ( + this.currentQuestionChainIndex === i || glass.answerData?.answerResult?.length > 0 + ); }, arePagePrerequisitesValid() { - const capabilityQuestionsPageData = store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS); - return capabilityQuestionsPageData && Object.keys(capabilityQuestionsPageData).length > 0; + const capabilityQuestionsPageData = store.getters.pageData( + fmgPageValues.CAPABILITY_QUESTIONS + ); + return ( + capabilityQuestionsPageData && Object.keys(capabilityQuestionsPageData).length > 0 + ); }, async forwardButtonAction() { const capabilityQuestionsAnswersArray = this.capabilityQuestionsData.map((glass) => { - const selectedAnswerResult2 = this.getCorrespondingAnswerResult2(glass.answerData.answerResult); + const selectedAnswerResult2 = this.getCorrespondingAnswerResult2( + glass.answerData.answerResult + ); return { location: glass.location, @@ -138,15 +168,29 @@ export default { }); // save to vuex store as order.damage.capabilityQuestionAnswers (array) - await this.dispatchStoreAction(this.storeActions.SAVE_CAPABILITY_QUESTION_ANSWERS, capabilityQuestionsAnswersArray, false); + await this.dispatchStoreAction( + this.storeActions.SAVE_CAPABILITY_QUESTION_ANSWERS, + capabilityQuestionsAnswersArray, + false + ); // get parts from the capabilityQuestionAnswers let partsOrQuestions = this.pageData.partsOrQuestions; for (let answer of capabilityQuestionsAnswersArray) { - const correspondingPart = partsOrQuestions.find(partOrQuestion => partOrQuestion.location === answer.location); - const partFromCapabilityQuestionAnswer = (await this.dispatchStoreAction(storeActions.GET_PART_FROM_CAPABILITY_QUESTION_ANSWER, answer.location, false)).data; + const correspondingPart = partsOrQuestions.find( + (partOrQuestion) => partOrQuestion.location === answer.location + ); + const partFromCapabilityQuestionAnswer = ( + await this.dispatchStoreAction( + storeActions.GET_PART_FROM_CAPABILITY_QUESTION_ANSWER, + answer.location, + false + ) + ).data; - partsOrQuestions.find(partOrQuestion => partOrQuestion.location === answer.location).parts = partFromCapabilityQuestionAnswer; + partsOrQuestions.find( + (partOrQuestion) => partOrQuestion.location === answer.location + ).parts = partFromCapabilityQuestionAnswer; } this.navigateForward(partsOrQuestions); @@ -154,7 +198,7 @@ export default { handleAnswerUpdates(answer) { // only runs when all questions in a question-chain have been answered // when selectedAnswers updates, user has completed this part's question chain and has a final answer - // (does not get run for each invididual question's answer, only when + // (does not get run for each invididual question's answer, only when // all relevant questions for the current part have been answered) /* @@ -182,7 +226,6 @@ export default { // loop through every answered question on the currently answered glass part answer.answeredQuestions?.forEach((aq) => { - // keep track of this question number answeredQuestionIndexes.push(aq.questionNum); @@ -193,10 +236,8 @@ export default { // loop through all glass parts data this.capabilityQuestionsData.forEach((glass, gpIndex) => { - // only look for duplicates forward... to parts that follow after the currently being answered part if (gpIndex > answer.glass) { - let suppressUntil; // reset this glass part, in case user is changing their previous answers glass.answerData = null; @@ -204,7 +245,6 @@ export default { // loop through this glass part's part questions, looking for a questionText match glass.capabilityQuestions.forEach((pq, pqIndex) => { - // clear out any previously set answers pq.answerSelected = null; @@ -222,8 +262,8 @@ export default { // if these match then we have a duplicate question if (pq.questionText.toUpperCase() === answeredQuestionText) { - - const thisAnsweredCapabilityQuestion = glass.capabilityQuestions[pqIndex]; + const thisAnsweredCapabilityQuestion = + glass.capabilityQuestions[pqIndex]; let matchedAnswer; let rejectedAnswers = []; // set as an array, in case we ever have questions with more than 2 answers... @@ -239,17 +279,26 @@ export default { }); // Update the key to re-render this part's question-chain component - this.capabilityQuestionsData[gpIndex].key = this.capabilityQuestionsData[gpIndex].location + this.capabilityQuestionsData[gpIndex].name + Date.now().toString(); + this.capabilityQuestionsData[gpIndex].key = + this.capabilityQuestionsData[gpIndex].location + + this.capabilityQuestionsData[gpIndex].name + + Date.now().toString(); // handle suppressing downstream in this question chain if (matchedAnswer.nextQuestionSequence) { // ensure that the question that the accepted answer has set to be next is NOT suppressed - glass.capabilityQuestions[matchedAnswer.nextQuestionSequence - 1].suppressQuestion = null; + glass.capabilityQuestions[ + matchedAnswer.nextQuestionSequence - 1 + ].suppressQuestion = null; // if the duplicate is the 1ST question, then set suppressUntil var to lowest nextQuestion number if (pqIndex === 0) { - if (!suppressUntil) { suppressUntil = matchedAnswer.nextQuestionSequence } - if (matchedAnswer.nextQuestionSequence < suppressUntil) { suppressUntil = matchedAnswer.nextQuestionSequence } + if (!suppressUntil) { + suppressUntil = matchedAnswer.nextQuestionSequence; + } + if (matchedAnswer.nextQuestionSequence < suppressUntil) { + suppressUntil = matchedAnswer.nextQuestionSequence; + } } } @@ -257,9 +306,13 @@ export default { glass.capabilityQuestions.forEach((q) => { q.answers.forEach((thisAns) => { // restore any of the answers that formerly led to the duplicated question - if (thisAns.originalNextQuestionSequence === pq.questionSequence) { + if ( + thisAns.originalNextQuestionSequence === + pq.questionSequence + ) { // restore original nextQuestionSequence - thisAns.nextQuestionSequence = thisAns.originalNextQuestionSequence; + thisAns.nextQuestionSequence = + thisAns.originalNextQuestionSequence; this.originalNextQuestionSequence = null; // restore original answerResult if (thisAns.originalAnswerResult) { @@ -271,12 +324,17 @@ export default { if (thisAns.nextQuestionSequence === pq.questionSequence) { // update either the nextQuestionSequence or the answerResult if (matchedAnswer.nextQuestionSequence) { - thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence; - thisAns.nextQuestionSequence = matchedAnswer.nextQuestionSequence; + thisAns.originalNextQuestionSequence = + thisAns.nextQuestionSequence; + thisAns.nextQuestionSequence = + matchedAnswer.nextQuestionSequence; } else { - thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence; + thisAns.originalNextQuestionSequence = + thisAns.nextQuestionSequence; thisAns.nextQuestionSequence = null; - thisAns.originalAnswerResult = thisAns.originalAnswerResult || thisAns.answerResult; + thisAns.originalAnswerResult = + thisAns.originalAnswerResult || + thisAns.answerResult; thisAns.answerResult = matchedAnswer.answerResult; } } @@ -288,11 +346,19 @@ export default { const thisGlassPart = "glass" + gpIndex; if (foundDuplicateQuestions[thisGlassPart]) { - if (!foundDuplicateQuestions[thisGlassPart].includes(thisAnsweredCapabilityQuestion.questionSequence)) { - foundDuplicateQuestions[thisGlassPart].push(thisAnsweredCapabilityQuestion.questionSequence); + if ( + !foundDuplicateQuestions[thisGlassPart].includes( + thisAnsweredCapabilityQuestion.questionSequence + ) + ) { + foundDuplicateQuestions[thisGlassPart].push( + thisAnsweredCapabilityQuestion.questionSequence + ); } } else { - foundDuplicateQuestions[thisGlassPart] = [thisAnsweredCapabilityQuestion.questionSequence]; + foundDuplicateQuestions[thisGlassPart] = [ + thisAnsweredCapabilityQuestion.questionSequence, + ]; } // are there any questions left that are not suppressed? @@ -312,28 +378,30 @@ export default { }; // set the answerData as 'already answered' glass.answerData = { - answerResult: matchedAnswer.nextQuestionSequence ? matchedAnswer.nextQuestionSequence : matchedAnswer.answerResult, + answerResult: matchedAnswer.nextQuestionSequence + ? matchedAnswer.nextQuestionSequence + : matchedAnswer.answerResult, answeredQuestions: [answeredQuestionObj], }; // suppress this glass because it has an answer glass.isSuppressedPart = true; } - } - }); // Update the key to re-render this part's question-chain component - this.capabilityQuestionsData[gpIndex].key = this.capabilityQuestionsData[gpIndex].location + this.capabilityQuestionsData[gpIndex].name + Date.now().toString(); - + this.capabilityQuestionsData[gpIndex].key = + this.capabilityQuestionsData[gpIndex].location + + this.capabilityQuestionsData[gpIndex].name + + Date.now().toString(); } }); }); // DETERMINE ANSWERED QUESTIONS LIST FOR THIS GLASS PART - // look through all (this part's) part questions for any duplicates that were suppressed; + // look through all (this part's) part questions for any duplicates that were suppressed; // add them to the list of answered questions if found // EX answeredQuestionIndexes: [1,5,11,13] @@ -344,7 +412,9 @@ export default { // }; const thisPartsDupes = foundDuplicateQuestions["glass" + answer.glassIndex]; - const completeAnsweredQuestions = answer.answeredQuestions ? [...answer.answeredQuestions] : []; + const completeAnsweredQuestions = answer.answeredQuestions + ? [...answer.answeredQuestions] + : []; const glassPartAnswered = this.capabilityQuestionsData[answer.glassIndex]; thisPartsDupes?.forEach((dupe) => { @@ -357,15 +427,21 @@ export default { // did one of the answers of this question point to the duplicated question? q.answers.forEach((a) => { - if ((dupe === a.originalNextQuestionSequence) && - (answeredQuestionIndexes.includes(q.questionSequence)) && - (a.answerText.toUpperCase() === dupeQuestionAnswer.answerText.toUpperCase())) { + if ( + dupe === a.originalNextQuestionSequence && + answeredQuestionIndexes.includes(q.questionSequence) && + a.answerText.toUpperCase() === + dupeQuestionAnswer.answerText.toUpperCase() + ) { includeThisDupeInAnsweredQuestions = true; } }); // is this q.questionSequence listed as the duplicated question's nextQuestionSequence? - if ((q.questionSequence === dupeQuestionAnswer.nextQuestionSequence) && (answeredQuestionIndexes.includes(q.questionSequence))) { + if ( + q.questionSequence === dupeQuestionAnswer.nextQuestionSequence && + answeredQuestionIndexes.includes(q.questionSequence) + ) { includeThisDupeInAnsweredQuestions = true; } @@ -382,18 +458,20 @@ export default { // make sure there are no duplicated dupes in the list... const foundInCompleteAnsweredQuestions = new Set(); - let filteredCompleteAnsweredQuestions = completeAnsweredQuestions.filter(el => { + let filteredCompleteAnsweredQuestions = completeAnsweredQuestions.filter((el) => { const duplicate = foundInCompleteAnsweredQuestions.has(el.questionText); foundInCompleteAnsweredQuestions.add(el.questionText); return !duplicate; }); - filteredCompleteAnsweredQuestions = filteredCompleteAnsweredQuestions.sort((a, b) => a.questionNum - b.questionNum); + filteredCompleteAnsweredQuestions = filteredCompleteAnsweredQuestions.sort( + (a, b) => a.questionNum - b.questionNum + ); // set final answer data for the current answered glass part glassPartAnswered.answerData = { answerResult: answer.answerResult, answeredQuestions: filteredCompleteAnsweredQuestions, - } + }; // this part has been fully answered, so advance to next part's question chain for (let i = answer.glassIndex + 1; i < this.capabilityQuestionsData.length; i++) { @@ -408,10 +486,15 @@ export default { // Find the part that has some answer that has an answerResult matching the selected answerResult, then get the corresponding answerResult2 getCorrespondingAnswerResult2(answerResult1) { return this.capabilityQuestionsData - .find(part => part.capabilityQuestions.some(question => question.answers.some(answer => answer.answerResult == answerResult1))) // found part - .capabilityQuestions.find(question => question.answers.some(answer => answer.answerResult == answerResult1)) // found question - .answers.find(answer => answer.answerResult == answerResult1) - .answerResult2; + .find((part) => + part.capabilityQuestions.some((question) => + question.answers.some((answer) => answer.answerResult == answerResult1) + ) + ) // found part + .capabilityQuestions.find((question) => + question.answers.some((answer) => answer.answerResult == answerResult1) + ) // found question + .answers.find((answer) => answer.answerResult == answerResult1).answerResult2; }, }, components: { @@ -430,7 +513,7 @@ export default { \ No newline at end of file + font-weight: 500; + color: $black; +} +div .current_car_info-text { + padding-bottom: 16px; +} + diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.spec.js b/src/layouts/license-plate-lookup/license-plate-lookup.spec.js index ca9c235b0..5c562d002 100644 --- a/src/layouts/license-plate-lookup/license-plate-lookup.spec.js +++ b/src/layouts/license-plate-lookup/license-plate-lookup.spec.js @@ -12,590 +12,607 @@ import { storeActions } from "@/constants/store-actions"; import { storeMutations } from "@/constants/store-mutations"; import store from "@/store"; -jest.mock('@/assets/img/loader.gif', () => 'loader.gif') -jest.mock('@/assets/img/windshield.png', () => 'windshield.png') +jest.mock("@/assets/img/loader.gif", () => "loader.gif"); +jest.mock("@/assets/img/windshield.png", () => "windshield.png"); // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ - settleAllPromises: jest.fn(), + settleAllPromises: jest.fn(), })); // Mock fetchCmsContentForPage jest.mock("@/helpers/cms-content-helper", () => ({ - fetchCmsContentForPage: jest.fn(), + fetchCmsContentForPage: jest.fn(), })); // Mock damage helper jest.mock("@/helpers/damage-helper", () => ({ - isGlassAvailableForCarId: () => { return false; }, - getDamageString: () => { return 'damage string'; } + isGlassAvailableForCarId: () => { + return false; + }, + getDamageString: () => { + return "damage string"; + }, })); describe("license-plate-lookup.vue", () => { - describe("get values from store", () => { - test("getLicensePlateFromStore returns store license plate", async () => { - // Arrange - const { wrapper } = setupMocks({}); - const mockLicensePlate = "TESTPLATE"; - store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, mockLicensePlate); + describe("get values from store", () => { + test("getLicensePlateFromStore returns store license plate", async () => { + // Arrange + const { wrapper } = setupMocks({}); + const mockLicensePlate = "TESTPLATE"; + store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, mockLicensePlate); - // Act - const licensePlate = wrapper.vm.getLicensePlateFromStore(); + // Act + const licensePlate = wrapper.vm.getLicensePlateFromStore(); - // Assert - expect(licensePlate).toEqual(mockLicensePlate); - }); - - test("getRegistrationZipFromStore returns store registration zip", async () => { - // Arrange - const { wrapper } = setupMocks({}); - const mockRegistrationZip = "12345"; - store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, mockRegistrationZip); - - // ACT - const registrationZip = wrapper.vm.getRegistrationZipFromStore(); - - // Assert - expect(registrationZip).toEqual(mockRegistrationZip); - }); - - test("getEmailFromStore returns store customer email", async () => { - // Arrange - const { wrapper } = setupMocks({}); - const mockEmail = "test@test.com"; - store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, mockEmail); - - // ACT - const customerEmail = wrapper.vm.getEmailFromStore(); - - // Assert - expect(customerEmail).toEqual(mockEmail); - }); - - test("getServiceZipFromStore returns store service zip", async () => { - // Arrange - const { wrapper } = setupMocks({}); - const mockServiceZip = "12345"; - store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP_CODE, mockServiceZip); - - // ACT - const serviceZip = wrapper.vm.getServiceZipFromStore(); - - // Assert - expect(serviceZip).toEqual(mockServiceZip); - }); - }); - - describe("navigation", () => { - test("BackButtonAction triggers a router.navigateWithoutSaving change", async () => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - licensePlateLookup.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "license-plate-lookup" } }, - undefined, - (c) => c(wrapper.vm) - ); - - wrapper.vm.backButtonAction(); - - //Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); - }); - - describe("on forwardButtonAction click", () => { - test("Navigate forward should be called and isCarIdDifferent should be set to false when data entered matches store data on forwardButtonAction click", async () => { - - // Arrange - const mockCarId = "TESTID"; - const { wrapper } = setupMocks({ carId: mockCarId, isServiceable: true }); - - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); - wrapper.vm.navigateForward = jest.fn(); - wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => Promise.resolve({ - data: { - vehicle: { - carId: mockCarId - } - } - })); - - //Act - licensePlateLookup.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "license-plate-lookup" } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.forwardButtonAction(); - - //Assert - expect(wrapper.vm.isCarIdDifferent).toEqual(false); - expect(wrapper.vm.navigateForward).toHaveBeenCalled(); - }); - - - - test("Function should stop and datam isCarIdDifferent should be set to true when carId entered doesn't match store carId or previously entered carId on forwardButtonAction click", async () => { - // Arrange - - // Setup state data / return data. - const { wrapper } = setupMocks({ carId: "C111111", isServiceable: true }); - store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, []); - - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => { - return ''; + // Assert + expect(licensePlate).toEqual(mockLicensePlate); }); - // Mock store action call - wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => Promise.resolve({ - data: { - vehicle: { - carId: "C00000" // Make sure carId returned from call does not match carId in state. - } - } - })); + test("getRegistrationZipFromStore returns store registration zip", async () => { + // Arrange + const { wrapper } = setupMocks({}); + const mockRegistrationZip = "12345"; + store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, mockRegistrationZip); - //Act - licensePlateLookup.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "license-plate-lookup" } }, - undefined, - (c) => c(wrapper.vm) - ); + // ACT + const registrationZip = wrapper.vm.getRegistrationZipFromStore(); - await wrapper.vm.forwardButtonAction(); - - //Assert - expect(wrapper.vm.isCarIdDifferent).toEqual(true); - }); - - test("Navigate forward should be called and isCarId should be set to true when carId entered matches previously entered carId and rest of data entered matches store data on forwardButtonAction click", async () => { - - // Arrange - const { wrapper } = setupMocks({ carId: "C10000", isServiceable: true }); - - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => { - return ''; + // Assert + expect(registrationZip).toEqual(mockRegistrationZip); }); - wrapper.vm.previouslyEnteredCarId = "C00000"; - wrapper.vm.navigateForward = jest.fn(); + test("getEmailFromStore returns store customer email", async () => { + // Arrange + const { wrapper } = setupMocks({}); + const mockEmail = "test@test.com"; + store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, mockEmail); - // Mock store action call - wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => Promise.resolve({ - data: { - vehicle: { - carId: "C00000" // Make sure carId returned from call does not match carId in state. - } - } - })); + // ACT + const customerEmail = wrapper.vm.getEmailFromStore(); - //Act - licensePlateLookup.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "license-plate-lookup" } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.forwardButtonAction(); - - //Assert - expect(wrapper.vm.navigateForward).toHaveBeenCalled(); - expect(wrapper.vm.isCarIdDifferent).toEqual(true); - }); - }); - - describe("navigateForward", () => { - test("navigateWithSaving should be called if isCarIdDifferent is true and isSelectedGlassAvailableForVehicle is false when navigateForward is called", async () => { - - // Arrange - const { wrapper } = setupMocks({}); - - //Act - await wrapper.setData({ - isCarIdDifferent: true, - isSelectedGlassAvailableForVehicle: false - }) - - wrapper.vm.$router.navigate = jest.fn(); - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => { - return ''; + // Assert + expect(customerEmail).toEqual(mockEmail); }); - wrapper.vm.dispatchStoreAction = jest.fn(); - await wrapper.vm.navigateForward(); + test("getServiceZipFromStore returns store service zip", async () => { + // Arrange + const { wrapper } = setupMocks({}); + const mockServiceZip = "12345"; + store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP_CODE, mockServiceZip); - //Assert - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); - }); + // ACT + const serviceZip = wrapper.vm.getServiceZipFromStore(); - test("navigateForwardWithSingleCarMatch should be called if isCarIdDifferent is false or isSelectedGlassAvailableForVehicle is true when navigateForward is called", async () => { - - // Arrange - const { wrapper } = setupMocks({}); - - //Act - await wrapper.setData({ - isCarIdDifferent: false - }) - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => { - return ''; + // Assert + expect(serviceZip).toEqual(mockServiceZip); }); - wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); - await wrapper.vm.navigateForward(); - - //Assert - expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalled(); - }); - - test("carId matches returned vehicle => navigateForwardWithSingleCarMatch", async () => { - // Arrange - const { wrapper } = setupMocks({}); - await wrapper.setData({ - isCarIdDifferent: false - }) - - wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); - - // Act - await wrapper.vm.navigateForward(); - - //Assert - expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1); - }); - - test("selected glass is available for returned vehicle => navigateForwardWithSingleCarMatch", async () => { - // Arrange - const { wrapper } = setupMocks({}); - await wrapper.setData({ - isSelectedGlassAvailableForVehicle: true - }) - - wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); - - // Act - await wrapper.vm.navigateForward(); - - //Assert - expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe("button text", () => { - test("Button Text should revert to initial value when licensePlate textfield has new text", async () => { - - // Arrange - const { wrapper } = setupMocks({}); - - //Act - wrapper.setData({ - licensePlate: "NEWPLATE" - }) - wrapper.vm.getCmsContent = jest.fn(); - await wrapper.vm.$nextTick(); - - //Assert - expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled(); }); - test("Button Text should revert to initial value when registrationZip textfield has new text", async () => { + describe("navigation", () => { + test("BackButtonAction triggers a router.navigateWithoutSaving change", async () => { + //Arrange + const { wrapper } = setupMocks({}); - // Arrange - const { wrapper } = setupMocks({}); + //Act + licensePlateLookup.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "license-plate-lookup" } }, + undefined, + (c) => c(wrapper.vm) + ); - //Act - await wrapper.setData({ - registrationZipCode: "55555" - }) - wrapper.vm.getCmsContent = jest.fn(); - await wrapper.vm.$nextTick(); + wrapper.vm.backButtonAction(); - //Assert - expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled(); + //Assert + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); + }); + + describe("on forwardButtonAction click", () => { + test("Navigate forward should be called and isCarIdDifferent should be set to false when data entered matches store data on forwardButtonAction click", async () => { + // Arrange + const mockCarId = "TESTID"; + const { wrapper } = setupMocks({ carId: mockCarId, isServiceable: true }); + + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); + wrapper.vm.navigateForward = jest.fn(); + wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => + Promise.resolve({ + data: { + vehicle: { + carId: mockCarId, + }, + }, + }) + ); + + //Act + licensePlateLookup.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "license-plate-lookup" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.isCarIdDifferent).toEqual(false); + expect(wrapper.vm.navigateForward).toHaveBeenCalled(); + }); + + test("Function should stop and datam isCarIdDifferent should be set to true when carId entered doesn't match store carId or previously entered carId on forwardButtonAction click", async () => { + // Arrange + + // Setup state data / return data. + const { wrapper } = setupMocks({ carId: "C111111", isServiceable: true }); + store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, []); + + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => { + return ""; + }); + + // Mock store action call + wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => + Promise.resolve({ + data: { + vehicle: { + carId: "C00000", // Make sure carId returned from call does not match carId in state. + }, + }, + }) + ); + + //Act + licensePlateLookup.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "license-plate-lookup" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.isCarIdDifferent).toEqual(true); + }); + + test("Navigate forward should be called and isCarId should be set to true when carId entered matches previously entered carId and rest of data entered matches store data on forwardButtonAction click", async () => { + // Arrange + const { wrapper } = setupMocks({ carId: "C10000", isServiceable: true }); + + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => { + return ""; + }); + + wrapper.vm.previouslyEnteredCarId = "C00000"; + wrapper.vm.navigateForward = jest.fn(); + + // Mock store action call + wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => + Promise.resolve({ + data: { + vehicle: { + carId: "C00000", // Make sure carId returned from call does not match carId in state. + }, + }, + }) + ); + + //Act + licensePlateLookup.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "license-plate-lookup" } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.forwardButtonAction(); + + //Assert + expect(wrapper.vm.navigateForward).toHaveBeenCalled(); + expect(wrapper.vm.isCarIdDifferent).toEqual(true); + }); + }); + + describe("navigateForward", () => { + test("navigateWithSaving should be called if isCarIdDifferent is true and isSelectedGlassAvailableForVehicle is false when navigateForward is called", async () => { + // Arrange + const { wrapper } = setupMocks({}); + + //Act + await wrapper.setData({ + isCarIdDifferent: true, + isSelectedGlassAvailableForVehicle: false, + }); + + wrapper.vm.$router.navigate = jest.fn(); + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => { + return ""; + }); + wrapper.vm.dispatchStoreAction = jest.fn(); + + await wrapper.vm.navigateForward(); + + //Assert + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); + }); + + test("navigateForwardWithSingleCarMatch should be called if isCarIdDifferent is false or isSelectedGlassAvailableForVehicle is true when navigateForward is called", async () => { + // Arrange + const { wrapper } = setupMocks({}); + + //Act + await wrapper.setData({ + isCarIdDifferent: false, + }); + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => { + return ""; + }); + wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); + await wrapper.vm.navigateForward(); + + //Assert + expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalled(); + }); + + test("carId matches returned vehicle => navigateForwardWithSingleCarMatch", async () => { + // Arrange + const { wrapper } = setupMocks({}); + await wrapper.setData({ + isCarIdDifferent: false, + }); + + wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); + + // Act + await wrapper.vm.navigateForward(); + + //Assert + expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1); + }); + + test("selected glass is available for returned vehicle => navigateForwardWithSingleCarMatch", async () => { + // Arrange + const { wrapper } = setupMocks({}); + await wrapper.setData({ + isSelectedGlassAvailableForVehicle: true, + }); + + wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); + + // Act + await wrapper.vm.navigateForward(); + + //Assert + expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1); + }); + }); }); - test("Button Text should revert to initial value when serviceZip textfield has new text", async () => { + describe("button text", () => { + test("Button Text should revert to initial value when licensePlate textfield has new text", async () => { + // Arrange + const { wrapper } = setupMocks({}); - // Arrange - const { wrapper } = setupMocks({}); + //Act + wrapper.setData({ + licensePlate: "NEWPLATE", + }); + wrapper.vm.getCmsContent = jest.fn(); + await wrapper.vm.$nextTick(); - //Act - await wrapper.setData({ - serviceZipCode: "55555" - }) - wrapper.vm.getCmsContent = jest.fn(); - await wrapper.vm.$nextTick(); + //Assert + expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled(); + }); - //Assert - expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled(); - }); - }) + test("Button Text should revert to initial value when registrationZip textfield has new text", async () => { + // Arrange + const { wrapper } = setupMocks({}); - describe("saving registrationZip and serviceZip on continue", () => { - test("registrationZip is serviceable and vehicle match is found => sets service zip/state to registration zip/state", async () => { - // Arrange - const { wrapper } = setupMocks({}); - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); + //Act + await wrapper.setData({ + registrationZipCode: "55555", + }); + wrapper.vm.getCmsContent = jest.fn(); + await wrapper.vm.$nextTick(); - await wrapper.setData({ registrationZipCode: "00000" }); - navigateToHeritage.navigateToHeritageFunnel = jest.fn(); + //Assert + expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled(); + }); - wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => Promise.resolve({ - data: { - vehicle: { - carId: "C00000" - } - } - })); + test("Button Text should revert to initial value when serviceZip textfield has new text", async () => { + // Arrange + const { wrapper } = setupMocks({}); - // Act - await wrapper.vm.forwardButtonAction(); + //Act + await wrapper.setData({ + serviceZipCode: "55555", + }); + wrapper.vm.getCmsContent = jest.fn(); + await wrapper.vm.$nextTick(); - // Assert - expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual(wrapper.vm.$store.getters.vehicle.registration.zipCode); - expect(wrapper.vm.$store.getters.vehicle.registration.zipCode).toEqual("12345"); - expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual("12345"); - }) - - test("vehicle match is found but registrationZip is not serviceable => shows service zip/state field", async () => { - // Arrange - const { wrapper } = setupMocks({}); - wrapper.vm.validateZip = jest.fn().mockImplementation(() => { - return { data: { isServiceable: false, state: "XX" } }; - }); - - await wrapper.setData({ registrationZip: "00000" }); - navigateToHeritage.navigateToHeritageFunnel = jest.fn(); - - wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => Promise.resolve({ - data: { - vehicle: { - carId: "C00000" - } - } - })); - - // Act - await wrapper.vm.forwardButtonAction(); - - // Assert - const serviceZipField = wrapper.findComponent("[cmsWidgetName='ServiceZipQuestionWidget']"); - expect(serviceZipField.exists()).toBe(true); - expect(serviceZipField.isVisible()).toBe(true); - }) - - test("registrationZip is not serviceable so serviceZip field is shown, continue clicked => user cannot continue", async () => { - // Arrange - const { wrapper } = setupMocks({}); - wrapper.vm.validateZip = jest.fn().mockImplementation(() => { - return { data: { isServiceable: false, state: "XX" } }; - }); - - await wrapper.setData({ registrationZipCode: "00000" }); - navigateToHeritage.navigateToHeritageFunnel = jest.fn(); - - wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => Promise.resolve({ - data: { - vehicle: { - carId: "C00000" - } - } - })); - - await wrapper.vm.forwardButtonAction(); - wrapper.vm.$router.navigate = jest.fn(); - // At this point, serviceZip field is shown - - // Act - // Continue without entering anything into service zip field - await wrapper.vm.forwardButtonAction(); - - // Assert - const serviceZipField = wrapper.findComponent("[cmsWidgetName='ServiceZipQuestionWidget']"); - expect(serviceZipField.exists()).toBe(true); - expect(serviceZipField.isVisible()).toBe(true); 3 - expect(navigateToHeritage.navigateToHeritageFunnel).not.toHaveBeenCalled(); - expect(wrapper.vm.$router.navigate).not.toHaveBeenCalled(); + //Assert + expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled(); + }); }); - test("registrationZip is not serviceable so serviceZip field is shown, user enters serviceZip => user can continue", async () => { - // Arrange - const { wrapper } = setupMocks({ isServiceable: false}); - const registrationZip = "00000"; - const serviceZip = "99999"; + describe("saving registrationZip and serviceZip on continue", () => { + test("registrationZip is serviceable and vehicle match is found => sets service zip/state to registration zip/state", async () => { + // Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); - wrapper.vm.navigateForward = jest.fn(); - wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => Promise.resolve({ - data: { - vehicle: { - carId: "C00000" - } - } - })); + await wrapper.setData({ registrationZipCode: "00000" }); + navigateToHeritage.navigateToHeritageFunnel = jest.fn(); - await wrapper.setData({ registrationZipCode: registrationZip }); - await wrapper.vm.forwardButtonAction(); + wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => + Promise.resolve({ + data: { + vehicle: { + carId: "C00000", + }, + }, + }) + ); - // At this point, serviceZip field is shown - await wrapper.setData({ serviceZipCode: serviceZip }); + // Act + await wrapper.vm.forwardButtonAction(); - // Act - - // Continue after entering input into service zip field - await wrapper.vm.forwardButtonAction(); + // Assert + expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual( + wrapper.vm.$store.getters.vehicle.registration.zipCode + ); + expect(wrapper.vm.$store.getters.vehicle.registration.zipCode).toEqual("12345"); + expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual("12345"); + }); - // Assert - const serviceZipField = wrapper.findComponent("[cmsWidgetName='ServiceZipQuestionWidget']"); - expect(serviceZipField.exists()).toBe(true); - expect(serviceZipField.isVisible()).toBe(true); + test("vehicle match is found but registrationZip is not serviceable => shows service zip/state field", async () => { + // Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.validateZip = jest.fn().mockImplementation(() => { + return { data: { isServiceable: false, state: "XX" } }; + }); + + await wrapper.setData({ registrationZip: "00000" }); + navigateToHeritage.navigateToHeritageFunnel = jest.fn(); + + wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => + Promise.resolve({ + data: { + vehicle: { + carId: "C00000", + }, + }, + }) + ); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + const serviceZipField = wrapper.findComponent( + "[cmsWidgetName='ServiceZipQuestionWidget']" + ); + expect(serviceZipField.exists()).toBe(true); + expect(serviceZipField.isVisible()).toBe(true); + }); + + test("registrationZip is not serviceable so serviceZip field is shown, continue clicked => user cannot continue", async () => { + // Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.validateZip = jest.fn().mockImplementation(() => { + return { data: { isServiceable: false, state: "XX" } }; + }); + + await wrapper.setData({ registrationZipCode: "00000" }); + navigateToHeritage.navigateToHeritageFunnel = jest.fn(); + + wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => + Promise.resolve({ + data: { + vehicle: { + carId: "C00000", + }, + }, + }) + ); + + await wrapper.vm.forwardButtonAction(); + wrapper.vm.$router.navigate = jest.fn(); + // At this point, serviceZip field is shown + + // Act + // Continue without entering anything into service zip field + await wrapper.vm.forwardButtonAction(); + + // Assert + const serviceZipField = wrapper.findComponent( + "[cmsWidgetName='ServiceZipQuestionWidget']" + ); + expect(serviceZipField.exists()).toBe(true); + expect(serviceZipField.isVisible()).toBe(true); + 3; + expect(navigateToHeritage.navigateToHeritageFunnel).not.toHaveBeenCalled(); + expect(wrapper.vm.$router.navigate).not.toHaveBeenCalled(); + }); + + test("registrationZip is not serviceable so serviceZip field is shown, user enters serviceZip => user can continue", async () => { + // Arrange + const { wrapper } = setupMocks({ isServiceable: false }); + const registrationZip = "00000"; + const serviceZip = "99999"; + + wrapper.vm.navigateForward = jest.fn(); + wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => + Promise.resolve({ + data: { + vehicle: { + carId: "C00000", + }, + }, + }) + ); + + await wrapper.setData({ registrationZipCode: registrationZip }); + await wrapper.vm.forwardButtonAction(); + + // At this point, serviceZip field is shown + await wrapper.setData({ serviceZipCode: serviceZip }); + + // Act + + // Continue after entering input into service zip field + await wrapper.vm.forwardButtonAction(); + + // Assert + const serviceZipField = wrapper.findComponent( + "[cmsWidgetName='ServiceZipQuestionWidget']" + ); + expect(serviceZipField.exists()).toBe(true); + expect(serviceZipField.isVisible()).toBe(true); + }); + + test("registrationZip is not serviceable so serviceZip field is shown, user enters serviceZip => service and registration zips/states saved", async () => { + // Arrange + const { wrapper } = setupMocks({ isServiceable: true }); + const registrationZip = "12345"; + const serviceZip = "12345"; + + wrapper.vm.navigateForward = jest.fn(); + wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => + Promise.resolve({ + data: { + vehicle: { + carId: "C00000", + }, + }, + }) + ); + + await wrapper.setData({ registrationZipCode: registrationZip }); + await wrapper.vm.forwardButtonAction(); + + // At this point, serviceZip field is shown + await wrapper.setData({ serviceZip: serviceZip }); + + // Act + + // Continue after entering value into service zip field + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.$store.getters.vehicle.registration.zipCode).toEqual(registrationZip); + expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual(serviceZip); + expect(wrapper.vm.navigateForward).toHaveBeenCalled(); + }); }); - test("registrationZip is not serviceable so serviceZip field is shown, user enters serviceZip => service and registration zips/states saved", async () => { - // Arrange - const { wrapper } = setupMocks({ isServiceable: true }); - const registrationZip = "12345"; - const serviceZip = "12345"; + describe("miscellaneous", () => { + test("CarId set, arePagePrerequisitesValid should be true ", async () => { + //Arrange + const { wrapper } = setupMocks({}); + store.commit(storeMutations.UPDATE_CAR_ID, "TESTCARID"); - wrapper.vm.navigateForward = jest.fn(); - wrapper.vm.dispatchStoreAction = jest.fn().mockImplementation(() => Promise.resolve({ - data: { - vehicle: { - carId: "C00000" - } - } - })); + //Act + licensePlateLookup.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "license-plate-lookup" } }, + undefined, + (c) => c(wrapper.vm) + ); - await wrapper.setData({ registrationZipCode: registrationZip }); - await wrapper.vm.forwardButtonAction(); - - // At this point, serviceZip field is shown - await wrapper.setData({ serviceZip: serviceZip }); + let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + await nextTick(); - - // Act - - // Continue after entering value into service zip field - await wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.$store.getters.vehicle.registration.zipCode).toEqual(registrationZip); - expect(wrapper.vm.$store.getters.order.serviceLocation.zipCode).toEqual(serviceZip); - expect(wrapper.vm.navigateForward).toHaveBeenCalled(); + //Assert + expect(arePagePrerequisitesValid).toBe(true); + }); }); - }) - - describe("miscellaneous", () => { - test("CarId set, arePagePrerequisitesValid should be true ", async () => { - //Arrange - const { wrapper } = setupMocks({}); - store.commit(storeMutations.UPDATE_CAR_ID, "TESTCARID"); - - //Act - licensePlateLookup.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "license-plate-lookup" } }, - undefined, - (c) => c(wrapper.vm) - ); - - let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - await nextTick(); - - //Assert - expect(arePagePrerequisitesValid).toBe(true); - }); - - }) }); function setupMocks({ - pageHeaderWidgetHeaderText = {}, - mountOptionsMockData = {}, - partsOrQuestions = [], - isServiceable = false, - carId = "" + pageHeaderWidgetHeaderText = {}, + mountOptionsMockData = {}, + partsOrQuestions = [], + isServiceable = false, + carId = "", }) { - store.commit(storeMutations.RESET_STATE); - //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", - }, - }, - serviceZipValidationResponse: { - isValid: true, - isServiceable: isServiceable - }, - registrationZipValidationResponse: { - state: "CO" - } - }; - - mountOptionsMockData = { - ...mountOptionsMockData, - router: { - navigate: jest.fn(), - navigateWithoutSaving: jest.fn(), - navigateWithSaving: jest.fn() - }, - store: { - getters: { - vehicle: { - registration: { - licensePlate: "TESTPLATE", - zipCode: "12345" - }, - carId: carId + store.commit(storeMutations.RESET_STATE); + //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", + }, }, - order: { - customer: { - emailAddress: "test@test.com" - }, - serviceLocation: { - zipCode: "12345" - } - } - } - }, - actionList: [ - { - actionName: storeActions.GET_PARTS_OR_QUESTIONS, - data: { - partsOrQuestions: partsOrQuestions - } - }, - ] - } + serviceZipValidationResponse: { + isValid: true, + isServiceable: isServiceable, + }, + registrationZipValidationResponse: { + state: "CO", + }, + }; - const apiPromise = Promise.resolve(apiResponses); + mountOptionsMockData = { + ...mountOptionsMockData, + router: { + navigate: jest.fn(), + navigateWithoutSaving: jest.fn(), + navigateWithSaving: jest.fn(), + }, + store: { + getters: { + vehicle: { + registration: { + licensePlate: "TESTPLATE", + zipCode: "12345", + }, + carId: carId, + }, + order: { + customer: { + emailAddress: "test@test.com", + }, + serviceLocation: { + zipCode: "12345", + }, + }, + }, + }, + actionList: [ + { + actionName: storeActions.GET_PARTS_OR_QUESTIONS, + data: { + partsOrQuestions: partsOrQuestions, + }, + }, + ], + }; - settleAllPromises.mockImplementation(() => apiPromise); - fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); + const apiPromise = Promise.resolve(apiResponses); - const mountOptions = getMountOptions(mountOptionsMockData); - mountOptions['attachTo'] = document.body; // append wrapper to document.body to test DOM methods + settleAllPromises.mockImplementation(() => apiPromise); + fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); - const wrapper = shallowMount(licensePlateLookup, mountOptions); + const mountOptions = getMountOptions(mountOptionsMockData); + mountOptions["attachTo"] = document.body; // append wrapper to document.body to test DOM methods - wrapper.vm.setCmsContent = jest.fn(); - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); - wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); - wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); - wrapper.vm.$refs.loadingModal.showModal = jest.fn(); + const wrapper = shallowMount(licensePlateLookup, mountOptions); - return { wrapper, apiPromise }; + wrapper.vm.setCmsContent = jest.fn(); + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); + wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); + wrapper.vm.$refs.loadingModal.showModal = jest.fn(); + + return { wrapper, apiPromise }; } diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.vue b/src/layouts/license-plate-lookup/license-plate-lookup.vue index cfb238980..c0d81f51b 100644 --- a/src/layouts/license-plate-lookup/license-plate-lookup.vue +++ b/src/layouts/license-plate-lookup/license-plate-lookup.vue @@ -1,112 +1,94 @@ diff --git a/src/layouts/molding-questions/molding-questions.vue b/src/layouts/molding-questions/molding-questions.vue index c73c6e5e1..14637cd82 100644 --- a/src/layouts/molding-questions/molding-questions.vue +++ b/src/layouts/molding-questions/molding-questions.vue @@ -1,14 +1,11 @@ @@ -23,55 +22,59 @@ import { errorMessages } from "@/constants/error-messages"; // DEFINE VALIDATION RULES defineRule("damage-location-required", required(errorMessages.DAMAGE_LOCATION_REQUIRED)); -export default ({ +export default { name: "damageLocationQuestion", - data(){ + data() { return { damageOptions: Object, - } + }; }, - props: { + props: { modelValue: Array, groupName: String, cmsWidgetName: String, }, methods: { - initializeComponent(damageOptions){ + initializeComponent(damageOptions) { this.damageOptions = damageOptions; }, }, computed: { - questionText(){ - return this.getCmsContent(this.cmsWidgetName, 'QuestionText'); + questionText() { + return this.getCmsContent(this.cmsWidgetName, "QuestionText"); }, - answersFromCms(){ - return this.getCmsContent(this.cmsWidgetName, 'Answers'); + answersFromCms() { + return this.getCmsContent(this.cmsWidgetName, "Answers"); }, selectedValues: { - get: function() { + get: function () { return this.modelValue; }, - set: function(newValue) { + set: function (newValue) { this.$emit("update:modelValue", newValue); - } + }, }, - damageOptionsMap(){ + damageOptionsMap() { return { Windshield: true, - SideDoor: this.damageOptions.driverSideOptions.availableReplacementOptions.length || this.damageOptions.passengerSideOptions.availableReplacementOptions.length, + SideDoor: + this.damageOptions.driverSideOptions.availableReplacementOptions.length || + this.damageOptions.passengerSideOptions.availableReplacementOptions.length, RearWindow: this.damageOptions.backGlassOptions.availableReplacementOptions.length, - } + }; }, - answersToDisplay(){ - const filteredAnswers = Array.isArray(this.answersFromCms) - ? this.answersFromCms.filter(ans => - { - const name = ans.Name.split('-'); - return name[0].toUpperCase() === store.getters.vehicle.category && this.damageOptionsMap[name[1]]; - }) + answersToDisplay() { + const filteredAnswers = Array.isArray(this.answersFromCms) + ? this.answersFromCms.filter((ans) => { + const name = ans.Name.split("-"); + return ( + name[0].toUpperCase() === store.getters.vehicle.category && + this.damageOptionsMap[name[1]] + ); + }) : []; - return filteredAnswers.map(ans => { - const newName = ans.Name.includes('-') ? ans.Name.split('-')[1] : ans.Name; + return filteredAnswers.map((ans) => { + const newName = ans.Name.includes("-") ? ans.Name.split("-")[1] : ans.Name; ans.Name = newName; return ans; }); @@ -80,5 +83,5 @@ export default ({ components: { buttonQuestion, }, -}) - \ No newline at end of file +}; + diff --git a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js index aa6ecb06d..8b1544af8 100644 --- a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js +++ b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js @@ -3,128 +3,147 @@ import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-que import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { nextTick } from "vue"; import store from "@/store"; -jest.mock("@/store",()=>{return{};},{virtual:true}); +jest.mock( + "@/store", + () => { + return {}; + }, + { virtual: true } +); describe("replace-options-question.vue", () => { - test("Selected damage option is emitted upon selection.", async () => { + test("Selected damage option is emitted upon selection.", async () => { + //Arrange + const { wrapper } = setupMocks({ modelValueProp: ["Windshield"] }); + const damageToSelect = ["Backseat"]; - //Arrange - const { wrapper } = setupMocks({ modelValueProp: ["Windshield"] }); - const damageToSelect = ["Backseat"]; + //Act + wrapper.setValue({ modelValue: damageToSelect }); + await wrapper.vm.$nextTick(); - //Act - wrapper.setValue({ modelValue: damageToSelect }); - await wrapper.vm.$nextTick(); - - //Assert - expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{modelValue: ["Backseat"]}]); - }); + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: ["Backseat"] }]); + }); }); describe("replace-options-question.vue", () => { - test("Answers to display filtered by data from api.", async () => { + test("Answers to display filtered by data from api.", async () => { + //Arrange + const { wrapper, cmsContent, replaceOptions } = setupMocks({ + dataFromStoreApi: ["Windshield", "FrontDoor"], + filterByVehicleCategory: true, + }); - //Arrange - const { wrapper, cmsContent, replaceOptions } = setupMocks({ dataFromStoreApi: ["Windshield", "FrontDoor"], filterByVehicleCategory: true}); + //Act + replaceOptionsQuestion.methods.initializeComponent.call( + wrapper.vm, + replaceOptions, + "car-group" + ); - //Act - replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm, replaceOptions, "car-group"); - - //Assert - expect(wrapper.vm.replaceOptions).toStrictEqual(["Windshield", "FrontDoor"]) - }); + //Assert + expect(wrapper.vm.replaceOptions).toStrictEqual(["Windshield", "FrontDoor"]); + }); }); describe("replace-options-question.vue", () => { - test("when updateSelectedValues method is called with a single answerToDisplay it will call to update this.selectedReplaceOptions", async () => { + test("when updateSelectedValues method is called with a single answerToDisplay it will call to update this.selectedReplaceOptions", async () => { + //Arrange + const { wrapper, cmsContent, replaceOptions } = setupMocks({ + modelValueProp: [], + }); + wrapper.setData({ + answersFromCms: [ + { + Name: "Stationary", + }, + { + Name: "Slider", + }, + ], + }); + wrapper.setData({ replaceOptions: ["Stationary"] }); - //Arrange - const { wrapper, cmsContent, replaceOptions - } = setupMocks({ - modelValueProp: [], - }); - wrapper.setData({answersFromCms: [ - { - "Name": "Stationary", - }, - { - "Name": "Slider", - } - ]}); - wrapper.setData({replaceOptions: ["Stationary"]}); + //Act + wrapper.vm.$options.methods.updateSelectedValues.call(wrapper.vm); - //Act - wrapper.vm.$options.methods.updateSelectedValues.call(wrapper.vm); - - expect(wrapper.vm.selectedValues).toEqual([]); - }); + expect(wrapper.vm.selectedValues).toEqual([]); + }); }); describe("replace-options-question.vue", () => { - test("when isAvailable is true, will run updateSelectedValues method", async () => { + test("when isAvailable is true, will run updateSelectedValues method", async () => { + //Arrange + const { wrapper, cmsContent, replaceOptions } = setupMocks({ + isAvailable: false, + methodsToMock: ["updateSelectedValues"], + }); - //Arrange - const { wrapper, cmsContent, replaceOptions } = setupMocks({ isAvailable: false, methodsToMock: ["updateSelectedValues"] }); + //Act + wrapper.vm.$options.methods.initializeComponent.call( + wrapper.vm, + cmsContent, + replaceOptions, + "car-group" + ); + wrapper.vm.$options.watch.isAvailable.call(wrapper.vm, true); - //Act - wrapper.vm.$options.methods.initializeComponent.call(wrapper.vm, cmsContent, replaceOptions, "car-group"); - wrapper.vm.$options.watch.isAvailable.call(wrapper.vm, true); - - //Assert - expect(replaceOptionsQuestion.methods.updateSelectedValues).toHaveBeenCalled(); - wrapper.unmount(); - }); + //Assert + expect(replaceOptionsQuestion.methods.updateSelectedValues).toHaveBeenCalled(); + wrapper.unmount(); + }); }); function setupMocks({ - modelValueProp = ["Windshield"], - isAvailable = true, - isMultiSelect = false, - filterByVehicleCategory = false, - groupName = "damageQuestion", - cmsQuestionText = "CMS text goes here", - cmsAnswers = [{Name: "car-Windshield"}, {Name: "car-BackDoor"}, {Name: "car-FrontDoor"}], - dataFromStoreApi = [], - methodsToMock = [], + modelValueProp = ["Windshield"], + isAvailable = true, + isMultiSelect = false, + filterByVehicleCategory = false, + groupName = "damageQuestion", + cmsQuestionText = "CMS text goes here", + cmsAnswers = [{ Name: "car-Windshield" }, { Name: "car-BackDoor" }, { Name: "car-FrontDoor" }], + dataFromStoreApi = [], + methodsToMock = [], }) { - - //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 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 - const mockMixin = { - methods: { - getCmsContent: jest.fn() - } - } - mountOptions.propsData = { - modelValue: modelValueProp, - isAvailable: isAvailable, - isMultiSelect: isMultiSelect, - filterByVehicleCategory: filterByVehicleCategory - }; - mountOptions.mixins = [mockMixin]; + //Mock props + const mockMixin = { + methods: { + getCmsContent: jest.fn(), + }, + }; + mountOptions.propsData = { + modelValue: modelValueProp, + isAvailable: isAvailable, + isMultiSelect: isMultiSelect, + filterByVehicleCategory: filterByVehicleCategory, + }; + mountOptions.mixins = [mockMixin]; - //Mock methods - methodsToMock.forEach((methodName) => { - replaceOptionsQuestion.methods[methodName] = jest.fn(); - }); - - const wrapper = shallowMount(replaceOptionsQuestion, mountOptions); + //Mock methods + methodsToMock.forEach((methodName) => { + replaceOptionsQuestion.methods[methodName] = jest.fn(); + }); - //Mock CMS content - const cmsContent = { - groupName: groupName, - QuestionText: cmsQuestionText, - Answers: cmsAnswers, - }; - const replaceOptions = dataFromStoreApi; - return { wrapper, cmsContent, replaceOptions }; -} \ No newline at end of file + const wrapper = shallowMount(replaceOptionsQuestion, mountOptions); + + //Mock CMS content + const cmsContent = { + groupName: groupName, + QuestionText: cmsQuestionText, + Answers: cmsAnswers, + }; + const replaceOptions = dataFromStoreApi; + return { wrapper, cmsContent, replaceOptions }; +} 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 d62f27312..ff2290664 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 @@ -1,6 +1,10 @@ @@ -21,12 +24,12 @@ import buttonQuestion from "@/common-components/button-question/button-question"; import store from "@/store"; -export default ({ +export default { name: "replaceOptionsQuestion", - data(){ + data() { return { replaceOptions: this.isMultiSelect ? [] : "", - } + }; }, props: { isAvailable: Boolean, @@ -40,49 +43,53 @@ export default ({ isRequired: Boolean, }, methods: { - initializeComponent(replaceOptions){ + initializeComponent(replaceOptions) { this.replaceOptions = replaceOptions; }, updateSelectedValues() { // UPDATE SELECTEDVALUES IF ONLY ONE ANSWER - if(Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1) { - this.selectedValues = this.isMultiSelect ? [this.answersToDisplay[0].Name] : this.answersToDisplay[0].Name; + if (Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1) { + this.selectedValues = this.isMultiSelect + ? [this.answersToDisplay[0].Name] + : this.answersToDisplay[0].Name; } }, }, computed: { - questionText(){ - return this.getCmsContent(this.cmsWidgetName, 'QuestionText'); + questionText() { + return this.getCmsContent(this.cmsWidgetName, "QuestionText"); }, - answersFromCms(){ - return this.getCmsContent(this.cmsWidgetName, 'Answers'); + answersFromCms() { + return this.getCmsContent(this.cmsWidgetName, "Answers"); }, selectedValues: { - get: function() { + get: function () { return this.modelValue; }, - set: function(newValue) { + set: function (newValue) { this.$emit("update:modelValue", newValue); - } + }, }, - answersToDisplay(){ - const filteredAnswers = Array.isArray(this.answersFromCms) - ? this.answersFromCms.filter(ans => - { - const name = ans.Name.split('-'); - return this.filterByVehicleCategory ? name[0].toUpperCase() === store.getters.vehicle.category && this.replaceOptions.includes(name[1]) : this.replaceOptions.includes(ans.Name); - }) + answersToDisplay() { + const filteredAnswers = Array.isArray(this.answersFromCms) + ? this.answersFromCms.filter((ans) => { + const name = ans.Name.split("-"); + return this.filterByVehicleCategory + ? name[0].toUpperCase() === store.getters.vehicle.category && + this.replaceOptions.includes(name[1]) + : this.replaceOptions.includes(ans.Name); + }) : []; - return filteredAnswers.map(ans => { - const newName = ans.Name.includes('-') ? ans.Name.split('-')[1] : ans.Name; + return filteredAnswers.map((ans) => { + const newName = ans.Name.includes("-") ? ans.Name.split("-")[1] : ans.Name; ans.Name = newName; return ans; }); }, shouldDisplayReplaceOptionsQuestion() { - return this.isAvailable && this.answersToDisplay.length > 0 - } + return this.isAvailable && this.answersToDisplay.length > 0; + }, }, watch: { isAvailable(val) { @@ -93,10 +100,10 @@ export default ({ if (!shouldDisplayReplaceOptionsQuestion) { this.selectedValues = this.isMultiSelect ? [] : ""; } - } + }, }, components: { buttonQuestion, - } -}) + }, +}; diff --git a/src/layouts/vehicle-damage/side-door-options/side-door-options.spec.js b/src/layouts/vehicle-damage/side-door-options/side-door-options.spec.js index eff56bbc9..e3fb8803b 100644 --- a/src/layouts/vehicle-damage/side-door-options/side-door-options.spec.js +++ b/src/layouts/vehicle-damage/side-door-options/side-door-options.spec.js @@ -3,130 +3,152 @@ import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-doo import { getMountOptions } from "@/helpers/unit-test-helper.js"; import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question"; import store from "@/store"; -jest.mock("@/store",()=>{return{};},{virtual:true}); +jest.mock( + "@/store", + () => { + return {}; + }, + { virtual: true } +); describe("replace-options-question.vue", () => { test("Selected side door option is emitted upon selection.", async () => { - - //Arrange - const { wrapper } = setupMocks({ modelValueProp: ["Windshield"] }); - const sideDoorOptions = ["Backseat"]; - - //Act - wrapper.setValue({ modelValue: sideDoorOptions }); - await wrapper.vm.$nextTick(); - - //Assert - expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{modelValue: ["Backseat"]}]); - }); - }); + //Arrange + const { wrapper } = setupMocks({ modelValueProp: ["Windshield"] }); + const sideDoorOptions = ["Backseat"]; - describe("replace-options-question.vue", () => { + //Act + wrapper.setValue({ modelValue: sideDoorOptions }); + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: ["Backseat"] }]); + }); +}); + +describe("replace-options-question.vue", () => { test("Selected door side option is updated when selection made.", async () => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - wrapper.vm.selectedDoorSidesValues = ["DriverSide"] - await wrapper.vm.$nextTick(); + //Arrange + const { wrapper } = setupMocks({}); - //Assert - expect(wrapper.vm.getSideDoorReplacementOptions()).toHaveBeenCalled; + //Act + wrapper.vm.selectedDoorSidesValues = ["DriverSide"]; + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.vm.getSideDoorReplacementOptions()).toHaveBeenCalled; }); - }); +}); - describe("replace-options-question.vue", () => { +describe("replace-options-question.vue", () => { test("Selected driver side option is updated when selection made.", async () => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - wrapper.vm.selectedDriverSideReplaceOptionsValues = ["FrontDoor"] - await wrapper.vm.$nextTick(); - - //Assert - expect(wrapper.vm.getSideDoorReplacementOptions()).toHaveBeenCalled; - }); - }); + //Arrange + const { wrapper } = setupMocks({}); - describe("replace-options-question.vue", () => { + //Act + wrapper.vm.selectedDriverSideReplaceOptionsValues = ["FrontDoor"]; + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.vm.getSideDoorReplacementOptions()).toHaveBeenCalled; + }); +}); + +describe("replace-options-question.vue", () => { test("Selected passenger side option is updated when selection made.", async () => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - wrapper.vm.selectedPassengerSideReplaceOptionsValues = ["BacktDoor"] - await wrapper.vm.$nextTick(); - - //Assert - expect(wrapper.vm.getSideDoorReplacementOptions()).toHaveBeenCalled; + //Arrange + const { wrapper } = setupMocks({}); + + //Act + wrapper.vm.selectedPassengerSideReplaceOptionsValues = ["BacktDoor"]; + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.vm.getSideDoorReplacementOptions()).toHaveBeenCalled; }); - }); +}); - - describe("replace-options-question.vue", () => { +describe("replace-options-question.vue", () => { test("Answers to display filtered by data from api.", async () => { - - //Arrange - const { wrapper, cmsContent, driverSideReplaceOptions, passengerSideReplaceOptions, driverSideOptions, passengerSideOptions } = setupMocks({}); - - //Act - sideDoorOptions.methods.initializeComponent.call(wrapper.vm, cmsContent, driverSideReplaceOptions, passengerSideReplaceOptions, driverSideOptions, passengerSideOptions, "car-group"); - - //Assert - expect(wrapper.vm.answersToDisplay).toStrictEqual([]) - }); - }); - + //Arrange + const { + wrapper, + cmsContent, + driverSideReplaceOptions, + passengerSideReplaceOptions, + driverSideOptions, + passengerSideOptions, + } = setupMocks({}); - function setupMocks({ + //Act + sideDoorOptions.methods.initializeComponent.call( + wrapper.vm, + cmsContent, + driverSideReplaceOptions, + passengerSideReplaceOptions, + driverSideOptions, + passengerSideOptions, + "car-group" + ); + + //Assert + expect(wrapper.vm.answersToDisplay).toStrictEqual([]); + }); +}); + +function setupMocks({ modelValueProp = ["DriverSide"], groupName = "sideDoorOptions", cmsQuestionText = "CMS text goes here", - cmsAnswers = [{Name: "Car-DriverSide"}, {Name: "Car-PassengerSide"}], + cmsAnswers = [{ Name: "Car-DriverSide" }, { Name: "Car-PassengerSide" }], driverSideReplaceOptions = ["FrontDoor", "BackDoor"], passengerSideReplaceOptions = ["FrontDoor", "BackDoor"], driverSideOptions = ["Car-FrontDoor", "Car-BackDoor"], passengerSideOptions = ["Car-FrontDoor", "Car-BackDoor"], - selectedDamageLocations = ["SideDoor"] - }) { - + selectedDamageLocations = ["SideDoor"], +}) { //Mock store store.dispatch = jest.fn(() => {}); - store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} }; + store.getters = { + vehicle: { year: 2019, make: "honda", model: "civc", style: "2 Door", category: "CAR" }, + }; const mountOptions = getMountOptions({ - store: { - dispatch: store.dispatch, - getters: store.getters, - }, + store: { + dispatch: store.dispatch, + getters: store.getters, + }, }); - + //Mock props const mockMixin = { - methods: { - getCmsContent: jest.fn() - } - } + methods: { + getCmsContent: jest.fn(), + }, + }; mountOptions.propsData = { - modelValue: modelValueProp, - groupName: groupName, - selectedDamageLocations: selectedDamageLocations, + modelValue: modelValueProp, + groupName: groupName, + selectedDamageLocations: selectedDamageLocations, }; mountOptions.mixins = [mockMixin]; - + const wrapper = shallowMount(sideDoorOptions, mountOptions); - - const OptionsWrapper = wrapper.findAllComponents({name: "replaceOptionsQuestion"}); + + const OptionsWrapper = wrapper.findAllComponents({ name: "replaceOptionsQuestion" }); OptionsWrapper[0].vm.initializeComponent = replaceOptionsQuestion.methods.initializeComponent; OptionsWrapper[1].vm.initializeComponent = replaceOptionsQuestion.methods.initializeComponent; //Mock CMS content const cmsContent = { - groupName: groupName, - QuestionText: cmsQuestionText, - Answers: cmsAnswers, + groupName: groupName, + QuestionText: cmsQuestionText, + Answers: cmsAnswers, }; - return { wrapper, cmsContent, driverSideReplaceOptions, passengerSideReplaceOptions, driverSideOptions, passengerSideOptions }; - } \ No newline at end of file + return { + wrapper, + cmsContent, + driverSideReplaceOptions, + passengerSideReplaceOptions, + driverSideOptions, + passengerSideOptions, + }; +} diff --git a/src/layouts/vehicle-damage/side-door-options/side-door-options.vue b/src/layouts/vehicle-damage/side-door-options/side-door-options.vue index 00665f5f1..b645c1ecd 100644 --- a/src/layouts/vehicle-damage/side-door-options/side-door-options.vue +++ b/src/layouts/vehicle-damage/side-door-options/side-door-options.vue @@ -1,7 +1,10 @@ @@ -51,9 +51,12 @@ import { damageLocationsSelected } from "@/constants/damage-locations-selected"; // DEFINE VALIDATION RULES defineRule("damage-side-required", required(errorMessages.DAMAGE_SIDE_REQUIRED)); defineRule("driver-side-options-required", required(errorMessages.DRIVER_SIDE_OPTIONS_REQUIRED)); -defineRule("passenger-side-options-required", required(errorMessages.PASSENGER_SIDE_OPTIONS_REQUIRED)); +defineRule( + "passenger-side-options-required", + required(errorMessages.PASSENGER_SIDE_OPTIONS_REQUIRED) +); -export default ({ +export default { name: "sideDoorOptions", props: { groupName: String, @@ -62,82 +65,107 @@ export default ({ cmsWidgetName: String, }, methods: { - initializeComponent(driverSideOptions, passengerSideOptions){ + initializeComponent(driverSideOptions, passengerSideOptions) { this.$refs.driverSideOptions.initializeComponent(driverSideOptions); this.$refs.passengerSideOptions.initializeComponent(passengerSideOptions); }, - getSideDoorReplacementOptions(selectedDoorSides, selectedDriverSideReplaceOptions, selectedPassengerSideReplaceOptions){ + getSideDoorReplacementOptions( + selectedDoorSides, + selectedDriverSideReplaceOptions, + selectedPassengerSideReplaceOptions + ) { return { selectedDoorSides: selectedDoorSides, selectedDriverSideReplaceOptions: selectedDriverSideReplaceOptions, - selectedPassengerSideReplaceOptions: selectedPassengerSideReplaceOptions - } + selectedPassengerSideReplaceOptions: selectedPassengerSideReplaceOptions, + }; }, }, computed: { - questionText(){ - return this.getCmsContent(this.cmsWidgetName, 'QuestionText'); + questionText() { + return this.getCmsContent(this.cmsWidgetName, "QuestionText"); }, - answersFromCms(){ - return this.getCmsContent(this.cmsWidgetName, 'Answers'); + answersFromCms() { + return this.getCmsContent(this.cmsWidgetName, "Answers"); }, selectedValues: { - get: function() { + get: function () { return this.modelValue; }, - set: function(newValue) { + set: function (newValue) { this.$emit("update:modelValue", newValue); - } + }, }, selectedDoorSidesValues: { - get: function() { + get: function () { return this.selectedValues.selectedDoorSides; }, - set: function(newValue) { - this.selectedValues = this.getSideDoorReplacementOptions(newValue, this.selectedValues.selectedDriverSideReplaceOptions, this.selectedValues.selectedPassengerSideReplaceOptions); - } + set: function (newValue) { + this.selectedValues = this.getSideDoorReplacementOptions( + newValue, + this.selectedValues.selectedDriverSideReplaceOptions, + this.selectedValues.selectedPassengerSideReplaceOptions + ); + }, }, selectedDriverSideReplaceOptionsValues: { - get: function() { + get: function () { return this.selectedValues.selectedDriverSideReplaceOptions; }, - set: function(newValue) { - this.selectedValues = this.getSideDoorReplacementOptions(this.selectedValues.selectedDoorSides, newValue, this.selectedValues.selectedPassengerSideReplaceOptions); - } + set: function (newValue) { + this.selectedValues = this.getSideDoorReplacementOptions( + this.selectedValues.selectedDoorSides, + newValue, + this.selectedValues.selectedPassengerSideReplaceOptions + ); + }, }, selectedPassengerSideReplaceOptionsValues: { - get: function() { + get: function () { return this.selectedValues.selectedPassengerSideReplaceOptions; }, - set: function(newValue) { - this.selectedValues = this.getSideDoorReplacementOptions(this.selectedValues.selectedDoorSides, this.selectedValues.selectedDriverSideReplaceOptions, newValue); - } + set: function (newValue) { + this.selectedValues = this.getSideDoorReplacementOptions( + this.selectedValues.selectedDoorSides, + this.selectedValues.selectedDriverSideReplaceOptions, + newValue + ); + }, }, - answersToDisplay(){ - const filteredAnswers = Array.isArray(this.answersFromCms) - ? this.answersFromCms.filter(ans => - { - const name = ans.Name.split('-'); - return name[0].toUpperCase() === store.getters.vehicle.category; - }) + answersToDisplay() { + const filteredAnswers = Array.isArray(this.answersFromCms) + ? this.answersFromCms.filter((ans) => { + const name = ans.Name.split("-"); + return name[0].toUpperCase() === store.getters.vehicle.category; + }) : []; - return filteredAnswers.map(ans => { - const newName = ans.Name.includes('-') ? ans.Name.split('-')[1] : ans.Name; + return filteredAnswers.map((ans) => { + const newName = ans.Name.includes("-") ? ans.Name.split("-")[1] : ans.Name; ans.Name = newName; return ans; }); }, - isDriverSideReplaceOptionsQuestionAvailable(){ - return (Array.isArray(this.selectedDoorSidesValues) && this.selectedDoorSidesValues.includes(damageLocationsSelected.DRIVERSIDE) && (Array.isArray(this.selectedDamageLocations) && this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR))); + isDriverSideReplaceOptionsQuestionAvailable() { + return ( + Array.isArray(this.selectedDoorSidesValues) && + this.selectedDoorSidesValues.includes(damageLocationsSelected.DRIVERSIDE) && + Array.isArray(this.selectedDamageLocations) && + this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR) + ); }, - isPassengerSideReplaceOptionsQuestionAvailable(){ - return (Array.isArray(this.selectedDoorSidesValues) && this.selectedDoorSidesValues.includes(damageLocationsSelected.PASSENGERSIDE) && (Array.isArray(this.selectedDamageLocations) && this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR))); + isPassengerSideReplaceOptionsQuestionAvailable() { + return ( + Array.isArray(this.selectedDoorSidesValues) && + this.selectedDoorSidesValues.includes(damageLocationsSelected.PASSENGERSIDE) && + Array.isArray(this.selectedDamageLocations) && + this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR) + ); }, }, components: { buttonQuestion, replaceOptionsQuestion, - } -}) - \ No newline at end of file + }, +}; + diff --git a/src/layouts/vehicle-damage/vehicle-damage.spec.js b/src/layouts/vehicle-damage/vehicle-damage.spec.js index cc6d9011f..2c2be3185 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.spec.js +++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js @@ -21,772 +21,848 @@ import * as cookieHelper from "@/helpers/heritage-integration/cookie-helper"; // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ - settleAllPromises: jest.fn(), + settleAllPromises: jest.fn(), })); // Mock fetchCmsContentForPage jest.mock("@/helpers/cms-content-helper", () => ({ - fetchCmsContentForPage: jest.fn(), + fetchCmsContentForPage: jest.fn(), })); // Mock getFunnelCookie jest.mock("@/helpers/heritage-integration/cookie-helper", () => ({ - getFunnelCookie: jest.fn(), + getFunnelCookie: jest.fn(), })); // Mock Store jest.mock("@/store", () => ({ - commit: jest.fn(), - dispatch: jest.fn(), - getters: { - vehicle: { - carId: "C00000000", - image: "test.jpg", - payment: { - insuranceCoverage: { - isVerified: false - } - } + commit: jest.fn(), + dispatch: jest.fn(), + getters: { + vehicle: { + carId: "C00000000", + image: "test.jpg", + payment: { + insuranceCoverage: { + isVerified: false, + }, + }, + }, + eventBusItem: jest.fn(), + damage: { + glassToReplace: [], + }, }, - eventBusItem: jest.fn(), - damage: { - glassToReplace: [] - }, - }, })); describe("vehicle-damage.vue", () => { - describe("navigation", () => { - test("BackButtonAction triggers a router.navigateWithoutSaving change", async () => { + describe("navigation", () => { + test("BackButtonAction triggers a router.navigateWithoutSaving change", async () => { + //Arrange + const { wrapper } = setupMocks({}); - //Arrange - const { wrapper } = setupMocks({}); + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); + wrapper.vm.backButtonAction(); - wrapper.vm.backButtonAction(); + //Assert + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); + }); - //Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); + test("Replace several pieces of glass on ForwardButtonAction triggers a router.navigate and saves selections to store", async () => { + //Arrange + const partsData = { + partsOrQuestions: [ + { + name: "Single", + location: "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: { navigate: jest.fn(), navigateWithSaving: jest.fn() }, + actionList: [ + { actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData }, + ], + store: { + getters: { + vehicle: {}, + payment: { insuranceCoverage: { isVerified: false } }, + }, + }, + }, + }); + + wrapper.setData({ + selectedDamageLocations: ["Windshield", "SideDoor", "RearWindow"], + selectedWindshieldOptions: { + selectedWindshieldChipCount: null, + selectedWindshieldReplaceOptions: ["Single"], + selectedWindshieldDamageType: "Replace", + }, + sideDoorOptionsData: { + selectedDoorSides: ["DriverSide", "PassengerSide"], + selectedDriverSideReplaceOptions: ["Back"], + selectedPassengerSideReplaceOptions: ["Quarter"], + }, + 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.navigateWithSaving).toHaveBeenCalled(); + expect(wrapper.vm.selectedGlassToReplace()).toEqual(expectedGlassToReplace); + expect(baseMixin.methods.dispatchStoreAction).toBeCalledWith( + storeActions.GET_DAMAGE_OPTIONS, + { carId: "C00000000" } + ); + }); + + test("Windshield replace with multiple parts on ForwardButtonAction triggers a router.navigate and saves selections to store", async () => { + //Arrange + const partsData = { + partsOrQuestions: [ + { + location: "Windshield", + name: "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, + }, + ], + }, + { + name: "Stationary", + location: "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: { navigate: jest.fn(), navigateWithSaving: jest.fn() }, + actionList: [ + { actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData }, + ], + store: { + getters: { + vehicle: {}, + payment: { insuranceCoverage: { isVerified: false } }, + }, + }, + }, + }); + + 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.navigateWithSaving).toHaveBeenCalled(); + expect(wrapper.vm.selectedGlassToReplace()).toEqual(expectedGlassToReplace); + expect(baseMixin.methods.dispatchStoreAction).toBeCalledWith( + storeActions.GET_DAMAGE_OPTIONS, + { carId: "C00000000" } + ); + }); }); - test("Replace several pieces of glass on ForwardButtonAction triggers a router.navigate and saves selections to store", async () => { - //Arrange - const partsData = { - partsOrQuestions: [{ - name: "Single", - location: "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" - }] - }] - }] - }; + describe("alert", () => { + test("when displayVehicleChangeAlert router params is true, the alert: 'vehicleChangeAlert' should be rendered", () => { + // Arrange & Act + const { wrapper } = setupMocks({ + mountOptionsMockData: { + route: { + params: { + [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true, + }, + }, + }, + }); + // Assert + expect(wrapper.findComponent({ ref: "vehicleChangeAlert" }).exists()).toBe(true); + }); - const { wrapper } = setupMocks({ - pageHeaderWidgetHeaderText: "", - mountOptionsMockData: { - router: { navigate: jest.fn(), navigateWithSaving: jest.fn(), }, - actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },], - store: { - getters: { - vehicle: {}, - payment: { insuranceCoverage: { isVerified: false } }, - }, - }, - }, - }); + test("when displayVehicleChangeAlert router params is false, the alert: 'vehicleChangeAlert' should not be rendered", () => { + // Arrange & Act + const { wrapper } = setupMocks({ + mountOptionsMockData: { + route: { + params: { + [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: false, + }, + }, + }, + }); + // Assert + expect(wrapper.findComponent({ ref: "vehicleChangeAlert" }).exists()).toBe(false); + }); - wrapper.setData({ - selectedDamageLocations: ["Windshield", "SideDoor", "RearWindow"], - selectedWindshieldOptions: { - selectedWindshieldChipCount: null, - selectedWindshieldReplaceOptions: ["Single"], - selectedWindshieldDamageType: "Replace" - }, - sideDoorOptionsData: { - selectedDoorSides: ["DriverSide", "PassengerSide"], - selectedDriverSideReplaceOptions: ["Back"], - selectedPassengerSideReplaceOptions: ["Quarter"] - }, - 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.navigateWithSaving).toHaveBeenCalled(); - expect(wrapper.vm.selectedGlassToReplace()).toEqual(expectedGlassToReplace); - expect(baseMixin.methods.dispatchStoreAction).toBeCalledWith(storeActions.GET_DAMAGE_OPTIONS, {"carId": "C00000000"}); + test("when displayVehicleChangeAlert router params is undefined, the alert: 'vehicleChangeAlert' should not be rendered", () => { + // Arrange & Act + const { wrapper } = setupMocks({ + mountOptionsMockData: { + route: { + params: { + [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: undefined, + }, + }, + }, + }); + // Assert + expect(wrapper.findComponent({ ref: "vehicleChangeAlert" }).exists()).toBe(false); + }); }); - test("Windshield replace with multiple parts on ForwardButtonAction triggers a router.navigate and saves selections to store", async () => { - //Arrange - const partsData = { - partsOrQuestions: [ - { - location: "Windshield", - name: "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 - } - ] - }, - { - name: "Stationary", - location: "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 - } + describe("glass selections and corresponding variables", () => { + 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); + }); + + 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); + }); + + test("isRearWindowDamageLocation is true if Rear Window damage 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 = ["RearWindow"]; + + //Assert + expect(wrapper.vm.isRearWindowDamageLocation).toEqual(true); + }); + + 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); + }); + + 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); + }); + + 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); + }); + + 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("state validations", () => { + test("CarId set, arePagePrerequisitesValid should be true ", async () => { + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + await nextTick(); + + //Assert + expect(arePagePrerequisitesValid).toBe(true); + }); + }); + + describe("input validations", () => { + // THE FOLLOWING TEST IS NOT NECESSARILY REQUIRED FOR COVERAGE + // BUT KEEP FOR AN EXAMPLE OF A VALIDATION TEST + test("when validation rules are set they should validate correctly", async () => { + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + const testNull = validate("", "replace-options-required"); + const testString = validate("sldfj", "replace-options-required"); + + //Assert + testNull.then(function (data) { + expect(data.valid).toEqual(false); + }); + testString.then(function (data) { + expect(data.valid).toEqual(true); + }); + }); + }); + + describe("get glass options from store", () => { + const damageLocations = [ + ["Windshield", [damageLocationsSelected.WINDSHIELD]], + ["Driver", [damageLocationsSelected.SIDEDOOR]], + ["Passenger", [damageLocationsSelected.SIDEDOOR]], + ["Rear", [damageLocationsSelected.REARWINDOW]], + ]; + test.each(damageLocations)( + "getDamageLocationsFromStore for %s returns expected %s", + async (damageLocation, expectedGlass) => { + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + store.getters = { + vehicle: { carId: "C0000000" }, + eventBusItem: jest.fn(), + damage: { glassToReplace: [{ location: damageLocation }] }, + isRepair: true, + }; + + var glassSelections = wrapper.vm.getDamageLocationsFromStore(); + + //Assert + expect(glassSelections).toEqual(expectedGlass); + } + ); + + const storeWindshieldOptions = [ + [ + 1, + false, + "Windshield", + "Single", + { + selectedWindshieldDamageType: damageLocationsSelected.REPLACE, + selectedWindshieldChipCount: null, + selectedWindshieldReplaceOptions: [damageLocationsSelected.SINGLE], + }, ], - "partQuestions": null - } - ] - }; + [ + 2, + false, + "Windshield", + "Driver", + { + selectedWindshieldDamageType: damageLocationsSelected.REPLACE, + selectedWindshieldChipCount: null, + selectedWindshieldReplaceOptions: [damageLocationsSelected.DRIVER], + }, + ], + [ + 3, + false, + "Windshield", + "Passenger", + { + selectedWindshieldDamageType: damageLocationsSelected.REPLACE, + selectedWindshieldChipCount: null, + selectedWindshieldReplaceOptions: [damageLocationsSelected.PASSENGER], + }, + ], + [ + 4, + true, + "", + "", + { + selectedWindshieldDamageType: damageLocationsSelected.REPAIR, + selectedWindshieldChipCount: 2, + selectedWindshieldReplaceOptions: [], + }, + ], + ]; + test.each(storeWindshieldOptions)( + "getWindshieldOptionsFromStore test #%s", + async (testNum, isRepair, damageLocation, damageName, expectedWindshieldOptions) => { + //Arrange + const { wrapper } = setupMocks({}); - const { wrapper } = setupMocks({ - pageHeaderWidgetHeaderText: "", - mountOptionsMockData: { - router: { navigate: jest.fn(), navigateWithSaving: jest.fn(), }, - actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: partsData, },], - store: { - getters: { - vehicle: {}, - payment: { insuranceCoverage: { isVerified: false } }, - }, - }, - }, - }); + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); - wrapper.vm.selectedDamageLocations = ["Windshield"]; - wrapper.vm.selectedWindshieldOptions = { - selectedWindshieldChipCount: null, - selectedWindshieldReplaceOptions: ["Single"], - selectedWindshieldDamageType: "Replace" - }; + store.getters = { + vehicle: { carId: "C0000000" }, + eventBusItem: jest.fn(), + damage: { + glassToReplace: [{ location: damageLocation, name: damageName }], + isRepair: isRepair, + numberOfChips: 2, + }, + }; - const expectedGlassToReplace = [{ location: "Windshield", name: "Single" },]; + var windshieldSelections = wrapper.vm.getWindshieldOptionsFromStore(); - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.forwardButtonAction(); - - //Assert - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); - expect(wrapper.vm.selectedGlassToReplace()).toEqual(expectedGlassToReplace); - expect(baseMixin.methods.dispatchStoreAction).toBeCalledWith(storeActions.GET_DAMAGE_OPTIONS, {"carId": "C00000000"}); - }); - - - }); - - describe("alert", () => { - test("when displayVehicleChangeAlert router params is true, the alert: 'vehicleChangeAlert' should be rendered", () => { - // Arrange & Act - const { wrapper } = setupMocks({ - mountOptionsMockData: { - route: { - params: { - [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true + //Assert + expect(windshieldSelections).toEqual(expectedWindshieldOptions); } - } - } - }); - // Assert - expect(wrapper.findComponent({ ref: 'vehicleChangeAlert' }).exists()).toBe(true); - }); + ); - test("when displayVehicleChangeAlert router params is false, the alert: 'vehicleChangeAlert' should not be rendered", () => { - // Arrange & Act - const { wrapper } = setupMocks({ - mountOptionsMockData: { - route: { - params: { - [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: false + const driverDoorSides = [ + ["Driver", "Front", [damageLocationsSelected.FRONT]], + ["Driver", "Back", [damageLocationsSelected.BACK]], + ["Driver", "Vent", [damageLocationsSelected.VENT]], + ["Driver", "Quarter", [damageLocationsSelected.QUARTER]], + ]; + test.each(driverDoorSides)( + "getDriverSideReplaceOptionsFromStore for %s-%s returns expected %s", + async (damageLocation, damageName, expectedGlass) => { + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + store.getters = { + vehicle: { carId: "C0000000" }, + eventBusItem: jest.fn(), + damage: { glassToReplace: [{ location: damageLocation, name: damageName }] }, + isRepair: true, + }; + + var glassSelections = wrapper.vm.getDriverSideReplaceOptionsFromStore(); + + //Assert + expect(glassSelections).toEqual(expectedGlass); } - } - } - }); - // Assert - expect(wrapper.findComponent({ ref: 'vehicleChangeAlert' }).exists()).toBe(false); - }); + ); - test("when displayVehicleChangeAlert router params is undefined, the alert: 'vehicleChangeAlert' should not be rendered", () => { - // Arrange & Act - const { wrapper } = setupMocks({ - mountOptionsMockData: { - route: { - params: { - [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: undefined + const passengerDoorSides = [ + ["Passenger", "Front", [damageLocationsSelected.FRONT]], + ["Passenger", "Back", [damageLocationsSelected.BACK]], + ["Passenger", "Vent", [damageLocationsSelected.VENT]], + ["Passenger", "Quarter", [damageLocationsSelected.QUARTER]], + ]; + test.each(passengerDoorSides)( + "getPassengerSideReplaceOptionsFromStore for %s-%s returns expected %s", + async (damageLocation, damageName, expectedGlass) => { + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); + + store.getters = { + vehicle: { carId: "C0000000" }, + eventBusItem: jest.fn(), + damage: { glassToReplace: [{ location: damageLocation, name: damageName }] }, + isRepair: true, + }; + + var glassSelections = wrapper.vm.getPassengerSideReplaceOptionsFromStore(); + + //Assert + expect(glassSelections).toEqual(expectedGlass); } - } - } - }); - // Assert - expect(wrapper.findComponent({ ref: 'vehicleChangeAlert' }).exists()).toBe(false); - }) - }); + ); - describe("glass selections and corresponding variables", () => { - test("isWindshieldDamageLocation is true when windshield is selected", async () => { + const rearReplaceOptions = [ + ["Rear", "Stationary", damageLocationsSelected.STATIONARY], + ["Rear", "Slider", damageLocationsSelected.SLIDER], + ]; + test.each(rearReplaceOptions)( + "getRearReplaceOptionsFromStore for %s-%s returns expected %s", + async (damageLocation, damageName, expectedGlass) => { + //Arrange + const { wrapper } = setupMocks({}); - //Arrange - const { wrapper } = setupMocks({}); + //Act + vehicleDamage.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-damage" } }, + undefined, + (c) => c(wrapper.vm) + ); - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); + store.getters = { + vehicle: { carId: "C0000000" }, + eventBusItem: jest.fn(), + damage: { glassToReplace: [{ location: damageLocation, name: damageName }] }, + isRepair: true, + }; - wrapper.vm.selectedDamageLocations = ["Windshield"]; + var glassSelections = wrapper.vm.getRearReplaceOptionsFromStore(); - //Assert - expect(wrapper.vm.isWindshieldDamageLocation).toEqual(true); + //Assert + expect(glassSelections).toEqual(expectedGlass); + } + ); }); - test("isSideDoorDamageLocation is true Side door is selected", async () => { + describe("hide back button", () => { + test("if claim registration is delayed => should hide back button", () => { + // Arrange + const { wrapper } = setupMocks({ + funnelCookie: { + HasDelayedClaimRegistration: true, + }, + }); - //Arrange - const { wrapper } = setupMocks({}); + // Assert + expect(wrapper.vm.shouldHideBackButton).toBe(true); + }); - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); + test("if claim is verified => should hide back button", () => { + // Arrange + const { wrapper } = setupMocks({ + mountOptionsMockData: { + router: { navigate: jest.fn() }, + actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: {} }], + store: { + getters: { + vehicle: {}, + payment: { insuranceCoverage: { isVerified: true } }, + }, + }, + }, + }); - wrapper.vm.selectedDamageLocations = ["Sidedoor"]; + // Assert + expect(wrapper.vm.shouldHideBackButton).toBe(true); + }); - //Assert - expect(wrapper.vm.isSideDoorDamageLocation).toEqual(true); + test("if claim isn't verified yet => allow user to go back", () => { + // Arrange + const { wrapper } = setupMocks({}); + + // Assert + expect(wrapper.vm.shouldHideBackButton).toBeFalsy(); + }); }); - - test("isRearWindowDamageLocation is true if Rear Window damage 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 = ["RearWindow"]; - - //Assert - expect(wrapper.vm.isRearWindowDamageLocation).toEqual(true); - }); - - 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); - }); - - 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); - }); - - 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); - }); - - 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("state validations", () => { - test("CarId set, arePagePrerequisitesValid should be true ", async () => { - //Arrange - const { wrapper } = setupMocks({}); - - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); - - let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - await nextTick(); - - //Assert - expect(arePagePrerequisitesValid).toBe(true); - }); - - }); - - describe("input validations", () => { - // THE FOLLOWING TEST IS NOT NECESSARILY REQUIRED FOR COVERAGE - // BUT KEEP FOR AN EXAMPLE OF A VALIDATION TEST - test("when validation rules are set they should validate correctly", async () => { - //Arrange - const { wrapper } = setupMocks({}); - - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); - const testNull = validate("", "replace-options-required"); - const testString = validate("sldfj", "replace-options-required"); - - //Assert - testNull.then(function (data) { - expect(data.valid).toEqual(false); - }); - testString.then(function (data) { - expect(data.valid).toEqual(true); - }); - }); - }); - - describe("get glass options from store", () => { - const damageLocations = [["Windshield", [damageLocationsSelected.WINDSHIELD]], - ["Driver", [damageLocationsSelected.SIDEDOOR]], - ["Passenger", [damageLocationsSelected.SIDEDOOR]], - ["Rear", [damageLocationsSelected.REARWINDOW]]]; - test.each(damageLocations)("getDamageLocationsFromStore for %s returns expected %s", async (damageLocation, expectedGlass) => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); - - store.getters = { vehicle: { carId: "C0000000" }, eventBusItem: jest.fn(), damage: { glassToReplace: [{ location: damageLocation }] }, isRepair: true }; - - var glassSelections = wrapper.vm.getDamageLocationsFromStore(); - - //Assert - expect(glassSelections).toEqual(expectedGlass); - }); - - const storeWindshieldOptions = [[1, false, "Windshield", "Single", { - selectedWindshieldDamageType: damageLocationsSelected.REPLACE, - selectedWindshieldChipCount: null, selectedWindshieldReplaceOptions: [damageLocationsSelected.SINGLE] - }], - [2, false, "Windshield", "Driver", { - selectedWindshieldDamageType: damageLocationsSelected.REPLACE, - selectedWindshieldChipCount: null, selectedWindshieldReplaceOptions: [damageLocationsSelected.DRIVER] - }], - [3, false, "Windshield", "Passenger", { - selectedWindshieldDamageType: damageLocationsSelected.REPLACE, - selectedWindshieldChipCount: null, selectedWindshieldReplaceOptions: [damageLocationsSelected.PASSENGER] - }], - [4, true, "", "", { - selectedWindshieldDamageType: damageLocationsSelected.REPAIR, - selectedWindshieldChipCount: 2, selectedWindshieldReplaceOptions: [] - }] - ]; - test.each(storeWindshieldOptions)("getWindshieldOptionsFromStore test #%s", async (testNum, isRepair, damageLocation, damageName, expectedWindshieldOptions) => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); - - store.getters = { - vehicle: - { carId: "C0000000" }, - eventBusItem: jest.fn(), - damage: - { - glassToReplace: [{ location: damageLocation, name: damageName }], - isRepair: isRepair, - numberOfChips: 2 - }, - }; - - var windshieldSelections = wrapper.vm.getWindshieldOptionsFromStore(); - - //Assert - expect(windshieldSelections).toEqual(expectedWindshieldOptions); - }); - - const driverDoorSides = [["Driver", "Front", [damageLocationsSelected.FRONT]], - ["Driver", "Back", [damageLocationsSelected.BACK]], - ["Driver", "Vent", [damageLocationsSelected.VENT]], - ["Driver", "Quarter", [damageLocationsSelected.QUARTER]] - ]; - test.each(driverDoorSides)("getDriverSideReplaceOptionsFromStore for %s-%s returns expected %s", async (damageLocation, damageName, expectedGlass) => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); - - store.getters = { vehicle: { carId: "C0000000" }, eventBusItem: jest.fn(), damage: { glassToReplace: [{ location: damageLocation, name: damageName }] }, isRepair: true }; - - var glassSelections = wrapper.vm.getDriverSideReplaceOptionsFromStore(); - - //Assert - expect(glassSelections).toEqual(expectedGlass); - - }); - - const passengerDoorSides = [["Passenger", "Front", [damageLocationsSelected.FRONT]], - ["Passenger", "Back", [damageLocationsSelected.BACK]], - ["Passenger", "Vent", [damageLocationsSelected.VENT]], - ["Passenger", "Quarter", [damageLocationsSelected.QUARTER]] - ]; - test.each(passengerDoorSides)("getPassengerSideReplaceOptionsFromStore for %s-%s returns expected %s", async (damageLocation, damageName, expectedGlass) => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); - - store.getters = { vehicle: { carId: "C0000000" }, eventBusItem: jest.fn(), damage: { glassToReplace: [{ location: damageLocation, name: damageName }] }, isRepair: true }; - - var glassSelections = wrapper.vm.getPassengerSideReplaceOptionsFromStore(); - - //Assert - expect(glassSelections).toEqual(expectedGlass); - }); - - const rearReplaceOptions = [["Rear", "Stationary", damageLocationsSelected.STATIONARY], - ["Rear", "Slider", damageLocationsSelected.SLIDER] - ]; - test.each(rearReplaceOptions)("getRearReplaceOptionsFromStore for %s-%s returns expected %s", async (damageLocation, damageName, expectedGlass) => { - - //Arrange - const { wrapper } = setupMocks({}); - - //Act - vehicleDamage.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-damage" } }, - undefined, - (c) => c(wrapper.vm) - ); - - store.getters = { vehicle: { carId: "C0000000" }, eventBusItem: jest.fn(), damage: { glassToReplace: [{ location: damageLocation, name: damageName }] }, isRepair: true }; - - var glassSelections = wrapper.vm.getRearReplaceOptionsFromStore(); - - //Assert - expect(glassSelections).toEqual(expectedGlass); - }); - }) - - describe("hide back button", () => { - test("if claim registration is delayed => should hide back button", () => { - // Arrange - const { wrapper } = setupMocks({ - funnelCookie: { - HasDelayedClaimRegistration: true - } - }); - - // Assert - expect(wrapper.vm.shouldHideBackButton).toBe(true); - }); - - test("if claim is verified => should hide back button", () => { - // Arrange - const { wrapper } = setupMocks({ - mountOptionsMockData: { - router: { navigate: jest.fn(), }, - actionList: [{ actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: {}, },], - store: { - getters: { - vehicle: {}, - payment: { insuranceCoverage: { isVerified: true } }, - }, - }, - }, - }) - - // Assert - expect(wrapper.vm.shouldHideBackButton).toBe(true); - }) - - test("if claim isn't verified yet => allow user to go back", () => { - // Arrange - const { wrapper } = setupMocks({}); - - // Assert - expect(wrapper.vm.shouldHideBackButton).toBeFalsy(); - }) - }); }); function setupMocks({ pageHeaderWidgetHeaderText, mountOptionsMockData, funnelCookie = {} }) { - var pageHeaderWidgetHeaderTextDefault = {}; - var mountOptionsMockDataDefault = { - router: { - navigate: jest.fn(), - navigateWithoutSaving: jest.fn(), - navigateWithSaving: jest.fn(), - }, - route: { - params: { - [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: false - } - }, - store: { - getters: { - vehicle: {}, - payment: { - insuranceCoverage: { - isVerified: false - } + var pageHeaderWidgetHeaderTextDefault = {}; + var mountOptionsMockDataDefault = { + router: { + navigate: jest.fn(), + navigateWithoutSaving: jest.fn(), + navigateWithSaving: jest.fn(), }, - }, - }, - }; - // Combine parameters with default values - pageHeaderWidgetHeaderText = Object.assign(pageHeaderWidgetHeaderTextDefault, pageHeaderWidgetHeaderText); - mountOptionsMockData = Object.assign(mountOptionsMockDataDefault, mountOptionsMockData); - //Mock api responses - baseMixin.methods.dispatchStoreAction = jest.fn(); - const apiResponses = { - cmsContent: { - FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, - VehicleBannerWidget: { - GenericVehicleImage: "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", - }, - FunnelHeaderWidget: { - LogoImage: "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", - }, - }, - damageOptions: { - driverSideOptions: { - availableReplacementOptions: ["Front", "Back", "Side"], - }, - passengerSideOptions: { - availableReplacementOptions: ["Front", "Back", "Side"], - }, - windshieldOptions: { - availableReplacementOptions: ["Single", "Driver", "Passenger"], - }, - backGlassOptions: { - availableReplacementOptions: ["Front", "Back", "Side"], - }, - }, - }; + route: { + params: { + [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: false, + }, + }, + store: { + getters: { + vehicle: {}, + payment: { + insuranceCoverage: { + isVerified: false, + }, + }, + }, + }, + }; + // Combine parameters with default values + pageHeaderWidgetHeaderText = Object.assign( + pageHeaderWidgetHeaderTextDefault, + pageHeaderWidgetHeaderText + ); + mountOptionsMockData = Object.assign(mountOptionsMockDataDefault, mountOptionsMockData); + //Mock api responses + baseMixin.methods.dispatchStoreAction = jest.fn(); + const apiResponses = { + cmsContent: { + FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, + VehicleBannerWidget: { + GenericVehicleImage: + "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", + }, + FunnelHeaderWidget: { + LogoImage: + "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", + }, + }, + damageOptions: { + driverSideOptions: { + availableReplacementOptions: ["Front", "Back", "Side"], + }, + passengerSideOptions: { + availableReplacementOptions: ["Front", "Back", "Side"], + }, + windshieldOptions: { + availableReplacementOptions: ["Single", "Driver", "Passenger"], + }, + backGlassOptions: { + availableReplacementOptions: ["Front", "Back", "Side"], + }, + }, + }; - const apiPromise = Promise.resolve(apiResponses); + const apiPromise = Promise.resolve(apiResponses); - settleAllPromises.mockImplementation(() => apiPromise); - fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); - cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie").mockReturnValue(funnelCookie); + settleAllPromises.mockImplementation(() => apiPromise); + fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); + cookieHelper.getFunnelCookie = jest + .spyOn(cookieHelper, "getFunnelCookie") + .mockReturnValue(funnelCookie); - //Mock damage initialize methods - damageLocationQuestion.methods = { - initializeComponent: jest.fn(), - }; + //Mock damage initialize methods + damageLocationQuestion.methods = { + initializeComponent: jest.fn(), + }; - sideDoorOptions.methods = { - initializeComponent: jest.fn(), - }; + sideDoorOptions.methods = { + initializeComponent: jest.fn(), + }; - windshieldOptions.methods = { - initializeComponent: jest.fn(), - }; + windshieldOptions.methods = { + initializeComponent: jest.fn(), + }; - replaceOptionsQuestion.methods = { - initializeComponent: jest.fn(), - updateSelectedValues: jest.fn(), - }; + replaceOptionsQuestion.methods = { + initializeComponent: jest.fn(), + updateSelectedValues: jest.fn(), + }; + const mountOptions = getMountOptions(mountOptionsMockData); + mountOptions["attachTo"] = document.body; // append wrapper to document.body to test DOM methods - const mountOptions = getMountOptions(mountOptionsMockData); - mountOptions['attachTo'] = document.body; // append wrapper to document.body to test DOM methods + const wrapper = shallowMount(vehicleDamage, mountOptions); - const wrapper = shallowMount(vehicleDamage, mountOptions); + const sideDoorOptionsWrapper = wrapper.findComponent({ name: "sideDoorOptions" }); + sideDoorOptionsWrapper.vm.initializeComponent = sideDoorOptions.methods.initializeComponent; - const sideDoorOptionsWrapper = wrapper.findComponent({ name: "sideDoorOptions" }); - sideDoorOptionsWrapper.vm.initializeComponent = - sideDoorOptions.methods.initializeComponent; + const windshieldOptionsWrapper = wrapper.findComponent({ name: "windshieldOptions" }); + windshieldOptionsWrapper.vm.initializeComponent = windshieldOptions.methods.initializeComponent; - const windshieldOptionsWrapper = wrapper.findComponent({ name: "windshieldOptions" }); - windshieldOptionsWrapper.vm.initializeComponent = - windshieldOptions.methods.initializeComponent; + const backGlassOptionsWrapper = wrapper.findComponent({ name: "replaceOptionsQuestion" }); + backGlassOptionsWrapper.vm.initializeComponent = + replaceOptionsQuestion.methods.initializeComponent; - const backGlassOptionsWrapper = wrapper.findComponent({ name: "replaceOptionsQuestion" }); - backGlassOptionsWrapper.vm.initializeComponent = - replaceOptionsQuestion.methods.initializeComponent; + const damageLocationQuestionWrapper = wrapper.findComponent({ name: "damageLocationQuestion" }); + damageLocationQuestionWrapper.vm.initializeComponent = + damageLocationQuestion.methods.initializeComponent; - const damageLocationQuestionWrapper = wrapper.findComponent({ name: "damageLocationQuestion" }); - damageLocationQuestionWrapper.vm.initializeComponent = - damageLocationQuestion.methods.initializeComponent; + wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; - wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; - - return { wrapper, apiPromise }; + return { wrapper, apiPromise }; } diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index 86435e8e4..ade1b7c59 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -1,67 +1,58 @@ @@ -95,306 +86,407 @@ import baseMixin from "@/mixins/base-mixin"; defineRule("replace-options-required", required(errorMessages.REPLACE_OPTIONS_REQUIRED)); export default { - name: "vehicle-damage", - async beforeRouteEnter(to, from, next) { - // Call APIs - const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); - const damageOptionsPromise = - baseMixin.methods.dispatchStoreAction( - storeActions.GET_DAMAGE_OPTIONS, - { carId: store.getters.vehicle.carId } - ); - - // Settle promises and get results - const promiseResultMap = [ - { - resultKey: "cmsContent", - promise: cmsContentPromise, - }, - { - resultKey: "damageOptions", - promise: damageOptionsPromise, - }, - ]; - - const resultMap = await settleAllPromises(promiseResultMap); - - // Call the "next" function to complete the transition to this page. - next((vm) => { - vm.setCmsContent(resultMap.cmsContent); - vm.$refs.damageLocation.initializeComponent( - resultMap.damageOptions - ); - vm.$refs.sideDoorOptions.initializeComponent( - resultMap.damageOptions.driverSideOptions.availableReplacementOptions, resultMap.damageOptions.passengerSideOptions.availableReplacementOptions - ); - vm.$refs.windshieldOptions.initializeComponent( - resultMap.damageOptions.windshieldOptions.availableReplacementOptions - ); - vm.$refs.backGlassOptions.initializeComponent( - resultMap.damageOptions.backGlassOptions.availableReplacementOptions - ); - - - }); - }, - data(){ - return { - selectedDamageLocations: this.getDamageLocationsFromStore(), - sideDoorOptionsData: { - selectedDoorSides: this.getDoorSidesFromStore(), - selectedDriverSideReplaceOptions: this.getDriverSideReplaceOptionsFromStore(), - selectedPassengerSideReplaceOptions: this.getPassengerSideReplaceOptionsFromStore() - }, - selectedWindshieldOptions: this.getWindshieldOptionsFromStore(), - selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore(), - } - }, - mounted(){ - this.attachCustomEvents(); - }, - methods: { - arePagePrerequisitesValid() { - if (store.getters.vehicle.carId) { - return true; - } - return false; - }, - - attachCustomEvents(){ - if(this.$store.getters.vehicle.imageVifNumber){ - this.pushEventToGA(this.GaCategories.EVOX, `${this.GaActions.VIF}_${this.$store.getters.vehicle.imageVifNumber}`, - this.$store.getters.vehicle.carId, true); - } - }, - - backButtonAction() { - // route to move backwards - this.$router.navigateWithoutSaving( - this.navigationScenarios.CLICKED_BACK, - this.$route - ); - }, - - getDamageLocationsFromStore() { - var glassSelections = []; - - if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD }) || - store.getters.damage.isRepair) { - glassSelections.push(damageLocationsSelected.WINDSHIELD); - } - if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.DRIVER || - glass.location === damageLocationsSelected.PASSENGER })) { - glassSelections.push(damageLocationsSelected.SIDEDOOR); - } - - if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.REAR })) { - glassSelections.push(damageLocationsSelected.REARWINDOW); - } - - return glassSelections; - }, - - getWindshieldOptionsFromStore() { - var windShieldOptions = { selectedWindshieldDamageType: "", selectedWindshieldChipCount: null, selectedWindshieldReplaceOptions: []}; - - if (store.getters.damage.isRepair === undefined) return windshieldOptions; - - if (store.getters.damage.isRepair) { - windShieldOptions.selectedWindshieldDamageType = damageLocationsSelected.REPAIR; - windShieldOptions.selectedWindshieldChipCount = store.getters.damage.numberOfChips; - } - else { - if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD && - glass.name === damageLocationsSelected.SINGLE })) { - windShieldOptions.selectedWindshieldDamageType = damageLocationsSelected.REPLACE; - windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.SINGLE); - } - - if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD && - glass.name === damageLocationsSelected.DRIVER })) { - windShieldOptions.selectedWindshieldDamageType = damageLocationsSelected.REPLACE; - windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.DRIVER); - } - - if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.WINDSHIELD && - glass.name === damageLocationsSelected.PASSENGER })) { - windShieldOptions.selectedWindshieldDamageType = damageLocationsSelected.REPLACE; - windShieldOptions.selectedWindshieldReplaceOptions.push(damageLocationsSelected.PASSENGER); - } - } - - return windShieldOptions; - }, - - getDoorSidesFromStore() { - var doorSides = []; - if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.DRIVER })){ - doorSides.push(damageLocationsSelected.DRIVERSIDE); - } - - if (store.getters.damage.glassToReplace?.some(glass => { return glass.location === damageLocationsSelected.PASSENGER })){ - doorSides.push(damageLocationsSelected.PASSENGERSIDE); - } - - return doorSides; - }, - - getDriverSideReplaceOptionsFromStore() { - var driverSideReplaceOptions = []; - - store.getters.damage.glassToReplace?.forEach(glass => { - if (glass.location === damageLocationsSelected.DRIVER){ - driverSideReplaceOptions.push(glass.name); - } - }); - - return driverSideReplaceOptions; - }, - - getPassengerSideReplaceOptionsFromStore() { - var passengerSideReplaceOptions = []; - - store.getters.damage.glassToReplace?.forEach(glass => { - if (glass.location === damageLocationsSelected.PASSENGER){ - passengerSideReplaceOptions.push(glass.name); - } - }); - - return passengerSideReplaceOptions; - }, - - getRearReplaceOptionsFromStore() { - var rearReplaceOptions = store.getters.damage.glassToReplace?.filter(glass => glass.location === damageLocationsSelected.REAR)[0]?.name; - - return rearReplaceOptions; - }, - - async forwardButtonAction() { - - await this.dispatchStoreAction(this.storeActions.SAVE_VEHICLE_DAMAGE, { - isWindshieldRepair: this.isWindshieldRepair, - selectedGlassToReplace: this.selectedGlassToReplace(), - selectedWindshieldChipCount: this.selectedWindshieldOptions.selectedWindshieldChipCount - }, false); - - return this.navigateForward(); - }, - - navigateForward(){ - // If vin already exists, navigate directly to vin-lookup - if(store.getters.vehicle.vin) { - this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, this.$route); - } - else { - this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, this.$route); - } - }, - - selectedGlassToReplace() { - const selectedGlassToReplace = []; - if (this.isWindshieldDamageLocation && !this.isWindshieldRepair){ - this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach(wsItem => { - selectedGlassToReplace.push({ location: damageLocationsSelected.WINDSHIELD, name: wsItem}); - }) - } - - if (this.isDriverSideReplace){ - this.sideDoorOptionsData.selectedDriverSideReplaceOptions.forEach(driverItem => { - selectedGlassToReplace.push({ location: damageLocationsSelected.DRIVER, name: driverItem}); - }) - } - - if (this.isPassengerSideReplace){ - this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach(passengerItem => { - selectedGlassToReplace.push({ location: damageLocationsSelected.PASSENGER, name: passengerItem}); - }) - } - - if (this.isRearWindowDamageLocation) { - selectedGlassToReplace.push({ location: damageLocationsSelected.REAR, name: this.selectedRearReplaceOptions}); - } - - 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() { - return this.isWindshieldDamageLocation && this.selectedWindshieldOptions.selectedWindshieldDamageType === damageLocationsSelected.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; - }); - }, - hasRepairReplaceConflict() { - return this.isWindshieldDamageLocation && this.selectedDamageLocations.length > 1 && this.isWindshieldRepair; - }, - hasSplitSingleConflict() { - if (!this.selectedDamageLocations?.includes("Windshield") || this.selectedWindshieldOptions.selectedWindshieldDamageType === damageLocationsSelected.REPAIR || !this.selectedWindshieldOptions.selectedWindshieldReplaceOptions) return false; - - return this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(selectedSingleWindshield => - { - return selectedSingleWindshield.toUpperCase() === damageLocationsSelected.SINGLE.toUpperCase(); - }) && - (this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(selectedDriverWindshield => - { - return selectedDriverWindshield.toUpperCase() === damageLocationsSelected.DRIVER.toUpperCase(); - }) || - this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some(selectedPassengerWindshield => - { - return selectedPassengerWindshield.toUpperCase() === damageLocationsSelected.PASSENGER.toUpperCase(); - }) + name: "vehicle-damage", + async beforeRouteEnter(to, from, next) { + // Call APIs + const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); + const damageOptionsPromise = baseMixin.methods.dispatchStoreAction( + storeActions.GET_DAMAGE_OPTIONS, + { carId: store.getters.vehicle.carId } ); - }, - shouldDisplayVehicleChangeAlert() { - return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]; - }, - shouldHideBackButton() { - return this.$store.getters.payment.insuranceCoverage.isVerified || getFunnelCookie()?.HasDelayedClaimRegistration; - } - }, - components: { - funnelHeader, - funnelFooter, - vehicleBanner, - funnelSubHeader, - sideDoorOptions, - damageLocationQuestion, - windshieldOptions, - replaceOptionsQuestion, - Form, - alert, - }, + // Settle promises and get results + const promiseResultMap = [ + { + resultKey: "cmsContent", + promise: cmsContentPromise, + }, + { + resultKey: "damageOptions", + promise: damageOptionsPromise, + }, + ]; + + const resultMap = await settleAllPromises(promiseResultMap); + + // Call the "next" function to complete the transition to this page. + next((vm) => { + vm.setCmsContent(resultMap.cmsContent); + vm.$refs.damageLocation.initializeComponent(resultMap.damageOptions); + vm.$refs.sideDoorOptions.initializeComponent( + resultMap.damageOptions.driverSideOptions.availableReplacementOptions, + resultMap.damageOptions.passengerSideOptions.availableReplacementOptions + ); + vm.$refs.windshieldOptions.initializeComponent( + resultMap.damageOptions.windshieldOptions.availableReplacementOptions + ); + vm.$refs.backGlassOptions.initializeComponent( + resultMap.damageOptions.backGlassOptions.availableReplacementOptions + ); + }); + }, + data() { + return { + selectedDamageLocations: this.getDamageLocationsFromStore(), + sideDoorOptionsData: { + selectedDoorSides: this.getDoorSidesFromStore(), + selectedDriverSideReplaceOptions: this.getDriverSideReplaceOptionsFromStore(), + selectedPassengerSideReplaceOptions: this.getPassengerSideReplaceOptionsFromStore(), + }, + selectedWindshieldOptions: this.getWindshieldOptionsFromStore(), + selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore(), + }; + }, + mounted() { + this.attachCustomEvents(); + }, + methods: { + arePagePrerequisitesValid() { + if (store.getters.vehicle.carId) { + return true; + } + return false; + }, + + attachCustomEvents() { + if (this.$store.getters.vehicle.imageVifNumber) { + this.pushEventToGA( + this.GaCategories.EVOX, + `${this.GaActions.VIF}_${this.$store.getters.vehicle.imageVifNumber}`, + this.$store.getters.vehicle.carId, + true + ); + } + }, + + backButtonAction() { + // route to move backwards + this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route); + }, + + getDamageLocationsFromStore() { + var glassSelections = []; + + if ( + store.getters.damage.glassToReplace?.some((glass) => { + return glass.location === damageLocationsSelected.WINDSHIELD; + }) || + store.getters.damage.isRepair + ) { + glassSelections.push(damageLocationsSelected.WINDSHIELD); + } + if ( + store.getters.damage.glassToReplace?.some((glass) => { + return ( + glass.location === damageLocationsSelected.DRIVER || + glass.location === damageLocationsSelected.PASSENGER + ); + }) + ) { + glassSelections.push(damageLocationsSelected.SIDEDOOR); + } + + if ( + store.getters.damage.glassToReplace?.some((glass) => { + return glass.location === damageLocationsSelected.REAR; + }) + ) { + glassSelections.push(damageLocationsSelected.REARWINDOW); + } + + return glassSelections; + }, + + getWindshieldOptionsFromStore() { + var windShieldOptions = { + selectedWindshieldDamageType: "", + selectedWindshieldChipCount: null, + selectedWindshieldReplaceOptions: [], + }; + + if (store.getters.damage.isRepair === undefined) return windshieldOptions; + + if (store.getters.damage.isRepair) { + windShieldOptions.selectedWindshieldDamageType = damageLocationsSelected.REPAIR; + windShieldOptions.selectedWindshieldChipCount = store.getters.damage.numberOfChips; + } else { + if ( + store.getters.damage.glassToReplace?.some((glass) => { + return ( + glass.location === damageLocationsSelected.WINDSHIELD && + glass.name === damageLocationsSelected.SINGLE + ); + }) + ) { + windShieldOptions.selectedWindshieldDamageType = + damageLocationsSelected.REPLACE; + windShieldOptions.selectedWindshieldReplaceOptions.push( + damageLocationsSelected.SINGLE + ); + } + + if ( + store.getters.damage.glassToReplace?.some((glass) => { + return ( + glass.location === damageLocationsSelected.WINDSHIELD && + glass.name === damageLocationsSelected.DRIVER + ); + }) + ) { + windShieldOptions.selectedWindshieldDamageType = + damageLocationsSelected.REPLACE; + windShieldOptions.selectedWindshieldReplaceOptions.push( + damageLocationsSelected.DRIVER + ); + } + + if ( + store.getters.damage.glassToReplace?.some((glass) => { + return ( + glass.location === damageLocationsSelected.WINDSHIELD && + glass.name === damageLocationsSelected.PASSENGER + ); + }) + ) { + windShieldOptions.selectedWindshieldDamageType = + damageLocationsSelected.REPLACE; + windShieldOptions.selectedWindshieldReplaceOptions.push( + damageLocationsSelected.PASSENGER + ); + } + } + + return windShieldOptions; + }, + + getDoorSidesFromStore() { + var doorSides = []; + if ( + store.getters.damage.glassToReplace?.some((glass) => { + return glass.location === damageLocationsSelected.DRIVER; + }) + ) { + doorSides.push(damageLocationsSelected.DRIVERSIDE); + } + + if ( + store.getters.damage.glassToReplace?.some((glass) => { + return glass.location === damageLocationsSelected.PASSENGER; + }) + ) { + doorSides.push(damageLocationsSelected.PASSENGERSIDE); + } + + return doorSides; + }, + + getDriverSideReplaceOptionsFromStore() { + var driverSideReplaceOptions = []; + + store.getters.damage.glassToReplace?.forEach((glass) => { + if (glass.location === damageLocationsSelected.DRIVER) { + driverSideReplaceOptions.push(glass.name); + } + }); + + return driverSideReplaceOptions; + }, + + getPassengerSideReplaceOptionsFromStore() { + var passengerSideReplaceOptions = []; + + store.getters.damage.glassToReplace?.forEach((glass) => { + if (glass.location === damageLocationsSelected.PASSENGER) { + passengerSideReplaceOptions.push(glass.name); + } + }); + + return passengerSideReplaceOptions; + }, + + getRearReplaceOptionsFromStore() { + var rearReplaceOptions = store.getters.damage.glassToReplace?.filter( + (glass) => glass.location === damageLocationsSelected.REAR + )[0]?.name; + + return rearReplaceOptions; + }, + + async forwardButtonAction() { + await this.dispatchStoreAction( + this.storeActions.SAVE_VEHICLE_DAMAGE, + { + isWindshieldRepair: this.isWindshieldRepair, + selectedGlassToReplace: this.selectedGlassToReplace(), + selectedWindshieldChipCount: + this.selectedWindshieldOptions.selectedWindshieldChipCount, + }, + false + ); + + return this.navigateForward(); + }, + + navigateForward() { + // If vin already exists, navigate directly to vin-lookup + if (store.getters.vehicle.vin) { + this.$router.navigateWithSaving( + this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, + this.$route + ); + } else { + this.$router.navigateWithSaving( + this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, + this.$route + ); + } + }, + + selectedGlassToReplace() { + const selectedGlassToReplace = []; + if (this.isWindshieldDamageLocation && !this.isWindshieldRepair) { + this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach( + (wsItem) => { + selectedGlassToReplace.push({ + location: damageLocationsSelected.WINDSHIELD, + name: wsItem, + }); + } + ); + } + + if (this.isDriverSideReplace) { + this.sideDoorOptionsData.selectedDriverSideReplaceOptions.forEach((driverItem) => { + selectedGlassToReplace.push({ + location: damageLocationsSelected.DRIVER, + name: driverItem, + }); + }); + } + + if (this.isPassengerSideReplace) { + this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach( + (passengerItem) => { + selectedGlassToReplace.push({ + location: damageLocationsSelected.PASSENGER, + name: passengerItem, + }); + } + ); + } + + if (this.isRearWindowDamageLocation) { + selectedGlassToReplace.push({ + location: damageLocationsSelected.REAR, + name: this.selectedRearReplaceOptions, + }); + } + + 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() { + return ( + this.isWindshieldDamageLocation && + this.selectedWindshieldOptions.selectedWindshieldDamageType === + damageLocationsSelected.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; + }); + }, + hasRepairReplaceConflict() { + return ( + this.isWindshieldDamageLocation && + this.selectedDamageLocations.length > 1 && + this.isWindshieldRepair + ); + }, + hasSplitSingleConflict() { + if ( + !this.selectedDamageLocations?.includes("Windshield") || + this.selectedWindshieldOptions.selectedWindshieldDamageType === + damageLocationsSelected.REPAIR || + !this.selectedWindshieldOptions.selectedWindshieldReplaceOptions + ) + return false; + + return ( + this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( + (selectedSingleWindshield) => { + return ( + selectedSingleWindshield.toUpperCase() === + damageLocationsSelected.SINGLE.toUpperCase() + ); + } + ) && + (this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( + (selectedDriverWindshield) => { + return ( + selectedDriverWindshield.toUpperCase() === + damageLocationsSelected.DRIVER.toUpperCase() + ); + } + ) || + this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( + (selectedPassengerWindshield) => { + return ( + selectedPassengerWindshield.toUpperCase() === + damageLocationsSelected.PASSENGER.toUpperCase() + ); + } + )) + ); + }, + shouldDisplayVehicleChangeAlert() { + return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]; + }, + shouldHideBackButton() { + return ( + this.$store.getters.payment.insuranceCoverage.isVerified || + getFunnelCookie()?.HasDelayedClaimRegistration + ); + }, + }, + + components: { + funnelHeader, + funnelFooter, + vehicleBanner, + funnelSubHeader, + sideDoorOptions, + damageLocationQuestion, + windshieldOptions, + replaceOptionsQuestion, + Form, + alert, + }, }; 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 e14fdf330..48b813f2d 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 @@ -9,8 +9,7 @@ useTextForValue v-model="selectedValue" :validationRules="validationRules" - isRequired - /> + isRequired />
@@ -18,8 +17,8 @@ \ No newline at end of file + }, +}; + diff --git a/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.spec.js b/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.spec.js index 4b247d705..f5ccbce06 100644 --- a/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.spec.js +++ b/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.spec.js @@ -1,62 +1,69 @@ import { shallowMount } from "@vue/test-utils"; -import windshieldDamageTypeQuestion from"@/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question"; +import windshieldDamageTypeQuestion from "@/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-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("windshield-damage-type-question.vue", () => { test("Selected windshield damage is emitted upon selection.", async () => { - //Arrange - const { wrapper } = setupMocks({modelValueProp: "Repair"}); - - //Act - wrapper.setValue({ modelValue: "Replace" }); - await wrapper.vm.$nextTick(); - - //Assert - expect(wrapper.vm.selectedValues).toEqual("Repair"); - expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: "Replace" }]); - }); - }); + //Arrange + const { wrapper } = setupMocks({ modelValueProp: "Repair" }); - function setupMocks({ + //Act + wrapper.setValue({ modelValue: "Replace" }); + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.vm.selectedValues).toEqual("Repair"); + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: "Replace" }]); + }); +}); + +function setupMocks({ modelValueProp = "", groupName = "WindshieldDamageTypeQuestion", cmsQuestionText = "What's your windshield damage?", - cmsAnswers = [{Name: "Repair"}, {Name: "Replace"}], + cmsAnswers = [{ Name: "Repair" }, { Name: "Replace" }], dataFromStoreApi = [], - }) { - +}) { //Mock store store.dispatch = jest.fn(() => dataFromStoreApi); - store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} }; + store.getters = { + vehicle: { year: 2019, make: "honda", model: "civc", style: "2 Door", category: "CAR" }, + }; const mountOptions = getMountOptions({ - store: { - dispatch: store.dispatch, - getters: store.getters, - }, + store: { + dispatch: store.dispatch, + getters: store.getters, + }, }); - + //Mock props mountOptions.propsData = { - modelValue: modelValueProp + modelValue: modelValueProp, }; const mockMixin = { - methods: { - getCmsContent: jest.fn() - } - } + methods: { + getCmsContent: jest.fn(), + }, + }; mountOptions.mixins = [mockMixin]; - + const wrapper = shallowMount(windshieldDamageTypeQuestion, mountOptions); - + //Mock CMS content const cmsContent = { - groupName: groupName, - QuestionText: cmsQuestionText, - Answers: cmsAnswers, + 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/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue b/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue index 338ffc1e1..01e146080 100644 --- a/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue +++ b/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue @@ -9,8 +9,7 @@ v-model="selectedValues" :suppressError="suppressError" :validationRules="validationRules" - isRequired - /> + isRequired />
@@ -18,7 +17,7 @@ \ No newline at end of file + }, +}; + diff --git a/src/layouts/vehicle-damage/windshield-options/windshield-options.vue b/src/layouts/vehicle-damage/windshield-options/windshield-options.vue index 8081bb2b2..b878e5380 100644 --- a/src/layouts/vehicle-damage/windshield-options/windshield-options.vue +++ b/src/layouts/vehicle-damage/windshield-options/windshield-options.vue @@ -1,48 +1,47 @@ diff --git a/src/layouts/vehicle-make/make-question/make-question.spec.js b/src/layouts/vehicle-make/make-question/make-question.spec.js index 5bc1672d4..7025c118c 100644 --- a/src/layouts/vehicle-make/make-question/make-question.spec.js +++ b/src/layouts/vehicle-make/make-question/make-question.spec.js @@ -3,84 +3,77 @@ import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import store from "@/store"; jest.mock( - "@/store", - () => { - return {}; - }, - { virtual: true } + "@/store", + () => { + return {}; + }, + { virtual: true } ); describe("make-question.vue", () => { - test("Selected make is emitted upon selection.", async () => { - //Arrange - const { wrapper } = setupMocks({ modelValueProp: "honda" }); - const makeToSelect = "ford"; + test("Selected make is emitted upon selection.", async () => { + //Arrange + const { wrapper } = setupMocks({ modelValueProp: "honda" }); + const makeToSelect = "ford"; - //Act - wrapper.setValue({ selectedMake: makeToSelect }); - await wrapper.vm.$nextTick(); + //Act + wrapper.setValue({ selectedMake: makeToSelect }); + await wrapper.vm.$nextTick(); - //Assert - expect(wrapper.emitted()["update:modelValue"][0]).toEqual([ - { selectedMake: "ford" }, - ]); - }); + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ selectedMake: "ford" }]); + }); }); describe("make-question.vue", () => { - test("Data from store api are used as radio question answers.", async () => { - //Arrange - const { wrapper, cmsContent } = setupMocks({ - dataFromStoreApi: ["honda", "ford", "dodge"], - }); + test("Data from store api are used as radio question answers.", async () => { + //Arrange + const { wrapper, cmsContent } = setupMocks({ + dataFromStoreApi: ["honda", "ford", "dodge"], + }); - //Act - const initialData = makeQuestion.methods.loadInitialData.call(wrapper.vm); - makeQuestion.methods.initializeComponent.call( - wrapper.vm, - initialData - ); + //Act + const initialData = makeQuestion.methods.loadInitialData.call(wrapper.vm); + makeQuestion.methods.initializeComponent.call(wrapper.vm, initialData); - //Assert - const buttonQuestionComponent = await wrapper.findComponent({ - name: "buttonQuestion", + //Assert + const buttonQuestionComponent = await wrapper.findComponent({ + name: "buttonQuestion", + }); + expect(buttonQuestionComponent.attributes("answers")).toBe("honda,ford,dodge"); }); - expect(buttonQuestionComponent.attributes("answers")).toBe( - "honda,ford,dodge" - ); - }); }); function setupMocks({ - modelValueProp = "1900", - cmsQuestionText = "CMS text goes here", - dataFromStoreApi = [], + modelValueProp = "1900", + cmsQuestionText = "CMS text goes here", + dataFromStoreApi = [], }) { - //Mock store - store.dispatch = jest.fn(() => dataFromStoreApi); - store.getters = { vehicle: { year: 2019 } }; - const mountOptions = getMountOptions({ - store: { - dispatch: store.dispatch, - getters: store.getters, - }, - }); + //Mock store + store.dispatch = jest.fn(() => dataFromStoreApi); + store.getters = { vehicle: { year: 2019 } }; + const mountOptions = getMountOptions({ + store: { + dispatch: store.dispatch, + getters: store.getters, + }, + }); - //Mock props - const mockMixin = { - methods: { - getCmsContent: jest.fn() - } - } - mountOptions.propsData = { - modelValue: modelValueProp, - }; - mountOptions.mixins = [mockMixin]; - const wrapper = shallowMount(makeQuestion, mountOptions); + //Mock props + const mockMixin = { + methods: { + getCmsContent: jest.fn(), + }, + }; + mountOptions.propsData = { + modelValue: modelValueProp, + }; + mountOptions.mixins = [mockMixin]; + const wrapper = shallowMount(makeQuestion, mountOptions); - //Mock CMS content - const cmsContent = { - QuestionText: cmsQuestionText, - }; - return { wrapper, cmsContent }; + //Mock CMS content + const cmsContent = { + QuestionText: cmsQuestionText, + }; + return { wrapper, cmsContent }; } diff --git a/src/layouts/vehicle-make/make-question/make-question.vue b/src/layouts/vehicle-make/make-question/make-question.vue index 28246814f..55edc05ce 100644 --- a/src/layouts/vehicle-make/make-question/make-question.vue +++ b/src/layouts/vehicle-make/make-question/make-question.vue @@ -1,15 +1,14 @@ diff --git a/src/layouts/vehicle-make/vehicle-make.spec.js b/src/layouts/vehicle-make/vehicle-make.spec.js index c533516ce..3b169a9b9 100644 --- a/src/layouts/vehicle-make/vehicle-make.spec.js +++ b/src/layouts/vehicle-make/vehicle-make.spec.js @@ -93,9 +93,9 @@ describe("vehicle-make.vue", () => { test("Year set, arePagePrerequisitesValid should be true", async () => { //Arrange const { wrapper } = setupMocks({ - vehicleData: { - year: 2019 - } + vehicleData: { + year: 2019, + }, }); //Act @@ -133,7 +133,7 @@ describe("vehicle-make.vue", () => { navigate: jest.fn(), navigateWithSaving: jest.fn(), }, - } + }, }); // Act @@ -184,7 +184,7 @@ function setupMocks({ makeQuestionInitialData = {}, pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {}, - vehicleData = {} + vehicleData = {}, }) { //Mock api responses const apiResponses = { @@ -206,8 +206,8 @@ function setupMocks({ const apiPromise = Promise.resolve(apiResponses); store.getters = { - vehicle: vehicleData - } + vehicle: vehicleData, + }; fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); settleAllPromises.mockImplementation(() => apiPromise); diff --git a/src/layouts/vehicle-make/vehicle-make.vue b/src/layouts/vehicle-make/vehicle-make.vue index 2bd49498f..1034b0959 100644 --- a/src/layouts/vehicle-make/vehicle-make.vue +++ b/src/layouts/vehicle-make/vehicle-make.vue @@ -1,27 +1,22 @@ diff --git a/src/layouts/vehicle-model/model-question/model-question.spec.js b/src/layouts/vehicle-model/model-question/model-question.spec.js index 805726c12..16ca638d9 100644 --- a/src/layouts/vehicle-model/model-question/model-question.spec.js +++ b/src/layouts/vehicle-model/model-question/model-question.spec.js @@ -3,85 +3,77 @@ import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import store from "@/store"; jest.mock( - "@/store", - () => { - return {}; - }, - { virtual: true } + "@/store", + () => { + return {}; + }, + { virtual: true } ); describe("model-question.vue", () => { - test("Selected model is emitted upon selection.", async () => { - //Arrange - const { wrapper } = setupMocks({ modelValueProp: "Accord" }); - const modelToSelect = "Civic"; + test("Selected model is emitted upon selection.", async () => { + //Arrange + const { wrapper } = setupMocks({ modelValueProp: "Accord" }); + const modelToSelect = "Civic"; - //Act - wrapper.setValue({ selectedModel: modelToSelect }); - await wrapper.vm.$nextTick(); + //Act + wrapper.setValue({ selectedModel: modelToSelect }); + await wrapper.vm.$nextTick(); - //Assert - expect(wrapper.emitted()["update:modelValue"][0]).toEqual([ - { selectedModel: "Civic" }, - ]); - }); + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ selectedModel: "Civic" }]); + }); }); - describe("model-question.vue", () => { - test("Data from store api are used as radio question answers.", async () => { - //Arrange - const { wrapper, cmsContent } = setupMocks({ - dataFromStoreApi: ["accord", "civic", "insight"], - }); + test("Data from store api are used as radio question answers.", async () => { + //Arrange + const { wrapper, cmsContent } = setupMocks({ + dataFromStoreApi: ["accord", "civic", "insight"], + }); - //Act - const initialData = modelQuestion.methods.loadInitialData.call(wrapper.vm); - modelQuestion.methods.initializeComponent.call( - wrapper.vm, - initialData - ); + //Act + const initialData = modelQuestion.methods.loadInitialData.call(wrapper.vm); + modelQuestion.methods.initializeComponent.call(wrapper.vm, initialData); - //Assert - const buttonQuestionComponent = await wrapper.findComponent({ - name: "buttonQuestion", + //Assert + const buttonQuestionComponent = await wrapper.findComponent({ + name: "buttonQuestion", + }); + expect(buttonQuestionComponent.attributes("answers")).toBe("accord,civic,insight"); }); - expect(buttonQuestionComponent.attributes("answers")).toBe( - "accord,civic,insight" - ); - }); }); function setupMocks({ - modelValueProp = "1900", - cmsQuestionText = "CMS text goes here", - dataFromStoreApi = [], + modelValueProp = "1900", + cmsQuestionText = "CMS text goes here", + dataFromStoreApi = [], }) { - //Mock store - store.dispatch = jest.fn(() => dataFromStoreApi); - store.getters = { vehicle: { year: 2019, make: "honda" } }; - const mountOptions = getMountOptions({ - store: { - dispatch: store.dispatch, - getters: store.getters, - }, - }); + //Mock store + store.dispatch = jest.fn(() => dataFromStoreApi); + store.getters = { vehicle: { year: 2019, make: "honda" } }; + const mountOptions = getMountOptions({ + store: { + dispatch: store.dispatch, + getters: store.getters, + }, + }); - //Mock props - const mockMixin = { - methods: { - getCmsContent: jest.fn() - } - } - mountOptions.propsData = { - modelValue: modelValueProp, - }; - mountOptions.mixins = [mockMixin]; - const wrapper = shallowMount(modelQuestion, mountOptions); + //Mock props + const mockMixin = { + methods: { + getCmsContent: jest.fn(), + }, + }; + mountOptions.propsData = { + modelValue: modelValueProp, + }; + mountOptions.mixins = [mockMixin]; + const wrapper = shallowMount(modelQuestion, mountOptions); - //Mock CMS content - const cmsContent = { - QuestionText: cmsQuestionText, - }; - return { wrapper, cmsContent }; + //Mock CMS content + const cmsContent = { + QuestionText: cmsQuestionText, + }; + return { wrapper, cmsContent }; } diff --git a/src/layouts/vehicle-model/model-question/model-question.vue b/src/layouts/vehicle-model/model-question/model-question.vue index bc20bdc6a..a236e50ef 100644 --- a/src/layouts/vehicle-model/model-question/model-question.vue +++ b/src/layouts/vehicle-model/model-question/model-question.vue @@ -1,15 +1,14 @@ diff --git a/src/layouts/vehicle-model/vehicle-model.spec.js b/src/layouts/vehicle-model/vehicle-model.spec.js index a1853969e..33b74482d 100644 --- a/src/layouts/vehicle-model/vehicle-model.spec.js +++ b/src/layouts/vehicle-model/vehicle-model.spec.js @@ -12,138 +12,136 @@ import baseMixin from "@/mixins/base-mixin.js"; // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ - settleAllPromises: jest.fn(), + settleAllPromises: jest.fn(), })); // Mock fetchCmsContentForPage jest.mock("@/helpers/cms-content-helper", () => ({ - fetchCmsContentForPage: jest.fn(), + fetchCmsContentForPage: jest.fn(), })); // Mock Store jest.mock("@/store", () => ({ - commit: jest.fn(), - dispatch: jest.fn(), - getters: { - vehicle: { - make: "Acura", + commit: jest.fn(), + dispatch: jest.fn(), + getters: { + vehicle: { + make: "Acura", + }, }, - }, })); describe("vehicle-model.vue", () => { - test("Model question component is initized with api data", async (done) => { - //Arange - const modelQuestionInitialData = ["accord", "civic", "insight"]; - const { wrapper, apiPromise } = setupMocks({ - modelQuestionInitialData: modelQuestionInitialData, + test("Model question component is initized with api data", async (done) => { + //Arange + const modelQuestionInitialData = ["accord", "civic", "insight"]; + const { wrapper, apiPromise } = setupMocks({ + modelQuestionInitialData: modelQuestionInitialData, + }); + //Act + vehicleModel.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-model" } }, + undefined, + (c) => c(wrapper.vm) + ); + //Assert + apiPromise.finally(() => { + expect(modelQuestion.methods.initializeComponent).toHaveBeenCalledWith( + modelQuestionInitialData + ); + done(); + }); }); - //Act - vehicleModel.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-model" } }, - undefined, - (c) => c(wrapper.vm) - ); - //Assert - apiPromise.finally(() => { - expect(modelQuestion.methods.initializeComponent).toHaveBeenCalledWith( - modelQuestionInitialData - ); - done(); - }); - }); }); describe("vehicle-model.vue", () => { - test("BackButtonAction triggers a router.navigateWithoutSaving change", async (done) => { - //Arrange - const { wrapper, apiPromise } = setupMocks({ - pageHeaderWidgetHeaderText: "Select a model to get started", - mountOptionsMockData: { - router: { - navigate: jest.fn(), - navigateWithoutSaving: jest.fn() - }, - }, + test("BackButtonAction triggers a router.navigateWithoutSaving change", async (done) => { + //Arrange + const { wrapper, apiPromise } = setupMocks({ + pageHeaderWidgetHeaderText: "Select a model to get started", + mountOptionsMockData: { + router: { + navigate: jest.fn(), + navigateWithoutSaving: jest.fn(), + }, + }, + }); + //Act + vehicleModel.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-model" } }, + undefined, + (c) => c(wrapper.vm) + ); + wrapper.vm.backButtonAction(); + await nextTick(); + //Assert + apiPromise.finally(() => { + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); + done(); + }); }); - //Act - vehicleModel.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-model" } }, - undefined, - (c) => c(wrapper.vm) - ); - wrapper.vm.backButtonAction(); - await nextTick(); - //Assert - apiPromise.finally(() => { - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); - done(); - }); - }); }); describe("vehicle-model.vue", () => { - test("Make set, arePagePrerequisitesValid should be true ", async () => { - //Arrange - const { wrapper } = setupMocks({}); + test("Make set, arePagePrerequisitesValid should be true ", async () => { + //Arrange + const { wrapper } = setupMocks({}); - //Act - vehicleModel.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-model" } }, - undefined, - (c) => c(wrapper.vm) - ); + //Act + vehicleModel.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-model" } }, + undefined, + (c) => c(wrapper.vm) + ); - let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - await nextTick(); + let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + await nextTick(); - //Assert - expect(arePagePrerequisitesValid).toBe(true); - }); + //Assert + expect(arePagePrerequisitesValid).toBe(true); + }); }); - function setupMocks({ - buttonQuestionContent = {}, - modelQuestionInitialData = {}, - pageHeaderWidgetHeaderText = {}, - mountOptionsMockData = {}, + buttonQuestionContent = {}, + modelQuestionInitialData = {}, + pageHeaderWidgetHeaderText = {}, + mountOptionsMockData = {}, }) { - //Mock api responses - const apiResponses = { - cmsContent: { - FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, - VehicleModelQuestion: buttonQuestionContent, - 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", - }, - }, - modelQuestionInitialData: modelQuestionInitialData, - }; - const apiPromise = Promise.resolve(apiResponses); + //Mock api responses + const apiResponses = { + cmsContent: { + FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, + VehicleModelQuestion: buttonQuestionContent, + 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", + }, + }, + modelQuestionInitialData: modelQuestionInitialData, + }; + const apiPromise = Promise.resolve(apiResponses); - settleAllPromises.mockImplementation(() => apiPromise); - fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); + settleAllPromises.mockImplementation(() => apiPromise); + fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); - //Mock model question methods - modelQuestion.methods = { - loadInitialData: jest.fn(), - initializeComponent: jest.fn(), - }; - const mountOptions = getMountOptions(mountOptionsMockData); - const wrapper = shallowMount(vehicleModel, mountOptions); - wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; + //Mock model question methods + modelQuestion.methods = { + loadInitialData: jest.fn(), + initializeComponent: jest.fn(), + }; + const mountOptions = getMountOptions(mountOptionsMockData); + const wrapper = shallowMount(vehicleModel, mountOptions); + wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; - const modelQuestionWrapper = wrapper.findComponent({ name: "modelQuestion" }); - modelQuestionWrapper.vm.initializeComponent = - modelQuestion.methods.initializeComponent; + const modelQuestionWrapper = wrapper.findComponent({ name: "modelQuestion" }); + modelQuestionWrapper.vm.initializeComponent = modelQuestion.methods.initializeComponent; - return { wrapper, apiPromise }; + return { wrapper, apiPromise }; } diff --git a/src/layouts/vehicle-model/vehicle-model.vue b/src/layouts/vehicle-model/vehicle-model.vue index 2db7e34c8..c53ed19d5 100644 --- a/src/layouts/vehicle-model/vehicle-model.vue +++ b/src/layouts/vehicle-model/vehicle-model.vue @@ -1,20 +1,22 @@ 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 index c4b2bbd22..41f38088e 100644 --- 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 @@ -3,24 +3,39 @@ 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 }); +jest.mock( + "@/store", + () => { + return {}; + }, + { virtual: true } +); store.getters = { - pageData: jest.fn() + pageData: jest.fn(), }; 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" }] } + { + 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" }, + ], + }, ], locationProp: "Rear", nameProp: "Stationary", - modelValueProp: {} -} + modelValueProp: {}, +}; describe("glass-part-question.vue", () => { test("Part data passed in, should map data for ButtonQuestion (radio type)", async () => { - //Arrange const { wrapper } = setupMocks(featureListData); @@ -29,15 +44,17 @@ describe("glass-part-question.vue", () => { //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"); - + 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 location and tintColor", async () => { - //Arrange const { wrapper } = setupMocks(featureListData); @@ -48,12 +65,9 @@ describe("glass-part-question.vue", () => { //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); @@ -64,14 +78,21 @@ describe("glass-part-question.vue", () => { //Assert expect(tintSourceImage).toBe(""); - }); test("Should emit updateModelValue, and have correct attributes", async () => { //Arrange const { wrapper } = setupMocks(featureListData); store.getters.pageData.mockReset(); - store.getters.pageData.mockReturnValueOnce({ partsOrQuestions: [{name: "Stationary", location: "Rear", parts: [{ partNumber: "DB12209GTYN", color: "Green Tint"}]}] }); + store.getters.pageData.mockReturnValueOnce({ + partsOrQuestions: [ + { + name: "Stationary", + location: "Rear", + parts: [{ partNumber: "DB12209GTYN", color: "Green Tint" }], + }, + ], + }); //Act await wrapper.vm.$nextTick(); @@ -79,30 +100,31 @@ describe("glass-part-question.vue", () => { name: "buttonQuestion", }); - await wrapper.setData({ selectedTint: 'Green Tint' }); + await wrapper.setData({ selectedTint: "Green Tint" }); // to trigger the computed setter wrapper.vm.selectedPartNumber = "DB12209GTYN"; //Assert - expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ partNumber: "DB12209GTYN", color: "Green Tint"}]); + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([ + { partNumber: "DB12209GTYN", color: "Green Tint" }, + ]); expect(listCard.attributes("groupname")).toBe("Rear-Stationary"); expect(listCard.attributes("validationrules")).toBe("Rear-Stationary-tint-required"); }); test("ResetTintAndPartSelections, should reset data elements ", async () => { - //Arrange const { wrapper } = setupMocks(featureListData); //Act await wrapper.vm.$nextTick(); - await wrapper.setData({ selectedTint: 'Green Tint', selectedPartNumber: "test" }); - - expect(wrapper.vm.selectedTint).toEqual('Green Tint'); - expect(wrapper.vm.selectedPartNumber).toEqual('test'); + await wrapper.setData({ selectedTint: "Green Tint", selectedPartNumber: "test" }); + + expect(wrapper.vm.selectedTint).toEqual("Green Tint"); + expect(wrapper.vm.selectedPartNumber).toEqual("test"); await wrapper.vm.ResetTintAndPartSelections(); - + expect(wrapper.vm.selectedTint).toEqual("Green Tint"); expect(wrapper.vm.selectedPartNumber).toEqual(null); }); @@ -110,7 +132,15 @@ describe("glass-part-question.vue", () => { test("default is selected if only one option", async () => { // Arrange store.getters.pageData.mockReset(); - store.getters.pageData.mockReturnValueOnce({ partsOrQuestions: [{name: "Stationary", location: "Rear", parts: [{ partNumber: "DB12209GTYN", color: "Green Tint"}]}] }); + store.getters.pageData.mockReturnValueOnce({ + partsOrQuestions: [ + { + name: "Stationary", + location: "Rear", + parts: [{ partNumber: "DB12209GTYN", color: "Green Tint" }], + }, + ], + }); const { wrapper } = setupMocks(featureListData); // Act @@ -118,9 +148,9 @@ describe("glass-part-question.vue", () => { await wrapper.setData({ selectedTint: "Green Tint" }); // take emitted value, pass down as modelValue // yes, yes, it's not ideal - await wrapper.setProps({ modelValue: wrapper.emitted()["update:modelValue"][0][0] }) + await wrapper.setProps({ modelValue: wrapper.emitted()["update:modelValue"][0][0] }); await wrapper.vm.$nextTick(); - + // Assert expect(wrapper.vm.selectedPartNumber).toBe("DB12209GTYN"); }); @@ -128,70 +158,115 @@ describe("glass-part-question.vue", () => { test("default is not selected if more than one option", async () => { // Arrange store.getters.pageData.mockReset(); - store.getters.pageData.mockReturnValueOnce({ partsOrQuestions: [{name: "Stationary", location: "Rear", parts: [{ partNumber: "DB12209GTYN", color: "Green Tint"}, { partNumber: "DB12209GTYNXXX", color: "Green Tint"}]}] }); + store.getters.pageData.mockReturnValueOnce({ + partsOrQuestions: [ + { + name: "Stationary", + location: "Rear", + parts: [ + { partNumber: "DB12209GTYN", color: "Green Tint" }, + { partNumber: "DB12209GTYNXXX", color: "Green Tint" }, + ], + }, + ], + }); const { wrapper } = setupMocks(featureListData); // Act await wrapper.vm.$nextTick(); await wrapper.setData({ selectedTint: "Green Tint" }); await wrapper.vm.$nextTick(); - + // Assert expect(wrapper.emitted()["update:modelValue"]).toBeFalsy(); expect(wrapper.vm.selectedPartNumber).toBeFalsy(); - }) + }); const partsForSelectedTintTestCases = [ - ["Rear", "Stationary", "Green Tint", [{ partNumber: "Glass1", color: "Green Tint"}, { partNumber: "Glass3", color: "Green Tint"}, { partNumber: "Glass4", color: "Green Tint"}, { partNumber: "Glass6", color: "Green Tint"} ]], - ["Rear", "Stationary", "Blue Tint", [{ partNumber: "Glass2", color: "Blue Tint"}, { partNumber: "Glass5", color: "Blue Tint"}]], - ["Rear", "Stationary", "Red Tint", [{ partNumber: "Glass7", color: "Red Tint"}]], - ["Windshield", "Single", "Green Tint", [{ partNumber: "Windshield1", color: "Green Tint"}, { partNumber: "Windshield2", color: "Green Tint"}]], + [ + "Rear", + "Stationary", + "Green Tint", + [ + { partNumber: "Glass1", color: "Green Tint" }, + { partNumber: "Glass3", color: "Green Tint" }, + { partNumber: "Glass4", color: "Green Tint" }, + { partNumber: "Glass6", color: "Green Tint" }, + ], + ], + [ + "Rear", + "Stationary", + "Blue Tint", + [ + { partNumber: "Glass2", color: "Blue Tint" }, + { partNumber: "Glass5", color: "Blue Tint" }, + ], + ], + ["Rear", "Stationary", "Red Tint", [{ partNumber: "Glass7", color: "Red Tint" }]], + [ + "Windshield", + "Single", + "Green Tint", + [ + { partNumber: "Windshield1", color: "Green Tint" }, + { partNumber: "Windshield2", color: "Green Tint" }, + ], + ], ["Windshield", "Single", "Blue Tint", []], - ["Driver", "Quarter", "Green Tint", []] + ["Driver", "Quarter", "Green Tint", []], ]; - test.each(partsForSelectedTintTestCases)("partsForSelectedTint returns correct parts", async (location, name, selectedTint, expectedResults) => { - // Arrange - store.getters.pageData.mockReset(); - store.getters.pageData.mockReturnValueOnce({ partsOrQuestions: [ - { - name: "Stationary", - location: "Rear", - parts: [ - { partNumber: "Glass1", color: "Green Tint"}, - { partNumber: "Glass2", color: "Blue Tint"}, - { partNumber: "Glass3", color: "Green Tint"}, - { partNumber: "Glass4", color: "Green Tint"}, - { partNumber: "Glass5", color: "Blue Tint"}, - { partNumber: "Glass6", color: "Green Tint"}, - { partNumber: "Glass7", color: "Red Tint"} - ] - }, - { - name: "Single", - location: "Windshield", - parts: [{ partNumber: "Windshield1", color: "Green Tint"}, { partNumber: "Windshield2", color: "Green Tint"}] - } - ]}); - const { wrapper } = setupMocks({ - locationProp: location, - nameProp: name, - colorAnswersProp: [], - }); + test.each(partsForSelectedTintTestCases)( + "partsForSelectedTint returns correct parts", + async (location, name, selectedTint, expectedResults) => { + // Arrange + store.getters.pageData.mockReset(); + store.getters.pageData.mockReturnValueOnce({ + partsOrQuestions: [ + { + name: "Stationary", + location: "Rear", + parts: [ + { partNumber: "Glass1", color: "Green Tint" }, + { partNumber: "Glass2", color: "Blue Tint" }, + { partNumber: "Glass3", color: "Green Tint" }, + { partNumber: "Glass4", color: "Green Tint" }, + { partNumber: "Glass5", color: "Blue Tint" }, + { partNumber: "Glass6", color: "Green Tint" }, + { partNumber: "Glass7", color: "Red Tint" }, + ], + }, + { + name: "Single", + location: "Windshield", + parts: [ + { partNumber: "Windshield1", color: "Green Tint" }, + { partNumber: "Windshield2", color: "Green Tint" }, + ], + }, + ], + }); + const { wrapper } = setupMocks({ + locationProp: location, + nameProp: name, + colorAnswersProp: [], + }); - // Act - await wrapper.setData({ selectedTint: selectedTint }); + // Act + await wrapper.setData({ selectedTint: selectedTint }); - // Assert - expect(expectedResults).toEqual(wrapper.vm.partsForSelectedTint); - }); + // Assert + expect(expectedResults).toEqual(wrapper.vm.partsForSelectedTint); + } + ); }); - - function setupMocks({ nameProp, locationProp, colorAnswersProp, modelValueProp }) { //Mock store - store.getters.pageData.mockReturnValueOnce({ partsOrQuestions: [{name: "Stationary", location: "Rear", parts: []}] }); - store.getters.lineItems = { glassParts: {} } + store.getters.pageData.mockReturnValueOnce({ + partsOrQuestions: [{ name: "Stationary", location: "Rear", parts: [] }], + }); + store.getters.lineItems = { glassParts: {} }; const mountOptions = getMountOptions({ store: { @@ -199,8 +274,8 @@ function setupMocks({ nameProp, locationProp, colorAnswersProp, modelValueProp } }, route: { query: { - fmgPage: 'vehicle-parts', - } + fmgPage: "vehicle-parts", + }, }, }); @@ -208,10 +283,10 @@ function setupMocks({ nameProp, locationProp, colorAnswersProp, modelValueProp } name: nameProp, location: locationProp, colorAnswers: colorAnswersProp, - modelValue: modelValueProp + 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 index 70fa62d2c..bc14812b2 100644 --- a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue +++ b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue @@ -26,8 +26,7 @@ :loaderEnabled="false" isRequired :groupName="`${location}-${name}-${selectedTint}`" - :validationRules="partValidationRules" - /> + :validationRules="partValidationRules" />
@@ -77,20 +76,14 @@ export default { tintValidationRules() { const validationRuleName = `${this.location}-${this.name}-tint-required`; - defineRule( - validationRuleName, - required(errorMessages.OPTION_REQUIRED) - ); + defineRule(validationRuleName, required(errorMessages.OPTION_REQUIRED)); return validationRuleName; }, partValidationRules() { const validationRuleName = `${this.location}-${this.name}-part-required`; - defineRule( - validationRuleName, - required(errorMessages.OPTION_REQUIRED) - ); + defineRule(validationRuleName, required(errorMessages.OPTION_REQUIRED)); return validationRuleName; }, colorQuestionText() { @@ -124,9 +117,7 @@ export default { set(newValue) { this.$emit( "update:modelValue", - this.partsForSelectedTint.filter( - (part) => part.partNumber == newValue - )[0] + this.partsForSelectedTint.filter((part) => part.partNumber == newValue)[0] ); }, }, @@ -135,16 +126,10 @@ export default { const matchingGlass = this.PartDataFromApi.partsOrQuestions?.filter( (dataForGlassLocationAndName) => dataForGlassLocationAndName.name == this.name && - dataForGlassLocationAndName.location == - this.location - ); - const matchingGlassParts = - matchingGlass?.length == 1 ? matchingGlass[0].parts : []; - return ( - matchingGlassParts.filter( - (part) => part.color == this.selectedTint - ) ?? [] + dataForGlassLocationAndName.location == this.location ); + const matchingGlassParts = matchingGlass?.length == 1 ? matchingGlass[0].parts : []; + return matchingGlassParts.filter((part) => part.color == this.selectedTint) ?? []; }, // Creates a map of the feature list data in the correct Name/Value @@ -156,15 +141,12 @@ export default { arr[itemColor] = arr[itemColor] || []; // Map the data to Name/Text object for ButtonQuestion - const mappedItem = item.FeatureAnswers.reduce( - (featureArr, item) => { - featureArr["Text"] = item.FeatureAnswerText; // Display to User - featureArr["Name"] = item.PartNumber; // Backing Value + const mappedItem = item.FeatureAnswers.reduce((featureArr, item) => { + featureArr["Text"] = item.FeatureAnswerText; // Display to User + featureArr["Name"] = item.PartNumber; // Backing Value - return featureArr; - }, - {} - ); + return featureArr; + }, {}); // Add onto the final object arr[itemColor].push(mappedItem); @@ -176,18 +158,14 @@ export default { }, PartDataFromApi() { - return ( - this.$store.getters.pageData(this.$route.query.fmgPage) ?? {} - ); + return this.$store.getters.pageData(this.$route.query.fmgPage) ?? {}; }, }, methods: { // Initialize the component data initializeComponent(cmsContent) { - this.glassColorQuestion = - cmsContent.ColorQuestionWidget.QuestionText; - this.glassFeatureQuestion = - cmsContent.FeatureQuestionWidget.QuestionText; + this.glassColorQuestion = cmsContent.ColorQuestionWidget.QuestionText; + this.glassFeatureQuestion = cmsContent.FeatureQuestionWidget.QuestionText; }, // Gets tint images based on the glass type, and tint name. @@ -195,10 +173,7 @@ export default { getTintSourceImage(location, tintColor) { const tintSourceObject = getTintImage(location, tintColor); - if ( - tintSourceObject === undefined || - tintSourceObject.src == undefined - ) { + if (tintSourceObject === undefined || tintSourceObject.src == undefined) { return ""; } @@ -215,8 +190,7 @@ export default { // Check if only a single part is present for the tint and set the v-model if it is. AutoSelectIfSinglePart() { if (this.partsForSelectedTint?.length == 1) { - this.selectedPartNumber = - this.partsForSelectedTint[0].partNumber; + this.selectedPartNumber = this.partsForSelectedTint[0].partNumber; } }, @@ -225,7 +199,7 @@ export default { this.$nextTick(() => { if (this.modelValue !== undefined) { // Populate button-question model-value if parts data already exists in VueX - this.selectedTint = this.modelValue?.color + this.selectedTint = this.modelValue?.color; } }); }, diff --git a/src/layouts/vehicle-parts/vehicle-parts.spec.js b/src/layouts/vehicle-parts/vehicle-parts.spec.js index c736701da..83f381f53 100644 --- a/src/layouts/vehicle-parts/vehicle-parts.spec.js +++ b/src/layouts/vehicle-parts/vehicle-parts.spec.js @@ -25,17 +25,23 @@ jest.mock("@/helpers/cms-content-helper", () => ({ })); jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({ - navigateToHeritageFunnel: jest.fn() - })); + navigateToHeritageFunnel: jest.fn(), +})); // Mock store -jest.mock("@/store", () => { return {}; }, { virtual: true }); +jest.mock( + "@/store", + () => { + return {}; + }, + { virtual: true } +); store.getters = { pageData: jest.fn(), damage: { - isRepair: false - } + isRepair: false, + }, }; const basePartResponse = { @@ -50,7 +56,7 @@ const basePartResponse = { color: "Green Tint", requiresRecalibration: false, requiresCapabilityQuestions: false, - childParts: null + childParts: null, }, { partNumber: "DB12209YPYNOEM", @@ -58,40 +64,37 @@ const basePartResponse = { color: "Gray Tint Privacy", requiresRecalibration: false, requiresCapabilityQuestions: false, - childParts: null - } + childParts: null, + }, ], - partQuestions: null - } - ] -} - + partQuestions: null, + }, + ], +}; describe("vehicle-parts.vue", () => { - test("Set cms content called on load", async (done) => { //Arrange store.getters.pageData.mockReturnValue(basePartResponse); - store.getters.lineItems = { glassParts: null } + store.getters.lineItems = { glassParts: null }; - const { wrapper, apiPromise } = setupMocks( - { - mountOptionsMockData: { - router: { - navigate: jest.fn(), - navigateWithSaving: jest.fn(), - navigateWithoutSaving: jest.fn(), + const { wrapper, apiPromise } = setupMocks({ + mountOptionsMockData: { + router: { + navigate: jest.fn(), + navigateWithSaving: jest.fn(), + navigateWithoutSaving: jest.fn(), + }, + route: { + query: { + fmgPage: "vehicle-parts", }, - route: { - query: { - fmgPage: 'vehicle-parts', - } - }, - store: { - getters: store.getters - }, - } - }); + }, + store: { + getters: store.getters, + }, + }, + }); //Act vehicleParts.beforeRouteEnter.call( @@ -110,10 +113,9 @@ describe("vehicle-parts.vue", () => { }); test("PageData / isRepair populated in Vuex. arePagePrerequisitesValid should be true ", async () => { - //Arrange store.getters.pageData.mockReturnValue(basePartResponse); - store.getters.lineItems = { glassParts: null } + store.getters.lineItems = { glassParts: null }; const { wrapper } = setupMocks({ mountOptionsMockData: { @@ -124,13 +126,13 @@ describe("vehicle-parts.vue", () => { }, route: { query: { - fmgPage: 'vehicle-parts', - } + fmgPage: "vehicle-parts", + }, }, store: { - getters: store.getters + getters: store.getters, }, - } + }, }); //Act @@ -149,10 +151,9 @@ describe("vehicle-parts.vue", () => { }); test("Initial data, should populate this.selectedGlassParts", async () => { - //Arrange store.getters.pageData.mockReturnValue(basePartResponse); - store.getters.lineItems = { glassParts: [{ partNumber: 'DB12209YPYNOEM' }] } + store.getters.lineItems = { glassParts: [{ partNumber: "DB12209YPYNOEM" }] }; const { wrapper } = setupMocks({ mountOptionsMockData: { @@ -163,13 +164,13 @@ describe("vehicle-parts.vue", () => { }, route: { query: { - fmgPage: 'vehicle-parts', - } + fmgPage: "vehicle-parts", + }, }, store: { - getters: store.getters + getters: store.getters, }, - } + }, }); //Act @@ -190,8 +191,8 @@ describe("vehicle-parts.vue", () => { color: "Gray Tint Privacy", requiresRecalibration: false, requiresCapabilityQuestions: false, - childParts: null - } + childParts: null, + }, }); }); @@ -207,8 +208,8 @@ describe("vehicle-parts.vue", () => { }, route: { query: { - fmgPage: 'vehicle-parts', - } + fmgPage: "vehicle-parts", + }, }, store: { getters: { @@ -219,19 +220,21 @@ describe("vehicle-parts.vue", () => { name: "Stationary", location: "Rear", parts: null, - partQuestions: [{ - testProperty: "some value" - }] - } - ] - } + partQuestions: [ + { + testProperty: "some value", + }, + ], + }, + ], + }; }, lineItems: { - glassParts: null - } - } + glassParts: null, + }, + }, }, - } + }, }); //Act @@ -245,8 +248,10 @@ describe("vehicle-parts.vue", () => { wrapper.vm.backButtonAction(); //Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, wrapper.vm.$route); - + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith( + navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, + wrapper.vm.$route + ); }); test("User did not have part questions > BackButtonAction triggers a router.navigateWithoutSaving change with correct scenario", async () => { @@ -261,18 +266,18 @@ describe("vehicle-parts.vue", () => { }, route: { query: { - fmgPage: 'vehicle-parts', - } + fmgPage: "vehicle-parts", + }, }, store: { getters: { pageData: () => basePartResponse, lineItems: { - glassParts: null - } - } + glassParts: null, + }, + }, }, - } + }, }); //Act @@ -286,12 +291,13 @@ describe("vehicle-parts.vue", () => { wrapper.vm.backButtonAction(); //Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, wrapper.vm.$route); - + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith( + navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, + wrapper.vm.$route + ); }); test("ForwardButtonAction triggers a router.navigateWithSaving change if there are child part questions", async () => { - //Arrange store.getters.pageData.mockReturnValueOnce({ partsOrQuestions: [ @@ -300,39 +306,41 @@ describe("vehicle-parts.vue", () => { location: "Windshield", parts: [ { - "partNumber": "FW03861GTYN", - "description": "rain sensor, heated glass, auto dimming mirror, solar, 3rd visor band, condensation sensor", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": false, - "recalibrationType": null, - "childParts": null, - "childPartQuestions": [ + partNumber: "FW03861GTYN", + description: + "rain sensor, heated glass, auto dimming mirror, solar, 3rd visor band, condensation sensor", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + recalibrationType: null, + childParts: null, + childPartQuestions: [ { - "questionSequence": 1, - "questionText": "Does the rubber seal around your windshield have a chrome strip running through it?", - "answers": [ - { - "answerResult": "WKT D1106 C", - "answerText": "Yes", - "nextQuestionSequence": null - }, - { - "answerResult": "WKT D1106 B", - "answerText": "No", - "nextQuestionSequence": null - } - ] - } - ] - } + questionSequence: 1, + questionText: + "Does the rubber seal around your windshield have a chrome strip running through it?", + answers: [ + { + answerResult: "WKT D1106 C", + answerText: "Yes", + nextQuestionSequence: null, + }, + { + answerResult: "WKT D1106 B", + answerText: "No", + nextQuestionSequence: null, + }, + ], + }, + ], + }, ], - partQuestions: null - } - ] + partQuestions: null, + }, + ], }); - store.getters.lineItems = { glassParts: {} } - + store.getters.lineItems = { glassParts: {} }; + const { wrapper } = setupMocks({ mountOptionsMockData: { router: { @@ -342,17 +350,19 @@ describe("vehicle-parts.vue", () => { }, route: { query: { - fmgPage: 'vehicle-parts', - } + fmgPage: "vehicle-parts", + }, }, store: { getters: store.getters, - commit: store.commit + commit: store.commit, }, - } + }, }); - wrapper.setData({ selectedGlassParts: { "Rear-Stationary": { partNumber: 'FW03861GTYN' } } }); + wrapper.setData({ + selectedGlassParts: { "Rear-Stationary": { partNumber: "FW03861GTYN" } }, + }); //Act vehicleParts.beforeRouteEnter.call( @@ -369,7 +379,6 @@ describe("vehicle-parts.vue", () => { }); test("ForwardButtonAction triggers a router.navigateWithSaving change if there are capability questions", async () => { - //Arrange store.getters.pageData.mockReturnValueOnce({ partsOrQuestions: [ @@ -383,16 +392,16 @@ describe("vehicle-parts.vue", () => { color: "Green Tint", requiresRecalibration: false, requiresCapabilityQuestions: true, - childParts: null - } + childParts: null, + }, ], - partQuestions: null - } - ] + partQuestions: null, + }, + ], }); - store.getters.lineItems = { glassParts: {} } - store.getters.vehicle = { carId: "TEST_CAR_ID" } - + store.getters.lineItems = { glassParts: {} }; + store.getters.vehicle = { carId: "TEST_CAR_ID" }; + const { wrapper } = setupMocks({ mountOptionsMockData: { router: { @@ -402,23 +411,25 @@ describe("vehicle-parts.vue", () => { }, route: { query: { - fmgPage: 'vehicle-parts', - } + fmgPage: "vehicle-parts", + }, }, store: { getters: store.getters, - commit: store.commit + commit: store.commit, }, actionList: [ { actionName: storeActions.GET_CAPABILITY_QUESTIONS, - data: [] - } - ] - } + data: [], + }, + ], + }, }); - wrapper.setData({ selectedGlassParts: { "Rear-Stationary": { partNumber: 'DB12209GTYN' } } }); + wrapper.setData({ + selectedGlassParts: { "Rear-Stationary": { partNumber: "DB12209GTYN" } }, + }); //Act vehicleParts.beforeRouteEnter.call( @@ -435,12 +446,11 @@ describe("vehicle-parts.vue", () => { }); test("ForwardButtonAction saves selected parts to store if no molding or capability questions", async () => { - //Arrange store.getters.pageData.mockReturnValueOnce(basePartResponse); - store.getters.lineItems = { glassParts: {} } - store.getters.vehicle = { carId: "TEST_CAR_ID" } - + store.getters.lineItems = { glassParts: {} }; + store.getters.vehicle = { carId: "TEST_CAR_ID" }; + const { wrapper } = setupMocks({ mountOptionsMockData: { router: { @@ -450,21 +460,23 @@ describe("vehicle-parts.vue", () => { }, route: { query: { - fmgPage: 'vehicle-parts', - } + fmgPage: "vehicle-parts", + }, }, store: { getters: store.getters, - commit: store.commit + commit: store.commit, }, - navigateToHeritageFunnel: jest.fn() - } + navigateToHeritageFunnel: jest.fn(), + }, }); wrapper.vm.$store.commit = jest.fn(); wrapper.vm.$refs.loadingModal.showModal = jest.fn(); - wrapper.setData({ selectedGlassParts: { "Rear-Stationary": { partNumber: 'DB12209GTYN' } } }); + wrapper.setData({ + selectedGlassParts: { "Rear-Stationary": { partNumber: "DB12209GTYN" } }, + }); //Act vehicleParts.beforeRouteEnter.call( @@ -495,13 +507,13 @@ function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {} 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', + 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', - } - } + BodyText: "Please choose your tint color", + HeadlineText: "Just a few more steps to go", + }, + }, }; const apiPromise = Promise.resolve(apiResponses); @@ -512,7 +524,7 @@ function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {} const mountOptions = getMountOptions(mountOptionsMockData); const wrapper = shallowMount(vehicleParts, mountOptions); - const partQuestionRearWrapper = wrapper.findComponent({ name: "glassPartQuestion", }); + const partQuestionRearWrapper = wrapper.findComponent({ name: "glassPartQuestion" }); partQuestionRearWrapper.vm.initializeComponent = glassPartQuestion.methods.initializeComponent; wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; diff --git a/src/layouts/vehicle-style/style-question/style-question.spec.js b/src/layouts/vehicle-style/style-question/style-question.spec.js index befbb0b61..c02301316 100644 --- a/src/layouts/vehicle-style/style-question/style-question.spec.js +++ b/src/layouts/vehicle-style/style-question/style-question.spec.js @@ -3,82 +3,77 @@ import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import store from "@/store"; jest.mock( - "@/store", - () => { - return {}; - }, - { virtual: true } + "@/store", + () => { + return {}; + }, + { virtual: true } ); describe("style-question.vue", () => { - test("Selected style is emitted upon selection.", async () => { - //Arrange - const { wrapper } = setupMocks({ modelValueProp: "2 Door" }); - const styleToSelect = "4 Door"; + test("Selected style is emitted upon selection.", async () => { + //Arrange + const { wrapper } = setupMocks({ modelValueProp: "2 Door" }); + const styleToSelect = "4 Door"; - //Act - wrapper.setValue({ selectedStyle: styleToSelect }); - await wrapper.vm.$nextTick(); + //Act + wrapper.setValue({ selectedStyle: styleToSelect }); + await wrapper.vm.$nextTick(); - //Assert - expect(wrapper.emitted()["update:modelValue"][0]).toEqual([ - { selectedStyle: "4 Door" }, - ]); - }); + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ selectedStyle: "4 Door" }]); + }); }); describe("style-question.vue", () => { - test("Data from store api are used as radio question answers.", async () => { - //Arrange - const { wrapper, cmsContent } = setupMocks({ - dataFromStoreApi: ["2 Door", "4 Door"], - }); + test("Data from store api are used as radio question answers.", async () => { + //Arrange + const { wrapper, cmsContent } = setupMocks({ + dataFromStoreApi: ["2 Door", "4 Door"], + }); - //Act - const initialData = styleQuestion.methods.loadInitialData.call(wrapper.vm); - styleQuestion.methods.initializeComponent.call( - wrapper.vm, - initialData - ); + //Act + const initialData = styleQuestion.methods.loadInitialData.call(wrapper.vm); + styleQuestion.methods.initializeComponent.call(wrapper.vm, initialData); - //Assert - const buttonQuestionComponent = await wrapper.findComponent({ - name: "buttonQuestion", + //Assert + const buttonQuestionComponent = await wrapper.findComponent({ + name: "buttonQuestion", + }); + expect(buttonQuestionComponent.attributes("answers")).toBe("2 Door,4 Door"); }); - expect(buttonQuestionComponent.attributes("answers")).toBe("2 Door,4 Door"); - }); }); function setupMocks({ - modelValueProp = "1900", - cmsQuestionText = "CMS text goes here", - dataFromStoreApi = [], + modelValueProp = "1900", + cmsQuestionText = "CMS text goes here", + dataFromStoreApi = [], }) { - //Mock store - store.dispatch = jest.fn(() => dataFromStoreApi); - store.getters = { vehicle: { year: 2019, make: "honda", model: "civc" } }; - const mountOptions = getMountOptions({ - store: { - dispatch: store.dispatch, - getters: store.getters, - }, - }); + //Mock store + store.dispatch = jest.fn(() => dataFromStoreApi); + store.getters = { vehicle: { year: 2019, make: "honda", model: "civc" } }; + const mountOptions = getMountOptions({ + store: { + dispatch: store.dispatch, + getters: store.getters, + }, + }); - //Mock props - const mockMixin = { - methods: { - getCmsContent: jest.fn() - } - } - mountOptions.propsData = { - modelValue: modelValueProp, - }; - mountOptions.mixins = [mockMixin]; - const wrapper = shallowMount(styleQuestion, mountOptions); + //Mock props + const mockMixin = { + methods: { + getCmsContent: jest.fn(), + }, + }; + mountOptions.propsData = { + modelValue: modelValueProp, + }; + mountOptions.mixins = [mockMixin]; + const wrapper = shallowMount(styleQuestion, mountOptions); - //Mock CMS content - const cmsContent = { - QuestionText: cmsQuestionText, - }; - return { wrapper, cmsContent }; + //Mock CMS content + const cmsContent = { + QuestionText: cmsQuestionText, + }; + return { wrapper, cmsContent }; } diff --git a/src/layouts/vehicle-style/style-question/style-question.vue b/src/layouts/vehicle-style/style-question/style-question.vue index 806f7ac41..934360323 100644 --- a/src/layouts/vehicle-style/style-question/style-question.vue +++ b/src/layouts/vehicle-style/style-question/style-question.vue @@ -1,15 +1,14 @@ diff --git a/src/layouts/vehicle-style/vehicle-style.spec.js b/src/layouts/vehicle-style/vehicle-style.spec.js index e97b14882..f7dacb6b4 100644 --- a/src/layouts/vehicle-style/vehicle-style.spec.js +++ b/src/layouts/vehicle-style/vehicle-style.spec.js @@ -10,248 +10,247 @@ import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { storeActions } from "@/constants/store-actions"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import baseMixin from "@/mixins/base-mixin.js"; -import router from "@/router" -import store from "@/store" +import router from "@/router"; +import store from "@/store"; import { storeMutations } from "@/constants/store-mutations"; // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ - settleAllPromises: jest.fn(), + settleAllPromises: jest.fn(), })); // Mock fetchCmsContentForPage jest.mock("@/helpers/cms-content-helper", () => ({ - fetchCmsContentForPage: jest.fn(), + fetchCmsContentForPage: jest.fn(), })); // Mock fetchCmsContentForPage jest.mock("@/router", () => ({ - overrideNavigation: jest.fn(), + overrideNavigation: jest.fn(), })); describe("vehicle-style.vue", () => { - beforeEach(() => { - jest.clearAllMocks(); - }) - - test("Style question component is initized with api data", async (done) => { - //Arrange - const styleQuestionInitialData = ["2 Door", "4 Door"]; - const { wrapper, apiPromise } = setupMocks({ - styleQuestionInitialData: styleQuestionInitialData, + beforeEach(() => { + jest.clearAllMocks(); }); - //Act - vehicleStyle.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-style" } }, - undefined, - (c) => c(wrapper.vm) - ); + test("Style question component is initized with api data", async (done) => { + //Arrange + const styleQuestionInitialData = ["2 Door", "4 Door"]; + const { wrapper, apiPromise } = setupMocks({ + styleQuestionInitialData: styleQuestionInitialData, + }); - //Assert - apiPromise.finally(() => { - expect(styleQuestion.methods.initializeComponent).toHaveBeenCalledWith( - styleQuestionInitialData - ); - done(); + //Act + vehicleStyle.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-style" } }, + undefined, + (c) => c(wrapper.vm) + ); + + //Assert + apiPromise.finally(() => { + expect(styleQuestion.methods.initializeComponent).toHaveBeenCalledWith( + styleQuestionInitialData + ); + done(); + }); }); - }); - test("BackButtonAction triggers a router.navigateWithoutSaving change", async (done) => { - //Arrange - const { wrapper, apiPromise } = setupMocks({ - pageHeaderWidgetHeaderText: "Select a style to get started", - mountOptionsMockData: { - router: { - navigate: jest.fn(), - navigateWithSaving: jest.fn(), - navigateWithoutSaving: jest.fn(), + test("BackButtonAction triggers a router.navigateWithoutSaving change", async (done) => { + //Arrange + const { wrapper, apiPromise } = setupMocks({ + pageHeaderWidgetHeaderText: "Select a style to get started", + mountOptionsMockData: { + router: { + navigate: jest.fn(), + navigateWithSaving: jest.fn(), + navigateWithoutSaving: jest.fn(), + }, + }, + }); + + //Act + vehicleStyle.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-style" } }, + undefined, + (c) => c(wrapper.vm) + ); + wrapper.vm.backButtonAction(); + await nextTick(); + + //Assert + apiPromise.finally(() => { + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); + done(); + }); + }); + + test("setVehicle triggers a dispatchStoreAction commit", async (done) => { + //Arrange + const { wrapper, apiPromise } = setupMocks({ + pageHeaderWidgetHeaderText: "Select a style to get started", + mountOptionsMockData: { + actionList: [ + { + actionName: storeActions.SET_VEHICLE, + data: "mockData", + }, + ], + }, + }); + + //Act + vehicleStyle.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-style" } }, + undefined, + (c) => c(wrapper.vm) + ); + wrapper.vm.setVehicle(); + await nextTick(); + + //Assert + apiPromise.finally(() => { + expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalled(); + done(); + }); + }); + + test("Model set, arePagePrerequisitesValid should be true ", async () => { + //Arrange + const { wrapper } = setupMocks({}); + + //Act + vehicleStyle.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-style" } }, + undefined, + (c) => c(wrapper.vm) + ); + + let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + await nextTick(); + + //Assert + expect(arePagePrerequisitesValid).toBe(true); + }); + + test("there is only one vehicle style => autoselect and move to vehicle damage", async () => { + //Arrange + const { wrapper } = setupMocks({ + styleQuestionInitialData: ["2 door sedan"], + }); + + // Act + await vehicleStyle.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-style" } }, + undefined, + (c) => c(wrapper.vm) + ); + + // Assert + expect(store.commit).toHaveBeenCalledWith(storeMutations.UPDATE_STYLE, "2 door sedan"); + expect(router.overrideNavigation).toHaveBeenCalled(); + }); + + test("there is only one vehicle style and vehicle-damage was visited => don't autoselect or move to vehicle damage", async () => { + //Arrange + const { wrapper } = setupMocks({ + styleQuestionInitialData: ["2 door sedan"], + mountOptionsMockData: { + store: { + getters: { + applicationUser: { + pageData: { + "part-questions": null, + "vehicle-make": {}, + "vehicle-model": {}, + "vehicle-style": {}, + "vehicle-damage": {}, + }, + }, + }, + }, + }, + }); + + // Act + await vehicleStyle.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-style" } }, + undefined, + (c) => c(wrapper.vm) + ); + + // Assert + expect(store.commit).not.toHaveBeenCalledWith(storeMutations.UPDATE_STYLE, "2 door sedan"); + expect(router.overrideNavigation).not.toHaveBeenCalled(); + }); +}); + +function setupMocks({ + vehicleStyleQuestionCmsContent = {}, + styleQuestionInitialData = {}, + pageHeaderWidgetHeaderText = {}, + mountOptionsMockData = {}, +}) { + //Mock api responses + const apiResponses = { + cmsContent: { + FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, + VehicleStyleQuestion: vehicleStyleQuestionCmsContent, + 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", + }, }, - }, - }); + styleQuestionInitialData: styleQuestionInitialData, + }; - //Act - vehicleStyle.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-style" } }, - undefined, - (c) => c(wrapper.vm) - ); - wrapper.vm.backButtonAction(); - await nextTick(); + const apiPromise = Promise.resolve(apiResponses); - //Assert - apiPromise.finally(() => { - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); - done(); - }); - }); + settleAllPromises.mockImplementation(() => apiPromise); + fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); - test("setVehicle triggers a dispatchStoreAction commit", async (done) => { - //Arrange - const { wrapper, apiPromise } = setupMocks({ - pageHeaderWidgetHeaderText: "Select a style to get started", - mountOptionsMockData: { - actionList: [ - { - actionName: storeActions.SET_VEHICLE, - data: "mockData", - }, - ], - }, - }); + //Mock style question methods + styleQuestion.methods = { + loadInitialData: jest.fn(), + initializeComponent: jest.fn(), + }; - //Act - vehicleStyle.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-style" } }, - undefined, - (c) => c(wrapper.vm) - ); - wrapper.vm.setVehicle(); - await nextTick(); - - //Assert - apiPromise.finally(() => { - expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalled(); - done(); - }); - }); - - test("Model set, arePagePrerequisitesValid should be true ", async () => { - //Arrange - const { wrapper } = setupMocks({}); - - //Act - vehicleStyle.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-style" } }, - undefined, - (c) => c(wrapper.vm) - ); - - let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - await nextTick(); - - //Assert - expect(arePagePrerequisitesValid).toBe(true); - }); - - test("there is only one vehicle style => autoselect and move to vehicle damage", async () => { - //Arrange - const { wrapper } = setupMocks({ - styleQuestionInitialData: ["2 door sedan"], - }); - - // Act - await vehicleStyle.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-style" } }, - undefined, - (c) => c(wrapper.vm) - ); - - // Assert - expect(store.commit).toHaveBeenCalledWith(storeMutations.UPDATE_STYLE, "2 door sedan"); - expect(router.overrideNavigation).toHaveBeenCalled(); - }) - - test("there is only one vehicle style and vehicle-damage was visited => don't autoselect or move to vehicle damage", async () => { - //Arrange - const { wrapper } = setupMocks({ - styleQuestionInitialData: ["2 door sedan"], - mountOptionsMockData: { - store: { - getters: { - applicationUser: { - pageData: { + store.commit = jest.fn(); + store.dispatch = jest.fn(); + store.getters = mountOptionsMockData.store?.getters ?? { + vehicle: { + model: "TL", + }, + applicationUser: { + pageData: { "part-questions": null, "vehicle-make": {}, "vehicle-model": {}, "vehicle-style": {}, - "vehicle-damage": {} - } - } - } - } - } + }, + }, + }; + + const mountOptions = getMountOptions({ + ...mountOptionsMockData, + store, }); + const wrapper = shallowMount(vehicleStyle, mountOptions); + wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; - // Act - await vehicleStyle.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-style" } }, - undefined, - (c) => c(wrapper.vm) - ); + const styleQuestionWrapper = wrapper.findComponent({ name: "styleQuestion" }); + styleQuestionWrapper.vm.initializeComponent = styleQuestion.methods.initializeComponent; - // Assert - expect(store.commit).not.toHaveBeenCalledWith(storeMutations.UPDATE_STYLE, "2 door sedan"); - expect(router.overrideNavigation).not.toHaveBeenCalled(); - }) -}); - -function setupMocks({ - vehicleStyleQuestionCmsContent = {}, - styleQuestionInitialData = {}, - pageHeaderWidgetHeaderText = {}, - mountOptionsMockData = {}, -}) { - //Mock api responses - const apiResponses = { - cmsContent: { - FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, - VehicleStyleQuestion: vehicleStyleQuestionCmsContent, - 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", - }, - }, - styleQuestionInitialData: styleQuestionInitialData, - }; - - const apiPromise = Promise.resolve(apiResponses); - - settleAllPromises.mockImplementation(() => apiPromise); - fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); - - //Mock style question methods - styleQuestion.methods = { - loadInitialData: jest.fn(), - initializeComponent: jest.fn(), - }; - - store.commit = jest.fn(); - store.dispatch = jest.fn(); - store.getters = mountOptionsMockData.store?.getters ?? { - vehicle: { - model: "TL", - }, - applicationUser: { - pageData: { - "part-questions": null, - "vehicle-make": {}, - "vehicle-model": {}, - "vehicle-style": {}, - } - } - } - - const mountOptions = getMountOptions({ - ...mountOptionsMockData, - store - }); - const wrapper = shallowMount(vehicleStyle, mountOptions); - wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; - - const styleQuestionWrapper = wrapper.findComponent({ name: "styleQuestion" }); - styleQuestionWrapper.vm.initializeComponent = - styleQuestion.methods.initializeComponent; - - return { wrapper, apiPromise }; + return { wrapper, apiPromise }; } diff --git a/src/layouts/vehicle-style/vehicle-style.vue b/src/layouts/vehicle-style/vehicle-style.vue index 4a31c06cc..7b9949234 100644 --- a/src/layouts/vehicle-style/vehicle-style.vue +++ b/src/layouts/vehicle-style/vehicle-style.vue @@ -1,27 +1,22 @@ diff --git a/src/layouts/vehicle-year/vehicle-year.spec.js b/src/layouts/vehicle-year/vehicle-year.spec.js index 3e07acd32..ad024f131 100644 --- a/src/layouts/vehicle-year/vehicle-year.spec.js +++ b/src/layouts/vehicle-year/vehicle-year.spec.js @@ -13,111 +13,110 @@ import yearQuestion from "@/layouts/vehicle-year/year-question/year-question"; import store from "@/store"; jest.mock("@/store", () => ({ - commit: jest.fn(), - dispatch: jest.fn(), - getters: { - applicationUser: { - experiments: [] - } - } + commit: jest.fn(), + dispatch: jest.fn(), + getters: { + applicationUser: { + experiments: [], + }, + }, })); // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ - settleAllPromises: jest.fn(), + settleAllPromises: jest.fn(), })); // Mock fetchCmsContentForPage jest.mock("@/helpers/cms-content-helper", () => ({ - fetchCmsContentForPage: jest.fn(), + fetchCmsContentForPage: jest.fn(), })); describe("vehicle-year.vue", () => { - test("Year question component is initized with api data", async (done) => { - //Arrange - const yearQuestionInitialData = ["2023", "2022", "2021"]; - const { wrapper, apiPromise } = setupMocks({ - yearQuestionInitialData: yearQuestionInitialData, - }); + test("Year question component is initized with api data", async (done) => { + //Arrange + const yearQuestionInitialData = ["2023", "2022", "2021"]; + const { wrapper, apiPromise } = setupMocks({ + yearQuestionInitialData: yearQuestionInitialData, + }); - //Act - vehicleYear.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-year" } }, - undefined, - (c) => c(wrapper.vm) - ); + //Act + vehicleYear.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-year" } }, + undefined, + (c) => c(wrapper.vm) + ); - //Assert - apiPromise.finally(() => { - expect(yearQuestion.methods.initializeComponent).toHaveBeenCalledWith( - yearQuestionInitialData - ); - done(); + //Assert + apiPromise.finally(() => { + expect(yearQuestion.methods.initializeComponent).toHaveBeenCalledWith( + yearQuestionInitialData + ); + done(); + }); }); - }); }); describe("vehicle-year.vue", () => { - test("arePagePrerequisitesValid should be true ", async () => { - //Arrange - const { wrapper } = setupMocks({}); + test("arePagePrerequisitesValid should be true ", async () => { + //Arrange + const { wrapper } = setupMocks({}); - //Act - vehicleYear.beforeRouteEnter.call( - wrapper.vm, - { query: { fmgPage: "vehicle-make" } }, - undefined, - (c) => c(wrapper.vm) - ); + //Act + vehicleYear.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "vehicle-make" } }, + undefined, + (c) => c(wrapper.vm) + ); - let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - await nextTick(); + let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + await nextTick(); - //Assert - expect(arePagePrerequisitesValid).toBe(true); - }); + //Assert + expect(arePagePrerequisitesValid).toBe(true); + }); }); function setupMocks({ - vehicleYearQuestionCmsContent = {}, - yearQuestionInitialData = {}, - pageHeaderWidgetHeaderText = {}, - mountOptionsMockData = {}, + vehicleYearQuestionCmsContent = {}, + yearQuestionInitialData = {}, + pageHeaderWidgetHeaderText = {}, + mountOptionsMockData = {}, }) { - //Mock api responses - const apiResponses = { - cmsContent: { - FunnelSubHeaderWidget: { HeaderText: pageHeaderWidgetHeaderText }, - VehicleYearQuestion: vehicleYearQuestionCmsContent, - 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", - }, - }, - yearQuestionInitialData: yearQuestionInitialData, - }; - const apiPromise = Promise.resolve(apiResponses); + //Mock api responses + const apiResponses = { + cmsContent: { + FunnelSubHeaderWidget: { HeaderText: pageHeaderWidgetHeaderText }, + VehicleYearQuestion: vehicleYearQuestionCmsContent, + 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", + }, + }, + yearQuestionInitialData: yearQuestionInitialData, + }; + const apiPromise = Promise.resolve(apiResponses); - settleAllPromises.mockImplementation(() => apiPromise); - fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); + settleAllPromises.mockImplementation(() => apiPromise); + fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); - //Mock year question methods - yearQuestion.methods = { - loadInitialData: jest.fn(), - initializeComponent: jest.fn(), - }; + //Mock year question methods + yearQuestion.methods = { + loadInitialData: jest.fn(), + initializeComponent: jest.fn(), + }; - const mountOptions = getMountOptions(mountOptionsMockData); - const wrapper = shallowMount(vehicleYear, mountOptions); - const yearQuestionWrapper = wrapper.findComponent({ name: "yearQuestion" }); - yearQuestionWrapper.vm.initializeComponent = - yearQuestion.methods.initializeComponent; - wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; + const mountOptions = getMountOptions(mountOptionsMockData); + const wrapper = shallowMount(vehicleYear, mountOptions); + const yearQuestionWrapper = wrapper.findComponent({ name: "yearQuestion" }); + yearQuestionWrapper.vm.initializeComponent = yearQuestion.methods.initializeComponent; + wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; - return { wrapper, apiPromise }; + return { wrapper, apiPromise }; } diff --git a/src/layouts/vehicle-year/vehicle-year.vue b/src/layouts/vehicle-year/vehicle-year.vue index fe9f6f30c..256a38d64 100644 --- a/src/layouts/vehicle-year/vehicle-year.vue +++ b/src/layouts/vehicle-year/vehicle-year.vue @@ -1,21 +1,20 @@ diff --git a/src/layouts/vehicle-year/year-question/year-question.spec.js b/src/layouts/vehicle-year/year-question/year-question.spec.js index cca9c8a12..f6a1e5e71 100644 --- a/src/layouts/vehicle-year/year-question/year-question.spec.js +++ b/src/layouts/vehicle-year/year-question/year-question.spec.js @@ -3,84 +3,76 @@ import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import store from "@/store"; jest.mock( - "@/store", - () => { - return {}; - }, - { virtual: true } + "@/store", + () => { + return {}; + }, + { virtual: true } ); describe("year-question.vue", () => { - test("Selected year is emitted upon selection.", async () => { - //Arrange - const { wrapper } = setupMocks({ modelValueProp: "2020" }); - const yearToSelect = "2021"; + test("Selected year is emitted upon selection.", async () => { + //Arrange + const { wrapper } = setupMocks({ modelValueProp: "2020" }); + const yearToSelect = "2021"; - //Act - wrapper.setValue({ modelValue: yearToSelect }); - await wrapper.vm.$nextTick(); + //Act + wrapper.setValue({ modelValue: yearToSelect }); + await wrapper.vm.$nextTick(); - //Assert - expect(wrapper.emitted()["update:modelValue"][0]).toEqual([ - { modelValue: "2021" }, - ]); - }); + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: "2021" }]); + }); }); describe("year-question.vue", () => { - test("Data from store api are used as radio question answers.", async () => { - //Arrange - const { wrapper, cmsContent } = setupMocks({ - dataFromStoreApi: ["2023", "2022", "2021"], - }); + test("Data from store api are used as radio question answers.", async () => { + //Arrange + const { wrapper, cmsContent } = setupMocks({ + dataFromStoreApi: ["2023", "2022", "2021"], + }); - //Act - const initialData = yearQuestion.methods.loadInitialData.call(wrapper.vm); - yearQuestion.methods.initializeComponent.call( - wrapper.vm, - initialData - ); + //Act + const initialData = yearQuestion.methods.loadInitialData.call(wrapper.vm); + yearQuestion.methods.initializeComponent.call(wrapper.vm, initialData); - //Assert - const buttonQuestionComponent = await wrapper.findComponent({ - name: "buttonQuestion", + //Assert + const buttonQuestionComponent = await wrapper.findComponent({ + name: "buttonQuestion", + }); + expect(buttonQuestionComponent.attributes("answers")).toBe("2023,2022,2021"); }); - expect(buttonQuestionComponent.attributes("answers")).toBe( - "2023,2022,2021" - ); - }); }); - function setupMocks({ - modelValueProp = "1900", - cmsQuestionText = "CMS text goes here", - dataFromStoreApi = [], + modelValueProp = "1900", + cmsQuestionText = "CMS text goes here", + dataFromStoreApi = [], }) { - //Mock store - store.dispatch = jest.fn(() => dataFromStoreApi); - const mountOptions = getMountOptions({ - store: { - dispatch: store.dispatch, - }, - }); + //Mock store + store.dispatch = jest.fn(() => dataFromStoreApi); + const mountOptions = getMountOptions({ + store: { + dispatch: store.dispatch, + }, + }); - //Mock props - const mockMixin = { - methods: { - getCmsContent: jest.fn() - } - } - mountOptions.propsData = { - modelValue: modelValueProp, - }; - mountOptions.mixins = [mockMixin]; - - const wrapper = shallowMount(yearQuestion, mountOptions); + //Mock props + const mockMixin = { + methods: { + getCmsContent: jest.fn(), + }, + }; + mountOptions.propsData = { + modelValue: modelValueProp, + }; + mountOptions.mixins = [mockMixin]; - //Mock CMS content - const cmsContent = { - QuestionText: cmsQuestionText, - }; - return { wrapper, cmsContent }; + const wrapper = shallowMount(yearQuestion, mountOptions); + + //Mock CMS content + const cmsContent = { + QuestionText: cmsQuestionText, + }; + return { wrapper, cmsContent }; } diff --git a/src/layouts/vehicle-year/year-question/year-question.vue b/src/layouts/vehicle-year/year-question/year-question.vue index 6851f282c..e4a36b401 100644 --- a/src/layouts/vehicle-year/year-question/year-question.vue +++ b/src/layouts/vehicle-year/year-question/year-question.vue @@ -1,15 +1,14 @@ diff --git a/src/layouts/vin-lookup/vin-information/vin-information.spec.js b/src/layouts/vin-lookup/vin-information/vin-information.spec.js index 7ffdeedf8..70341ba5d 100644 --- a/src/layouts/vin-lookup/vin-information/vin-information.spec.js +++ b/src/layouts/vin-lookup/vin-information/vin-information.spec.js @@ -2,25 +2,25 @@ import { shallowMount } from "@vue/test-utils"; import vinInformation from "@/layouts/vin-lookup/vin-information/vin-information"; describe("vinInformation.vue", () => { - it("Should return input class active if isActive is true", async () => { - // Act - const wrapper = shallowMount(vinInformation, { - mixins: [mockMixin] + it("Should return input class active if isActive is true", async () => { + // Act + const wrapper = shallowMount(vinInformation, { + mixins: [mockMixin], + }); + + await wrapper.setData({ + isActive: true, + }); + + // Assert + wrapper.vm.toggleClass(); + + expect(wrapper.vm.isActive).toEqual(false); }); - - await wrapper.setData({ - isActive: true, - }); - - // Assert - wrapper.vm.toggleClass() - - expect(wrapper.vm.isActive).toEqual(false); - }); }); const mockMixin = { - methods: { - getCmsContent: jest.fn() - } -} + methods: { + getCmsContent: jest.fn(), + }, +}; diff --git a/src/layouts/vin-lookup/vin-information/vin-information.vue b/src/layouts/vin-lookup/vin-information/vin-information.vue index ec973b892..625442e82 100644 --- a/src/layouts/vin-lookup/vin-information/vin-information.vue +++ b/src/layouts/vin-lookup/vin-information/vin-information.vue @@ -1,101 +1,99 @@ diff --git a/src/layouts/vin-lookup/vin-lookup.spec.js b/src/layouts/vin-lookup/vin-lookup.spec.js index 246b442b0..096728af8 100644 --- a/src/layouts/vin-lookup/vin-lookup.spec.js +++ b/src/layouts/vin-lookup/vin-lookup.spec.js @@ -12,24 +12,24 @@ jest.mock("@/store", () => ({ getters: { vehicle: { year: 2019, - carId: 'C00000' + carId: "C00000", }, order: { serviceLocation: { - zipCode: "45253" + zipCode: "45253", }, customer: { - emailAddress: "builddigitaltest@safelite.com" - } + emailAddress: "builddigitaltest@safelite.com", + }, }, payment: { insuranceCoverage: { - isVerified: true - } + isVerified: true, + }, }, damage: { - glassToReplace: "windshield" - } + glassToReplace: "windshield", + }, }, })); @@ -38,7 +38,6 @@ jest.mock("@/helpers/layout-helper.js", () => ({ settleAllPromises: jest.fn(), })); - jest.mock("@/helpers/damage-helper", () => ({ isGlassAvailableForCarId: jest.fn(() => { return Promise.resolve(); @@ -47,7 +46,6 @@ jest.mock("@/helpers/damage-helper", () => ({ getDamageString: jest.fn(), })); - describe("vin-lookup.vue", () => { it("Should update the funnel-footer forward button when VIN is changed", (done) => { //Arrange @@ -59,7 +57,6 @@ describe("vin-lookup.vue", () => { expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toBeCalled(); done(); }); - }); it("Should call navigateForward() if the store carId matches the vin response carId and forward button is clicked", async () => { @@ -68,7 +65,7 @@ describe("vin-lookup.vue", () => { mockOutPromises({ carId: "C00000" }); wrapper.vm.navigateForward = jest.fn(); - // Act + // Act await wrapper.vm.forwardButtonAction(); //Assert @@ -78,14 +75,14 @@ describe("vin-lookup.vue", () => { it("Should not call navigateForward() if the store carId does not match the vin response carId and forward button is clicked", async () => { // Arrange const { wrapper } = setupMocks({}); - mockOutPromises({ carId: 'C11111' }); + mockOutPromises({ carId: "C11111" }); wrapper.vm.vinTouched = true; wrapper.vm.vin = ""; wrapper.vm.initialVin = "foo"; wrapper.vm.navigateForward = jest.fn(); - // Act + // Act await wrapper.vm.forwardButtonAction(); //Assert @@ -95,13 +92,13 @@ describe("vin-lookup.vue", () => { it("Should call navigateForward() if the store carId does not match the vin response carId but does match previously enterted carId and forward button is clicked", async () => { // Arrange const { wrapper } = setupMocks({}); - mockOutPromises({ carId: 'C11111' }); + mockOutPromises({ carId: "C11111" }); wrapper.vm.lookupVehicle = jest.fn().mockImplementation(() => vinPromise); wrapper.vm.navigateForward = jest.fn(); - wrapper.vm.previouslyEnteredCarId = 'C11111'; + wrapper.vm.previouslyEnteredCarId = "C11111"; - // Act + // Act await wrapper.vm.forwardButtonAction(); //Assert @@ -113,8 +110,8 @@ describe("vin-lookup.vue", () => { const { wrapper } = setupMocks({}); const zipValidationApiResponse = { data: { - isServiceable: false - } + isServiceable: false, + }, }; const zipPromise = Promise.resolve(zipValidationApiResponse); @@ -123,9 +120,9 @@ describe("vin-lookup.vue", () => { wrapper.vm.setupUiForNonServiceableZip = jest.fn(); wrapper.vm.navigateForward = jest.fn(); - wrapper.vm.previouslyEnteredCarId = 'new carId'; + wrapper.vm.previouslyEnteredCarId = "new carId"; - // Act + // Act await wrapper.vm.forwardButtonAction(); //Assert @@ -140,9 +137,9 @@ describe("vin-lookup.vue", () => { wrapper.vm.initialVin = "!foo"; wrapper.vm.navigateForward = jest.fn(); - wrapper.vm.previouslyEnteredCarId = 'new carId'; + wrapper.vm.previouslyEnteredCarId = "new carId"; - // Act + // Act await wrapper.vm.forwardButtonAction(); //Assert @@ -155,14 +152,14 @@ describe("vin-lookup.vue", () => { const { wrapper } = setupMocks({ customMountOptions: { router: { - navigateWithSaving: jest.fn() - } - } + navigateWithSaving: jest.fn(), + }, + }, }); wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn(); wrapper.setData({ isCarIdDifferent: true, - isSelectedGlassAvailableForVehicle: false + isSelectedGlassAvailableForVehicle: false, }); // Act @@ -170,8 +167,13 @@ describe("vin-lookup.vue", () => { //Assert expect(wrapper.vm.$router.navigateWithSaving).toBeCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, wrapper.vm.$route, expect.anything(), expect.anything()); - }) + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, + wrapper.vm.$route, + expect.anything(), + expect.anything() + ); + }); test("carId matches => navigateForwardWithSingleCarMatch", async () => { // Arrange @@ -186,7 +188,7 @@ describe("vin-lookup.vue", () => { //Assert expect(wrapper.vm.navigateForwardWithSingleCarMatch).toBeCalledTimes(1); - }) + }); test("selected glass is available for returned vehicle => navigateForwardWithSingleCarMatch", async () => { // Arrange @@ -201,50 +203,51 @@ describe("vin-lookup.vue", () => { //Assert expect(wrapper.vm.navigateForwardWithSingleCarMatch).toBeCalledTimes(1); - }) - }) + }); + }); describe("alerts", () => { test("Zip is invalid => show AlertInvalidZipWidget", async () => { // Arrange const { wrapper } = setupMocks({}); mockOutPromises({ isZipValid: false }); - await wrapper.setData({serviceZipCode: "11111"}) - + await wrapper.setData({ serviceZipCode: "11111" }); + // Act await wrapper.vm.forwardButtonAction(); // Assert expect(wrapper.vm.displayInvalidZipAlert).toEqual(true); - expect(wrapper.findComponent({ref: "alertInvalidZip"}).exists()).toBe(true); - }) - }) + expect(wrapper.findComponent({ ref: "alertInvalidZip" }).exists()).toBe(true); + }); + }); }); - function setupMocks({ customMountOptions }) { const mountOptions = getMountOptions({ - ...customMountOptions + ...customMountOptions, }); // Modify/augment default mount options mountOptions.global.mocks["$store"] = store; mountOptions.global.mixins = [mockMixin]; - mountOptions['attachTo'] = document.body; // append wrapper to document.body to test DOM methods + mountOptions["attachTo"] = document.body; // append wrapper to document.body to test DOM methods const wrapper = shallowMount(vinLookup, mountOptions); mockOutStubFunctions(wrapper); return { wrapper }; } -function mockOutPromises({carId, isZipValid = true, isZipServiceable = true}) { +function mockOutPromises({ carId, isZipValid = true, isZipServiceable = true }) { const apiResponses = { vehicleLookupResponse: { - carId: carId + carId: carId, }, zipCodeData: { - isValid: true, isServiceable: true, state: "OH" - } + isValid: true, + isServiceable: true, + state: "OH", + }, }; settleAllPromises.mockImplementation(() => apiResponses); @@ -253,11 +256,13 @@ function mockOutPromises({carId, isZipValid = true, isZipServiceable = true}) { function mockOutStubFunctions(wrapper) { wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); - wrapper.vm.getZipCodeData = jest.fn().mockReturnValue({ isValid: true, isServiceable: true, state: "OH" }); + wrapper.vm.getZipCodeData = jest + .fn() + .mockReturnValue({ isValid: true, isServiceable: true, state: "OH" }); } const mockMixin = { methods: { getCmsContent: jest.fn(() => "placeholder CMS content"), - } -} \ No newline at end of file + }, +}; diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index 3614c74cf..f4543f7c2 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -1,125 +1,110 @@ diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index e773c6a01..a4eb993de 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -1,186 +1,212 @@ import { storeActions } from "@/constants/store-actions"; -import { setCookieProperties, getDeviceIdValue, getSessionIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper"; +import { + setCookieProperties, + getDeviceIdValue, + getSessionIdValue, + getSessionKeyValue, +} from "@/helpers/heritage-integration/cookie-helper"; import { queryStrings } from "@/constants/query-strings"; import { experimentSettings } from "@/constants/experiments"; -import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes } from "@/constants/analytics"; +import { + analyticsPageEvents, + GaCategories, + GaActions, + GaLabels, + GaEvents, + ValueToLogTypes, +} from "@/constants/analytics"; import { cookieNames } from "@/constants/cookie-names"; import store from "@/store"; import baseMixin from "@/mixins/base-mixin"; export default { - methods: { - logPageView(pageEvent) { - const currentPageName = getPageNameByQueryString(); - var payload = { - userId: getDeviceIdValue(), - sessionKey: getSessionKeyValue(), - pageName: currentPageName, - sessionId: getSessionIdValue(), - action: '', - event: pageEvent, - shouldUseSessionId: false, - experimentsForUser: store.getters.applicationUser.experiments, - }; + methods: { + logPageView(pageEvent) { + const currentPageName = getPageNameByQueryString(); + var payload = { + userId: getDeviceIdValue(), + sessionKey: getSessionKeyValue(), + pageName: currentPageName, + sessionId: getSessionIdValue(), + action: "", + event: pageEvent, + shouldUseSessionId: false, + experimentsForUser: store.getters.applicationUser.experiments, + }; - baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false); + baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false); + }, + + logCustomEvent(category, action, label, value) { + const currentPageName = getPageNameByQueryString(); + + var payload = { + userId: getDeviceIdValue(), + sessionKey: getSessionKeyValue(), + pageName: currentPageName, + sessionId: getSessionIdValue(), + category: category, + action: action, + label: label, + value: value, + shouldUseSessionId: false, + experimentsForUser: store.getters.applicationUser.experiments, + }; + + baseMixin.methods.dispatchStoreAction(storeActions.LOG_CUSTOM_EVENT, payload, false); + }, + + pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null) { + const currentPageName = getPageNameByQueryString(); + const labelToLog = getValueToLog(label, valueToLogType); + + const eventToBePushed = { + event: GaEvents.GENERIC_EVENT, + category: category, + action: action, + label: labelToLog, + value: undefined, + path: `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}`, + }; + + pushToDataLayerIfDefined(eventToBePushed); + + if (pushToLogApp) { + this.logCustomEvent(category, action, labelToLog, undefined); + } + }, + + pushPageViewToGA() { + const currentPageName = getPageNameByQueryString(); + const pageViewEvent = { + event: GaEvents.PAGE_VIEW_EVENT, + pagePath: `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}`, + pageTitle: currentPageName, + }; + + pushToDataLayerIfDefined(pageViewEvent); + + this.logPageView(analyticsPageEvents.ENTRY); + }, + + pushExperimentsToDataLayer() { + const experiments = store.getters.applicationUser.experiments; + experiments?.forEach((exp) => { + // Set Google Dimension Index based on experiment settings. + let googleDimensionIndex = 99; + + if (exp.settings[experimentSettings.GOOGLE_CUSTOM_DIMENSION_INDEX] !== undefined) { + googleDimensionIndex = + exp.settings[experimentSettings.GOOGLE_CUSTOM_DIMENSION_INDEX]; + } + + // Create object with dimension index and value. + const experimentWithDimension = { + [`experimentId_${googleDimensionIndex}`]: exp.universeId, + [`variationId_${googleDimensionIndex}`]: exp.variationId, + [`experimentName_${googleDimensionIndex}`]: exp.universeName, + [`variationName_${googleDimensionIndex}`]: exp.variationName, + [`customDimension_${googleDimensionIndex}`]: `${exp.universeId}_${exp.variationId}_${exp.universeName}_${exp.variationName}`, + }; + + // Push to the data layer with the Google Custom Dimension Index. + pushToDataLayerIfDefined(experimentWithDimension); + }); + }, + + prependActionToMethod(object, method, actionToPrepend) { + const baseMethodName = method.name.startsWith("bound ") + ? method.name.substring(6) + : method.name; + const baseMethod = object[baseMethodName]; + object[baseMethodName] = function () { + actionToPrepend.apply(this, arguments); + return baseMethod.apply(object, arguments); + }; + }, + + async initSession() { + const sid = getSessionIdValue(); + const skey = getSessionKeyValue(); + var payload = { + userId: getDeviceIdValue(), + sessionId: sid, + userAgent: navigator.userAgent, + referrer: document.referrer, + }; + + const response = await baseMixin.methods.dispatchStoreAction( + storeActions.INITIALIZE_SESSION, + payload, + false + ); + + if (response.data) { + if (response.data.sessionKey && skey === 0) { + setCookieProperties( + { [cookieNames.SESSION_KEY]: response.data.sessionKey }, + { + useDefaultFunnelCookieAttributes: false, + } + ); + } + if (response.data.sessionId && sid === "00000000-0000-0000-0000-000000000000") { + setCookieProperties( + { [cookieNames.SESSION_ID]: response.data.sessionId }, + { + maxAge: 60 * 30, // 30 minutes + } + ); + } + } + }, + + noSession() { + return ( + getSessionKeyValue() === 0 || + getSessionIdValue() === "00000000-0000-0000-0000-000000000000" + ); + }, }, - - logCustomEvent(category, action, label, value) { - const currentPageName = getPageNameByQueryString(); - - var payload = { - userId: getDeviceIdValue(), - sessionKey: getSessionKeyValue(), - pageName: currentPageName, - sessionId: getSessionIdValue(), - category: category, - action: action, - label: label, - value: value, - shouldUseSessionId: false, - experimentsForUser: store.getters.applicationUser.experiments - }; - - baseMixin.methods.dispatchStoreAction(storeActions.LOG_CUSTOM_EVENT, payload, false); + computed: { + analyticsPageEvents() { + return analyticsPageEvents; + }, + GaCategories() { + return GaCategories; + }, + GaActions() { + return GaActions; + }, + GaLabels() { + return GaLabels; + }, + ValueToLogTypes() { + return ValueToLogTypes; + }, }, - - pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null) { - const currentPageName = getPageNameByQueryString(); - const labelToLog = getValueToLog(label, valueToLogType); - - const eventToBePushed = { - 'event': GaEvents.GENERIC_EVENT, - 'category': category, - 'action': action, - 'label': labelToLog, - 'value': undefined, - 'path': `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}` - } - - pushToDataLayerIfDefined(eventToBePushed); - - if (pushToLogApp) { - this.logCustomEvent(category, action, labelToLog, undefined); - } - - }, - - pushPageViewToGA() { - const currentPageName = getPageNameByQueryString(); - const pageViewEvent = { - 'event': GaEvents.PAGE_VIEW_EVENT, - 'pagePath': `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}`, - 'pageTitle': currentPageName - }; - - pushToDataLayerIfDefined(pageViewEvent); - - this.logPageView(analyticsPageEvents.ENTRY); - }, - - pushExperimentsToDataLayer() { - const experiments = store.getters.applicationUser.experiments; - experiments?.forEach(exp => { - - // Set Google Dimension Index based on experiment settings. - let googleDimensionIndex = 99; - - if (exp.settings[experimentSettings.GOOGLE_CUSTOM_DIMENSION_INDEX] !== undefined) { - googleDimensionIndex = exp.settings[experimentSettings.GOOGLE_CUSTOM_DIMENSION_INDEX]; - } - - // Create object with dimension index and value. - const experimentWithDimension = { - [`experimentId_${googleDimensionIndex}`]: exp.universeId, - [`variationId_${googleDimensionIndex}`]: exp.variationId, - [`experimentName_${googleDimensionIndex}`]: exp.universeName, - [`variationName_${googleDimensionIndex}`]: exp.variationName, - [`customDimension_${googleDimensionIndex}`]: `${exp.universeId}_${exp.variationId}_${exp.universeName}_${exp.variationName}` - }; - - // Push to the data layer with the Google Custom Dimension Index. - pushToDataLayerIfDefined(experimentWithDimension); - }); - }, - - prependActionToMethod(object, method, actionToPrepend) { - const baseMethodName = method.name.startsWith('bound ') ? method.name.substring(6) : method.name ; - const baseMethod = object[baseMethodName]; - object[baseMethodName] = function () { - actionToPrepend.apply(this, arguments); - return baseMethod.apply(object, arguments); - }; - }, - - async initSession() { - const sid = getSessionIdValue(); - const skey = getSessionKeyValue(); - var payload = { - userId: getDeviceIdValue(), - sessionId: sid, - userAgent: navigator.userAgent, - referrer: document.referrer, - }; - - const response = await baseMixin.methods.dispatchStoreAction(storeActions.INITIALIZE_SESSION, payload, false); - - if (response.data) { - if (response.data.sessionKey && skey === 0) { - setCookieProperties({ [cookieNames.SESSION_KEY]: response.data.sessionKey}, { - useDefaultFunnelCookieAttributes: false - }); - } - if (response.data.sessionId && sid === '00000000-0000-0000-0000-000000000000') { - setCookieProperties({ [cookieNames.SESSION_ID]: response.data.sessionId}, { - maxAge: 60 * 30 // 30 minutes - }); - } - } - }, - - noSession() { - return getSessionKeyValue() === 0 || getSessionIdValue() === '00000000-0000-0000-0000-000000000000'; - }, - }, - computed: { - analyticsPageEvents() { - return analyticsPageEvents; - }, - GaCategories() { - return GaCategories; - }, - GaActions() { - return GaActions; - }, - GaLabels() { - return GaLabels; - }, - ValueToLogTypes() { - return ValueToLogTypes; - } - }, }; function pushToDataLayerIfDefined(data) { - if (window.dataLayer !== undefined) { - window.dataLayer.push(data); - } + if (window.dataLayer !== undefined) { + window.dataLayer.push(data); + } } function getPageNameByQueryString() { - const params = new URLSearchParams(location.search); + const params = new URLSearchParams(location.search); - if (params.has(queryStrings.FMG_PAGE)) { - return params.get(queryStrings.FMG_PAGE); - } else { - return ''; - } + if (params.has(queryStrings.FMG_PAGE)) { + return params.get(queryStrings.FMG_PAGE); + } else { + return ""; + } } function getValueToLog(value, valueToLogType) { - if (valueToLogType != null && valueToLogType === ValueToLogTypes.LAST_5 ) { - return value.slice(-5); - } - return value; -} \ No newline at end of file + if (valueToLogType != null && valueToLogType === ValueToLogTypes.LAST_5) { + return value.slice(-5); + } + return value; +} diff --git a/src/mixins/analytics-mixin.spec.js b/src/mixins/analytics-mixin.spec.js index bedcf8e2e..0426ca297 100644 --- a/src/mixins/analytics-mixin.spec.js +++ b/src/mixins/analytics-mixin.spec.js @@ -1,266 +1,299 @@ import analyticsMixin from "@/mixins/analytics-mixin"; import { setupMocksForJsFiles, setupCookies } from "@/helpers/unit-test-helper.js"; import { storeActions } from "@/constants/store-actions"; -import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes } from "@/constants/analytics"; +import { + analyticsPageEvents, + GaCategories, + GaActions, + GaLabels, + GaEvents, + ValueToLogTypes, +} from "@/constants/analytics"; import store from "@/store"; describe("analyticsMixin.js", () => { - test("logPageView: calls dispatch with type and payload", () => { - const type = ""; - const payload = {}; + test("logPageView: calls dispatch with type and payload", () => { + const type = ""; + const payload = {}; - const mockData = { - actionList: [{ - actionName: storeActions.LOG_PAGE_VIEW - }], - } - const mocks = setupMocksForJsFiles(mockData); + const mockData = { + actionList: [ + { + actionName: storeActions.LOG_PAGE_VIEW, + }, + ], + }; + const mocks = setupMocksForJsFiles(mockData); - const testCookieValue = { - sid: '10000000-0000-0000-0000-000000000001' - } + const testCookieValue = { + sid: "10000000-0000-0000-0000-000000000001", + }; - setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); + setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); - analyticsMixin.methods.logPageView(type, payload); + analyticsMixin.methods.logPageView(type, payload); - expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); - }); + expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); + }); - test("logCustomEvent: calls dispatch with type and payload", () => { - const mockData = { - actionList: [{ - actionName: storeActions.LOG_CUSTOM_EVENT - }], - } - const mocks = setupMocksForJsFiles(mockData); + test("logCustomEvent: calls dispatch with type and payload", () => { + const mockData = { + actionList: [ + { + actionName: storeActions.LOG_CUSTOM_EVENT, + }, + ], + }; + const mocks = setupMocksForJsFiles(mockData); - analyticsMixin.methods.logCustomEvent("someCat", "someAction", "someLabel", "someVal"); + analyticsMixin.methods.logCustomEvent("someCat", "someAction", "someLabel", "someVal"); - expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); - }); + expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); + }); - test("pushEventToGA, should call dataLayer push and logCustomEvent too", () => { - // Arrange - window.dataLayer = []; - const mockData = { - actionList: [{ - actionName: storeActions.LOG_CUSTOM_EVENT - }], - } - const mocks = setupMocksForJsFiles(mockData); - var mockDataLayer = []; - mockDataLayer.push({ - event: 'event', - category: 'category', - action: 'action', - label: 'label', - value: undefined, - path: '/fmg/?fmgPage=' - }); + test("pushEventToGA, should call dataLayer push and logCustomEvent too", () => { + // Arrange + window.dataLayer = []; + const mockData = { + actionList: [ + { + actionName: storeActions.LOG_CUSTOM_EVENT, + }, + ], + }; + const mocks = setupMocksForJsFiles(mockData); + var mockDataLayer = []; + mockDataLayer.push({ + event: "event", + category: "category", + action: "action", + label: "label", + value: undefined, + path: "/fmg/?fmgPage=", + }); - // Act - analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true); + // Act + analyticsMixin.methods.pushEventToGA("category", "action", "label", true); - // Assert - expect(mockDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); - expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); - }); + // Assert + expect(mockDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); + expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); + }); - test("pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 only logs last 5 of label", () => { - // Arrange - window.dataLayer = []; - var expectedDataLayer = []; - expectedDataLayer.push({ - event: 'event', - category: 'category', - action: 'action', - label: '33333', - value: undefined, - path: '/fmg/?fmgPage=' - }); + test("pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 only logs last 5 of label", () => { + // Arrange + window.dataLayer = []; + var expectedDataLayer = []; + expectedDataLayer.push({ + event: "event", + category: "category", + action: "action", + label: "33333", + value: undefined, + path: "/fmg/?fmgPage=", + }); - // Act - analyticsMixin.methods.pushEventToGA('category', 'action', '1111122222333333', false, ValueToLogTypes.LAST_5); + // Act + analyticsMixin.methods.pushEventToGA( + "category", + "action", + "1111122222333333", + false, + ValueToLogTypes.LAST_5 + ); - // Assert - expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); - }); + // Assert + expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); + }); - test("pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 logs only the last 3 characters for a 3 character string", () => { - // Arrange - window.dataLayer = []; - var expectedDataLayer = []; - expectedDataLayer.push({ - event: 'event', - category: 'category', - action: 'action', - label: '111', - value: undefined, - path: '/fmg/?fmgPage=' - }); + test("pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 logs only the last 3 characters for a 3 character string", () => { + // Arrange + window.dataLayer = []; + var expectedDataLayer = []; + expectedDataLayer.push({ + event: "event", + category: "category", + action: "action", + label: "111", + value: undefined, + path: "/fmg/?fmgPage=", + }); - // Act - analyticsMixin.methods.pushEventToGA('category', 'action', '111', false, ValueToLogTypes.LAST_5); + // Act + analyticsMixin.methods.pushEventToGA( + "category", + "action", + "111", + false, + ValueToLogTypes.LAST_5 + ); - // Assert - expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); - }); + // Assert + expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); + }); - test("Experiments, should push to dataLayer with default Google Custom Dimension Index", () => { - // Arrange - window.dataLayer = []; + test("Experiments, should push to dataLayer with default Google Custom Dimension Index", () => { + // Arrange + window.dataLayer = []; - const mockExperimentData = - [ - { - settings: {}, - variationName: 'test', - universeName: 'testUniverse' - } - ] - - // Mock store - jest.mock("@/store", () => { return {}; }, { virtual: true }); - - store.getters = { - applicationUser: { - experiments: [ + const mockExperimentData = [ { - settings: {}, - variationName: 'test', - universeName: 'testUniverse' - } - ] - } - }; + settings: {}, + variationName: "test", + universeName: "testUniverse", + }, + ]; + // Mock store + jest.mock( + "@/store", + () => { + return {}; + }, + { virtual: true } + ); - // Act - analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData); + store.getters = { + applicationUser: { + experiments: [ + { + settings: {}, + variationName: "test", + universeName: "testUniverse", + }, + ], + }, + }; - // Assert - expect(window.dataLayer).toEqual([{ - experimentId_99: undefined, - variationId_99: undefined, - experimentName_99: 'testUniverse', - variationName_99: 'test', - customDimension_99: 'undefined_undefined_testUniverse_test' - }]); + // Act + analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData); - }); - - test("Experiments, should push to dataLayer with custom Google Custom Dimension Index", () => { - // Arrange - window.dataLayer = []; - - const mockExperimentData = - [ - { - settings: { "Google Custom Dimension Index": "5" }, - variationName: 'test', - universeName: 'testUniverse' - } - ] - - // Mock store - jest.mock("@/store", () => { return {}; }, { virtual: true }); - - store.getters = { - applicationUser: { - experiments: [ + // Assert + expect(window.dataLayer).toEqual([ { - settings: { "Google Custom Dimension Index": "5" }, - variationName: 'test', - universeName: 'testUniverse' - } - ] - } - }; + experimentId_99: undefined, + variationId_99: undefined, + experimentName_99: "testUniverse", + variationName_99: "test", + customDimension_99: "undefined_undefined_testUniverse_test", + }, + ]); + }); - // Act - analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData); + test("Experiments, should push to dataLayer with custom Google Custom Dimension Index", () => { + // Arrange + window.dataLayer = []; - // Assert - expect(window.dataLayer).toEqual([{ - experimentId_5: undefined, - variationId_5: undefined, - experimentName_5: 'testUniverse', - variationName_5: 'test', - customDimension_5: 'undefined_undefined_testUniverse_test' - }]); + const mockExperimentData = [ + { + settings: { "Google Custom Dimension Index": "5" }, + variationName: "test", + universeName: "testUniverse", + }, + ]; - }); + // Mock store + jest.mock( + "@/store", + () => { + return {}; + }, + { virtual: true } + ); - test("Obj is not null after action prepended", () => { - //Arrange - const obj = {baseMethodName:"testMethodName", data:"testData"}; - const method = {name:"testMethodName", data:"testData" } - const action = "testAction"; + store.getters = { + applicationUser: { + experiments: [ + { + settings: { "Google Custom Dimension Index": "5" }, + variationName: "test", + universeName: "testUniverse", + }, + ], + }, + }; - //Act - analyticsMixin.methods.prependActionToMethod(obj, method, action); + // Act + analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData); - //Assert - expect(obj!=null); - }); - test("Obj method name does not include bound", () => { - //Arrange - const obj = {baseMethodName:"testMethodName", data:"testData"}; - const method = {name:"testMethodName", data:"testData" } - const action = "testAction"; + // Assert + expect(window.dataLayer).toEqual([ + { + experimentId_5: undefined, + variationId_5: undefined, + experimentName_5: "testUniverse", + variationName_5: "test", + customDimension_5: "undefined_undefined_testUniverse_test", + }, + ]); + }); - //Act - analyticsMixin.methods.prependActionToMethod(obj, method, action); - + test("Obj is not null after action prepended", () => { + //Arrange + const obj = { baseMethodName: "testMethodName", data: "testData" }; + const method = { name: "testMethodName", data: "testData" }; + const action = "testAction"; - //Assert - expect(method.name.startsWith("bound ")).toBe(false); - }); - test("Prepended action does not include bound", () => { - //Arrange - const obj = {baseMethodName:"testMethodName", data:"testData"}; - const method = {name:"testMethodName", data:"testData" } - const action = "testAction"; + //Act + analyticsMixin.methods.prependActionToMethod(obj, method, action); - //Act - analyticsMixin.methods.prependActionToMethod(obj, method, action); - + //Assert + expect(obj != null); + }); + test("Obj method name does not include bound", () => { + //Arrange + const obj = { baseMethodName: "testMethodName", data: "testData" }; + const method = { name: "testMethodName", data: "testData" }; + const action = "testAction"; - //Assert - expect(action.startsWith("bound ")).toBe(false); - }); + //Act + analyticsMixin.methods.prependActionToMethod(obj, method, action); - test("analyticsPageEvents returns constants analyticsPageEvents", () => { - //Act - const analyticsPE = analyticsMixin.computed.analyticsPageEvents(); + //Assert + expect(method.name.startsWith("bound ")).toBe(false); + }); + test("Prepended action does not include bound", () => { + //Arrange + const obj = { baseMethodName: "testMethodName", data: "testData" }; + const method = { name: "testMethodName", data: "testData" }; + const action = "testAction"; - //Assert - expect(analyticsPE).toEqual(analyticsPageEvents); - }); + //Act + analyticsMixin.methods.prependActionToMethod(obj, method, action); - test("GaActions returns constants GaActions", () => { - //Act - const gaActions = analyticsMixin.computed.GaActions(); + //Assert + expect(action.startsWith("bound ")).toBe(false); + }); - //Assert - expect(gaActions).toEqual(GaActions); - }); + test("analyticsPageEvents returns constants analyticsPageEvents", () => { + //Act + const analyticsPE = analyticsMixin.computed.analyticsPageEvents(); - test("GaCategories returns constants GaCategories", () => { - //Act - const gaCategories = analyticsMixin.computed.GaCategories(); + //Assert + expect(analyticsPE).toEqual(analyticsPageEvents); + }); - //Assert - expect(gaCategories).toEqual(GaCategories); - }); + test("GaActions returns constants GaActions", () => { + //Act + const gaActions = analyticsMixin.computed.GaActions(); - test("GaLabels returns constants GaLabels", () => { - //Act - const gaLabels = analyticsMixin.computed.GaLabels(); + //Assert + expect(gaActions).toEqual(GaActions); + }); - //Assert - expect(gaLabels).toEqual(GaLabels); - }); + test("GaCategories returns constants GaCategories", () => { + //Act + const gaCategories = analyticsMixin.computed.GaCategories(); -}); \ No newline at end of file + //Assert + expect(gaCategories).toEqual(GaCategories); + }); + + test("GaLabels returns constants GaLabels", () => { + //Act + const gaLabels = analyticsMixin.computed.GaLabels(); + + //Assert + expect(gaLabels).toEqual(GaLabels); + }); +}); diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index 22f1b49ce..a72bc0186 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -9,103 +9,107 @@ import { dynamicStrings } from "@/constants/dynamic-strings"; import { partTypeStrings } from "../constants/part-type-strings"; export default { - data() { - return { - cmsContentByWidget: {}, - }; - }, - methods: { - setCmsContent(cmsContent) { - this.$root.cmsContentByWidget = cmsContent; + data() { + return { + cmsContentByWidget: {}, + }; }, - getCmsContent(widgetName, fieldName) { - return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] - ? this.$root.cmsContentByWidget[widgetName][fieldName] - : ""; - }, - dispatchStoreAction(type, payload, encodePayload = true) { - // Encode the payload if required - if (encodePayload) { - encodeUriData(payload); - } + methods: { + setCmsContent(cmsContent) { + this.$root.cmsContentByWidget = cmsContent; + }, + getCmsContent(widgetName, fieldName) { + return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] + ? this.$root.cmsContentByWidget[widgetName][fieldName] + : ""; + }, + dispatchStoreAction(type, payload, encodePayload = true) { + // Encode the payload if required + if (encodePayload) { + encodeUriData(payload); + } - return store.dispatch(type, payload); - }, - savePageDataToStore(page, data) { - store.commit(storeMutations.UPDATE_PAGE_DATA, { page: page, data: data }); - }, - onSubmit() {}, // DO NOT REMOVE; needed to prevent default form submit behavior. Cannot use .prevent modifier for vee-validate Form - onInvalidSubmit({ values, errors, results }) { - // identify the first error field and put focus on it - // get error names array - const errorNames = errors ? Object.keys(errors) : []; - const firstErrorEl = errorNames[0]; - if (firstErrorEl) { - const qsString = "[data-focus-target='" + firstErrorEl + "']"; - const el = document.querySelector(qsString); - el && el.focus(); - } - }, - getFooterInfoBoxHeight() { - const footerInfoBox = document.querySelector(".footer#infoBox"); - return footerInfoBox ? footerInfoBox.offsetHeight : 0; - }, - async getZipCodeData(zipCode) { - const serviceZipValidationResponse = await this.dispatchStoreAction( - storeActions.VALIDATE_ZIP, - { zip: zipCode } - ); + return store.dispatch(type, payload); + }, + savePageDataToStore(page, data) { + store.commit(storeMutations.UPDATE_PAGE_DATA, { page: page, data: data }); + }, + onSubmit() {}, // DO NOT REMOVE; needed to prevent default form submit behavior. Cannot use .prevent modifier for vee-validate Form + onInvalidSubmit({ values, errors, results }) { + // identify the first error field and put focus on it + // get error names array + const errorNames = errors ? Object.keys(errors) : []; + const firstErrorEl = errorNames[0]; + if (firstErrorEl) { + const qsString = "[data-focus-target='" + firstErrorEl + "']"; + const el = document.querySelector(qsString); + el && el.focus(); + } + }, + getFooterInfoBoxHeight() { + const footerInfoBox = document.querySelector(".footer#infoBox"); + return footerInfoBox ? footerInfoBox.offsetHeight : 0; + }, + async getZipCodeData(zipCode) { + const serviceZipValidationResponse = await this.dispatchStoreAction( + storeActions.VALIDATE_ZIP, + { zip: zipCode } + ); - return { - isValid: serviceZipValidationResponse.data.isValid, - isServiceable: serviceZipValidationResponse.data.isServiceable, - state: serviceZipValidationResponse.data.state, - }; + return { + isValid: serviceZipValidationResponse.data.isValid, + isServiceable: serviceZipValidationResponse.data.isServiceable, + state: serviceZipValidationResponse.data.state, + }; + }, + getTierOnePackagePrice(lineItems) { + let totalPrice = 0; + lineItems.forEach((lineItem) => { + const partType = lineItem.partType.toUpperCase(); + if ( + partType != partTypeStrings.FRONT_WIPER && + partType != partTypeStrings.REAR_WIPER && + partType != partTypeStrings.RAIN_DEFENSE + ) { + totalPrice += lineItem.price; + } + }); + return totalPrice; + }, }, - getTierOnePackagePrice(lineItems) { - let totalPrice = 0; - lineItems.forEach((lineItem) => { - const partType = lineItem.partType.toUpperCase(); - if (partType != partTypeStrings.FRONT_WIPER && partType != partTypeStrings.REAR_WIPER && partType != partTypeStrings.RAIN_DEFENSE) { - totalPrice += lineItem.price; - } - }); - return totalPrice; - } - }, - computed: { - storeActions() { - return storeActions; + computed: { + storeActions() { + return storeActions; + }, + storeMutations() { + return storeMutations; + }, + navigationScenarios() { + return navigationScenarios; + }, + vehicleCategories() { + return vehicleCategories; + }, + routerParams() { + return routerParams; + }, + queryStrings() { + return queryStrings; + }, + dynamicStrings() { + return dynamicStrings; + }, + cssClassNameForCmsWidget() { + return "widget-name-" + this.cmsWidgetName; + }, }, - storeMutations() { - return storeMutations; - }, - navigationScenarios() { - return navigationScenarios; - }, - vehicleCategories() { - return vehicleCategories; - }, - routerParams() { - return routerParams; - }, - queryStrings() { - return queryStrings; - }, - dynamicStrings() { - return dynamicStrings; - }, - cssClassNameForCmsWidget() { - return "widget-name-" + this.cmsWidgetName; - }, - }, }; function encodeUriData(payload) { - if (payload && Object.keys(payload).length > 0) { - // Loop through the payload and encode the values - Object.keys(payload).forEach((key) => { - payload[key] = encodeURIComponent(payload[key]); - }); - } + if (payload && Object.keys(payload).length > 0) { + // Loop through the payload and encode the values + Object.keys(payload).forEach((key) => { + payload[key] = encodeURIComponent(payload[key]); + }); + } } diff --git a/src/mixins/base-mixin.spec.js b/src/mixins/base-mixin.spec.js index 6009dd67c..b768b67d5 100644 --- a/src/mixins/base-mixin.spec.js +++ b/src/mixins/base-mixin.spec.js @@ -6,141 +6,147 @@ import { vehicleCategories } from "@/constants/vehicle-categories.js"; import store from "@/store"; describe("baseMixin.js", () => { - test("dispatchStoreAction: calls dispatch with type and payload", () => { - const mixIn = getMixInInstance({}); - const type = ""; - const payload = {}; + test("dispatchStoreAction: calls dispatch with type and payload", () => { + const mixIn = getMixInInstance({}); + const type = ""; + const payload = {}; - mixIn.methods.dispatchStoreAction(type, payload); + mixIn.methods.dispatchStoreAction(type, payload); - expect(store.dispatch).toBeCalledWith(type, payload); - }); + expect(store.dispatch).toBeCalledWith(type, payload); + }); - test("dispatchStoreAction: calls dispatch with type and payload, handles Uri encode", () => { - const mixIn = getMixInInstance({}); - const type = ""; - const payload = { make: "Alfa Romeo/Chrysler" }; + test("dispatchStoreAction: calls dispatch with type and payload, handles Uri encode", () => { + const mixIn = getMixInInstance({}); + const type = ""; + const payload = { make: "Alfa Romeo/Chrysler" }; - mixIn.methods.dispatchStoreAction(type, payload, true); + mixIn.methods.dispatchStoreAction(type, payload, true); - expect(store.dispatch).toBeCalledWith(type, payload); - }); + expect(store.dispatch).toBeCalledWith(type, payload); + }); - test("savePageDataToStore, should call store commit", () => { - const mixIn = getMixInInstance({}); + test("savePageDataToStore, should call store commit", () => { + const mixIn = getMixInInstance({}); - mixIn.methods.savePageDataToStore('vehicle-year', {}); + mixIn.methods.savePageDataToStore("vehicle-year", {}); - expect(store.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: 'vehicle-year', data: {}}); - }); + 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({}); + test("computed: storeActions should be equal to import object", () => { + // Arrange + const mixIn = getMixInInstance({}); - // Act - let storeActionsForTest = mixIn.computed.storeActions(); + // Act + let storeActionsForTest = mixIn.computed.storeActions(); - // Assert - expect(storeActionsForTest).toEqual(storeActions); - }); + // Assert + expect(storeActionsForTest).toEqual(storeActions); + }); - test("computed: storeMutations should be equal to import object", () => { - // Arrange - const mixIn = getMixInInstance({}); + test("computed: storeMutations should be equal to import object", () => { + // Arrange + const mixIn = getMixInInstance({}); - // Act - let storeMutationsForTest = mixIn.computed.storeMutations(); + // Act + let storeMutationsForTest = mixIn.computed.storeMutations(); - // Assert - expect(storeMutationsForTest).toEqual(storeMutations); - }); + // Assert + expect(storeMutationsForTest).toEqual(storeMutations); + }); - test("computed: navigationScenarios should be equal to import object", () => { - // Arrange - const mixIn = getMixInInstance({}); + test("computed: navigationScenarios should be equal to import object", () => { + // Arrange + const mixIn = getMixInInstance({}); - // Act - let navigationScenariosForTest = mixIn.computed.navigationScenarios(); + // Act + let navigationScenariosForTest = mixIn.computed.navigationScenarios(); - // Assert - expect(navigationScenariosForTest).toEqual(navigationScenarios); - }); - test("computed: vehicleCategories should be equal to import object", () => { - // Arrange - const mixIn = getMixInInstance({}); + // Assert + expect(navigationScenariosForTest).toEqual(navigationScenarios); + }); + test("computed: vehicleCategories should be equal to import object", () => { + // Arrange + const mixIn = getMixInInstance({}); - // Act - let vehicleCategoriesForTest = mixIn.computed.vehicleCategories(); + // Act + let vehicleCategoriesForTest = mixIn.computed.vehicleCategories(); - // Assert - expect(vehicleCategoriesForTest).toEqual(vehicleCategories); - }); + // Assert + expect(vehicleCategoriesForTest).toEqual(vehicleCategories); + }); - test("onInvalidSubmit: puts focus on first error", () => { - // Arrange - const mixIn = getMixInInstance({}); - const validationData = { - errors: { - fieldOne: 'error message 1', - fieldTwo: 'error message 2', - } - }; + test("onInvalidSubmit: puts focus on first error", () => { + // Arrange + const mixIn = getMixInInstance({}); + const validationData = { + errors: { + fieldOne: "error message 1", + fieldTwo: "error message 2", + }, + }; - global.document.querySelector = jest.fn(); + global.document.querySelector = jest.fn(); - // Act - mixIn.methods.onInvalidSubmit(validationData); + // Act + mixIn.methods.onInvalidSubmit(validationData); - // Assert - expect(global.document.querySelector).toBeCalledWith("[data-focus-target='fieldOne']"); - }); + // Assert + expect(global.document.querySelector).toBeCalledWith("[data-focus-target='fieldOne']"); + }); - test("getZipCodeData calls dispatch", () => { - const mixIn = getMixInInstance({}); - mixIn.methods.dispatchStoreAction = jest.fn(); - mixIn.methods.dispatchStoreAction.mockReturnValue({data: { isValid:true, isServiceable:true, state:"OH" }}); - const type = ""; - const payload = { zip: 43015 }; + test("getZipCodeData calls dispatch", () => { + const mixIn = getMixInInstance({}); + mixIn.methods.dispatchStoreAction = jest.fn(); + mixIn.methods.dispatchStoreAction.mockReturnValue({ + data: { isValid: true, isServiceable: true, state: "OH" }, + }); + const type = ""; + const payload = { zip: 43015 }; - const mockData = { - actionList: [{ - actionName: storeActions.VALIDATE_ZIP - }], - } + const mockData = { + actionList: [ + { + actionName: storeActions.VALIDATE_ZIP, + }, + ], + }; - mixIn.methods.getZipCodeData(type, payload); - - expect(mixIn.methods.dispatchStoreAction).toBeCalled(); - }); + mixIn.methods.getZipCodeData(type, payload); + expect(mixIn.methods.dispatchStoreAction).toBeCalled(); + }); }); function getMixInInstance({ isDispatchSuccess = true }) { - // Mock Store - const storeDispatch = jest.fn(); + // Mock Store + const storeDispatch = jest.fn(); - if (isDispatchSuccess) { - storeDispatch.mockReturnValue(Promise.resolve()); - } else { - storeDispatch.mockReturnValue(Promise.reject()); - } + if (isDispatchSuccess) { + storeDispatch.mockReturnValue(Promise.resolve()); + } else { + storeDispatch.mockReturnValue(Promise.reject()); + } - // Mock Route - const route = { - query: { - fmgPage: "test-page", - }, - }; + // Mock Route + const route = { + query: { + fmgPage: "test-page", + }, + }; - // Attach mocks to mixin - const baseMixIn = baseMixin; - baseMixIn.methods.$route = route; - baseMixIn.methods.storeActions = storeActions; - baseMixIn.methods.vehicleCategories = vehicleCategories; + // Attach mocks to mixin + const baseMixIn = baseMixin; + baseMixIn.methods.$route = route; + baseMixIn.methods.storeActions = storeActions; + baseMixIn.methods.vehicleCategories = vehicleCategories; - store.dispatch = storeDispatch; - store.commit = jest.fn(); + store.dispatch = storeDispatch; + store.commit = jest.fn(); - return baseMixIn; + return baseMixIn; } diff --git a/src/mixins/experiment-mixin.js b/src/mixins/experiment-mixin.js index 2a6b10f34..11b573683 100644 --- a/src/mixins/experiment-mixin.js +++ b/src/mixins/experiment-mixin.js @@ -9,7 +9,9 @@ export default { return Object.hasOwn(store.getters.experimentSettings, settingName); }, getSettingValue(settingName) { - return this.hasSetting(settingName) ? store.getters.experimentSettings[settingName] : null; - } + return this.hasSetting(settingName) + ? store.getters.experimentSettings[settingName] + : null; + }, }, -} \ No newline at end of file +}; diff --git a/src/mixins/experiment-mixin.spec.js b/src/mixins/experiment-mixin.spec.js index b3d68d1b6..1b2bff2f5 100644 --- a/src/mixins/experiment-mixin.spec.js +++ b/src/mixins/experiment-mixin.spec.js @@ -58,7 +58,7 @@ describe("experiment-mixin", () => { // Act const result = wrapper.vm.hasSetting("Setting4"); - // Assert + // Assert expect(result).toEqual(true); }); @@ -69,7 +69,7 @@ describe("experiment-mixin", () => { // Act const result = wrapper.vm.hasSetting("BooglyWoogly"); - // Assert + // Assert expect(result).toEqual(false); }); @@ -80,7 +80,7 @@ describe("experiment-mixin", () => { // Act const result = wrapper.vm.hasSetting("Setting4"); - // Assert + // Assert expect(result).toEqual(false); }); }); @@ -94,7 +94,7 @@ describe("experiment-mixin", () => { const result = wrapper.vm.getSettingValue("Setting3"); // Assert - expect(result).toEqual("Value3") + expect(result).toEqual("Value3"); }); test("does not have setting => return null", () => { @@ -124,25 +124,25 @@ describe("experiment-mixin", () => { function setupMocks({ experimentSettings }) { const mocks = getMountOptions({}); - const testExperimentSettings = { - "Setting1": "Value1", - "Setting2": "Value2", - "Setting3": "Value3", - "Setting4": "Value1", - "Setting5": "Value2", - "Setting6": "Value3" + const testExperimentSettings = { + Setting1: "Value1", + Setting2: "Value2", + Setting3: "Value3", + Setting4: "Value1", + Setting5: "Value2", + Setting6: "Value3", }; store.getters = { - experimentSettings: experimentSettings ?? testExperimentSettings + experimentSettings: experimentSettings ?? testExperimentSettings, }; const mockComponent = { template: "
", - mixins: [experimentMixin] + mixins: [experimentMixin], }; const wrapper = shallowMount(mockComponent, mocks); return { wrapper }; -} \ No newline at end of file +} diff --git a/src/mixins/input-button-wrapper-mixin.js b/src/mixins/input-button-wrapper-mixin.js index e5c6808df..1d34d6520 100644 --- a/src/mixins/input-button-wrapper-mixin.js +++ b/src/mixins/input-button-wrapper-mixin.js @@ -37,4 +37,4 @@ export default { }, }, }, -}; \ No newline at end of file +}; diff --git a/src/mixins/vehicle-questions-mixin.js b/src/mixins/vehicle-questions-mixin.js index 1abf7f484..78562a5dd 100644 --- a/src/mixins/vehicle-questions-mixin.js +++ b/src/mixins/vehicle-questions-mixin.js @@ -10,23 +10,23 @@ import store from "@/store"; export default { methods: { hasPartQuestions(partsOrQuestions) { - return partsOrQuestions?.some(pq => pq.partQuestions?.length > 0); + return partsOrQuestions?.some((pq) => pq.partQuestions?.length > 0); }, hasGlassLocationWithMultipleParts(partsOrQuestions) { - return partsOrQuestions?.some(pq => pq.parts?.length > 1); + return partsOrQuestions?.some((pq) => pq.parts?.length > 1); }, hasChildPartQuestions(partsOrQuestions) { - return partsOrQuestions?.some(pq => { - return pq.parts?.some(part => part.childPartQuestions?.length > 0); + return partsOrQuestions?.some((pq) => { + return pq.parts?.some((part) => part.childPartQuestions?.length > 0); }); }, hasCapabilityQuestions(partsOrQuestions) { - return partsOrQuestions?.some(pq => { - return pq.parts?.some(part => part.requiresCapabilityQuestions === true); + return partsOrQuestions?.some((pq) => { + return pq.parts?.some((part) => part.requiresCapabilityQuestions === true); }); }, - // method to only include keys listed for lineItems.glassParts in + // method to only include keys listed for lineItems.glassParts in // https://safelite.atlassian.net/wiki/spaces/DC/pages/17137665/Catalog+Front-End+State#glassParts reducedGlassPartsArray(glassParts) { const reducedGlassParts = []; @@ -53,10 +53,13 @@ export default { fmgPageValues.VEHICLE_PARTS, fmgPageValues.MOLDING_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, - fmgPageValues.QUOTE - ] + fmgPageValues.QUOTE, + ]; - return orderedVehicleQuestionPages.indexOf(currentPage) - orderedVehicleQuestionPages.indexOf(fmgPage); + return ( + orderedVehicleQuestionPages.indexOf(currentPage) - + orderedVehicleQuestionPages.indexOf(fmgPage) + ); }, currentPageComesBeforePage(currentPage = this.$route.query.fmgPage, fmgPage) { return this.comparePageIndices(currentPage, fmgPage) < 0; @@ -68,61 +71,81 @@ export default { glass.key = glass.location + "-" + glass.name; const self = vm ?? this; // clear answerData if no questions are already answered - if (!alreadyAnsweredQuestions) { glass.answerData = null } - + if (!alreadyAnsweredQuestions) { + glass.answerData = null; + } + alreadyAnsweredQuestions?.forEach((answeredGlass) => { // if answeredGlass lacks any of these properties then exit - if (!answeredGlass.location || - !answeredGlass.name || - !answeredGlass.answeredQuestions || - !answeredGlass.result && !answeredGlass.partNum) { return } + if ( + !answeredGlass.location || + !answeredGlass.name || + !answeredGlass.answeredQuestions || + (!answeredGlass.result && !answeredGlass.partNum) + ) { + return; + } // test if glass parts match - if (glass.location === answeredGlass.location && glass.name === answeredGlass.name) { + if ( + glass.location === answeredGlass.location && + glass.name === answeredGlass.name + ) { let answerString = ""; // loop through answeredQuestions for matches answeredGlass.answeredQuestions.forEach((aq) => { - if (!aq.questionNum || !aq.selectedAnswerText) { return } + if (!aq.questionNum || !aq.selectedAnswerText) { + return; + } // determine which answer was previously chosen - const chosenAns = glass.questions[aq.questionNum-1].answers.find((a) => { - return a.answerText.toUpperCase() === aq.selectedAnswerText.toUpperCase(); + const chosenAns = glass.questions[aq.questionNum - 1].answers.find((a) => { + return ( + a.answerText.toUpperCase() === aq.selectedAnswerText.toUpperCase() + ); }); // set the answerString to use for answerSelected if (chosenAns.nextQuestionSequence) { - answerString = `${aq.questionNum}|nextQuestion|${chosenAns.nextQuestionSequence}|${chosenAns.answerText}` + answerString = `${aq.questionNum}|nextQuestion|${chosenAns.nextQuestionSequence}|${chosenAns.answerText}`; } else { - answerString = `${aq.questionNum}|answer|${chosenAns.answerResult}|${chosenAns.answerText}` + answerString = `${aq.questionNum}|answer|${chosenAns.answerResult}|${chosenAns.answerText}`; } // mark this question as answered (question-chain will read this) - glass.questions[aq.questionNum-1].answerSelected = answerString; + glass.questions[aq.questionNum - 1].answerSelected = answerString; // mark this question as suppressed if needed (question-chain uses this) - if (aq.suppressQuestion) { glass.questions[aq.questionNum-1].suppressQuestion = true } + if (aq.suppressQuestion) { + glass.questions[aq.questionNum - 1].suppressQuestion = true; + } }); // advance the currentGlassIndex self.currentGlassIndex = i; - const answerResult = (answeredGlass.partNum) ? answeredGlass.partNum : answeredGlass.result; + const answerResult = answeredGlass.partNum + ? answeredGlass.partNum + : answeredGlass.result; // the above logic for answerResult covers all 3 ___-questions page scenarios // add answerData to current glass glass.answerData = { answerResult: answerResult, - answeredQuestions: answeredGlass.answeredQuestions - } + answeredQuestions: answeredGlass.answeredQuestions, + }; } - }); - // Set up watch for each set of glass questions + // Set up watch for each set of glass questions // (updated when all questions for a glass have been answered in question-chain) - self.$watch("selectedAnswers." + glass.location + '-' + glass.name, (newValue) => { - if (newValue) { - self.handleAnswerUpdates(newValue); - } - }, {deep: true}) + self.$watch( + "selectedAnswers." + glass.location + "-" + glass.name, + (newValue) => { + if (newValue) { + self.handleAnswerUpdates(newValue); + } + }, + { deep: true } + ); return glass; }, @@ -131,48 +154,89 @@ export default { const self = vm ?? this; const currentPage = self.$route.query.fmgPage; - const hasPartQuestions = this.hasPartQuestions(partsOrQuestions) - const hasGlassLocationWithMultipleParts = this.hasGlassLocationWithMultipleParts(partsOrQuestions); + const hasPartQuestions = this.hasPartQuestions(partsOrQuestions); + const hasGlassLocationWithMultipleParts = + this.hasGlassLocationWithMultipleParts(partsOrQuestions); const hasChildPartQuestions = this.hasChildPartQuestions(partsOrQuestions); const hasCapabilityQuestions = this.hasCapabilityQuestions(partsOrQuestions); - if (hasPartQuestions && this.currentPageComesBeforePage(currentPage, fmgPageValues.PART_QUESTIONS)) { - self.$router.navigateWithSaving(self.navigationScenarios.HAS_PART_QUESTIONS, self.$route, {}, {}, { partsOrQuestions: partsOrQuestions }); - } - else if (hasGlassLocationWithMultipleParts && this.currentPageComesBeforePage(currentPage, fmgPageValues.VEHICLE_PARTS)) { + if ( + hasPartQuestions && + this.currentPageComesBeforePage(currentPage, fmgPageValues.PART_QUESTIONS) + ) { + self.$router.navigateWithSaving( + self.navigationScenarios.HAS_PART_QUESTIONS, + self.$route, + {}, + {}, + { partsOrQuestions: partsOrQuestions } + ); + } else if ( + hasGlassLocationWithMultipleParts && + this.currentPageComesBeforePage(currentPage, fmgPageValues.VEHICLE_PARTS) + ) { // if multiple parts on any glass // go to vehicle-parts page and pass the partsData - self.$router.navigateWithSaving(self.navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, self.$route, {}, {}, { partsOrQuestions: partsOrQuestions }); - } else if (hasChildPartQuestions && this.currentPageComesBeforePage(currentPage, fmgPageValues.MOLDING_QUESTIONS)) { + self.$router.navigateWithSaving( + self.navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, + self.$route, + {}, + {}, + { partsOrQuestions: partsOrQuestions } + ); + } else if ( + hasChildPartQuestions && + this.currentPageComesBeforePage(currentPage, fmgPageValues.MOLDING_QUESTIONS) + ) { // if any childpart questions // go to molding-questions page and pass the partsData - self.$router.navigateWithSaving(self.navigationScenarios.HAS_MOLDING_QUESTIONS, self.$route, {}, {}, { partsOrQuestions: partsOrQuestions }); - } else if (hasCapabilityQuestions && this.currentPageComesBeforePage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS)) { + self.$router.navigateWithSaving( + self.navigationScenarios.HAS_MOLDING_QUESTIONS, + self.$route, + {}, + {}, + { partsOrQuestions: partsOrQuestions } + ); + } else if ( + hasCapabilityQuestions && + this.currentPageComesBeforePage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS) + ) { // if has capability questions // go to capability-questions page and pass the partsData // mimic part-questions page data for consistency for (let partOrQuestion of partsOrQuestions) { if (this.hasCapabilityQuestions([partOrQuestion])) { - let capabilityQuestionsForGlassLocation = (await baseMixin.methods.dispatchStoreAction(storeActions.GET_CAPABILITY_QUESTIONS, { - carId: store.getters.vehicle.carId, - partNumber: partOrQuestion.parts[0].partNumber - })).data; + let capabilityQuestionsForGlassLocation = ( + await baseMixin.methods.dispatchStoreAction( + storeActions.GET_CAPABILITY_QUESTIONS, + { + carId: store.getters.vehicle.carId, + partNumber: partOrQuestion.parts[0].partNumber, + } + ) + ).data; - capabilityQuestionsForGlassLocation.forEach(question => { - question.answers = question.answers.map(answer => { + capabilityQuestionsForGlassLocation.forEach((question) => { + question.answers = question.answers.map((answer) => { return { ...answer, - answerResult: answer.answerResult1 - } - }) - }) + answerResult: answer.answerResult1, + }; + }); + }); partOrQuestion.capabilityQuestions = capabilityQuestionsForGlassLocation; } } - self.$router.navigateWithSaving(self.navigationScenarios.HAS_CAPABILITY_QUESTIONS, self.$route, {}, {}, { partsOrQuestions }); + self.$router.navigateWithSaving( + self.navigationScenarios.HAS_CAPABILITY_QUESTIONS, + self.$route, + {}, + {}, + { partsOrQuestions } + ); } else { // if single parts only const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions); @@ -184,31 +248,42 @@ export default { } }, backButtonAction() { - const partsOrQuestions = (this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS) ?? this.$store.getters.pageData(fmgPageValues.VEHICLE_PARTS))?.partsOrQuestions; + const partsOrQuestions = ( + this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS) ?? + this.$store.getters.pageData(fmgPageValues.VEHICLE_PARTS) + )?.partsOrQuestions; const hasPartQuestions = this.hasPartQuestions(partsOrQuestions); - const hasGlassLocationWithMultipleParts = this.hasGlassLocationWithMultipleParts(partsOrQuestions); + const hasGlassLocationWithMultipleParts = + this.hasGlassLocationWithMultipleParts(partsOrQuestions); const hasChildPartQuestions = this.hasChildPartQuestions(partsOrQuestions); const hasCapabilityQuestions = this.hasCapabilityQuestions(partsOrQuestions); let backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS; const currentPage = this.$route.query.fmgPage; - if (hasCapabilityQuestions && this.currentPageComesAfterPage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS)) { + if ( + hasCapabilityQuestions && + this.currentPageComesAfterPage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS) + ) { backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_CAPABILITY_QUESTIONS; - } - else if (hasChildPartQuestions && this.currentPageComesAfterPage(currentPage, fmgPageValues.MOLDING_QUESTIONS)) { + } else if ( + hasChildPartQuestions && + this.currentPageComesAfterPage(currentPage, fmgPageValues.MOLDING_QUESTIONS) + ) { backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS; - } - else if (hasGlassLocationWithMultipleParts && this.currentPageComesAfterPage(currentPage, fmgPageValues.VEHICLE_PARTS)) { - backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE; - } - else if (hasPartQuestions && this.currentPageComesAfterPage(currentPage, fmgPageValues.PART_QUESTIONS)) { + } else if ( + hasGlassLocationWithMultipleParts && + this.currentPageComesAfterPage(currentPage, fmgPageValues.VEHICLE_PARTS) + ) { + backNavigationScenario = + navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE; + } else if ( + hasPartQuestions && + this.currentPageComesAfterPage(currentPage, fmgPageValues.PART_QUESTIONS) + ) { backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS; } - this.$router.navigateWithoutSaving( - backNavigationScenario, - this.$route - ); - } - } -} \ No newline at end of file + this.$router.navigateWithoutSaving(backNavigationScenario, this.$route); + }, + }, +}; diff --git a/src/mixins/vehicle-questions-mixin.spec.js b/src/mixins/vehicle-questions-mixin.spec.js index b2dab9645..fe5d3d4e8 100644 --- a/src/mixins/vehicle-questions-mixin.spec.js +++ b/src/mixins/vehicle-questions-mixin.spec.js @@ -4,19 +4,19 @@ import { setupMocksForJsFiles, getMountOptions } from "@/helpers/unit-test-helpe import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { storeMutations } from "@/constants/store-mutations"; import { storeActions } from "@/constants/store-actions"; -import loadingModal from '@/common-components/loading-modal/loading-modal.vue'; +import loadingModal from "@/common-components/loading-modal/loading-modal.vue"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { navigationScenarios } from "../router/router-constants/navigation-scenarios"; -import { getters } from "@/store" +import { getters } from "@/store"; jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({ - navigateToHeritageFunnel: jest.fn() + navigateToHeritageFunnel: jest.fn(), })); describe("vehicle-questions-mixin", () => { afterEach(() => { jest.clearAllMocks(); - }) + }); describe("hasPartQuestions", () => { test("has no part questions => return false", () => { @@ -26,9 +26,9 @@ describe("vehicle-questions-mixin", () => { // Act const hasPartQuestions = wrapper.vm.hasPartQuestions([ { - partQuestions: [] - } - ]) + partQuestions: [], + }, + ]); // Assert expect(hasPartQuestions).toBe(false); @@ -39,7 +39,7 @@ describe("vehicle-questions-mixin", () => { const { wrapper } = setupMocks({}); // Act - const hasPartQuestions = wrapper.vm.hasPartQuestions([]) + const hasPartQuestions = wrapper.vm.hasPartQuestions([]); // Assert expect(hasPartQuestions).toBe(false); @@ -52,11 +52,13 @@ describe("vehicle-questions-mixin", () => { // Act const hasPartQuestions = wrapper.vm.hasPartQuestions([ { - partQuestions: [{ - testProperty: "some value" - }] - } - ]) + partQuestions: [ + { + testProperty: "some value", + }, + ], + }, + ]); // Assert expect(hasPartQuestions).toBe(true); @@ -73,11 +75,13 @@ describe("vehicle-questions-mixin", () => { { name: "Something", location: "somewhere", - parts: [{ - partNumber: "1234567" - }] - } - ]) + parts: [ + { + partNumber: "1234567", + }, + ], + }, + ]); // Assert expect(hasGlassLocationWithMultipleParts).toBe(false); @@ -92,25 +96,31 @@ describe("vehicle-questions-mixin", () => { { name: "Something", location: "somewhere", - parts: [{ - partNumber: "1234567" - }] + parts: [ + { + partNumber: "1234567", + }, + ], }, { name: "Another glass", location: "somewhere else", - parts: [{ - partNumber: "1234568" - }] + parts: [ + { + partNumber: "1234568", + }, + ], }, { name: "Special glass", location: "Another where", - parts: [{ - partNumber: "1234569" - }] - } - ]) + parts: [ + { + partNumber: "1234569", + }, + ], + }, + ]); // Assert expect(hasGlassLocationWithMultipleParts).toBe(false); @@ -125,30 +135,34 @@ describe("vehicle-questions-mixin", () => { { name: "Something", location: "somewhere", - parts: [{ - partNumber: "1234567" - }] + parts: [ + { + partNumber: "1234567", + }, + ], }, { name: "Another glass", location: "somewhere else", - parts: [{ - partNumber: "1234568" - }] + parts: [ + { + partNumber: "1234568", + }, + ], }, { name: "Special glass", location: "Another where", parts: [ { - partNumber: "1234569" + partNumber: "1234569", }, { - partNumber: "1234560" - } - ] - } - ]) + partNumber: "1234560", + }, + ], + }, + ]); // Assert expect(hasGlassLocationWithMultipleParts).toBe(true); @@ -165,11 +179,11 @@ describe("vehicle-questions-mixin", () => { { parts: [ { - childPartQuestions: [] - } - ] - } - ]) + childPartQuestions: [], + }, + ], + }, + ]); // Assert expect(hasChildPartQuestions).toBe(false); @@ -180,11 +194,10 @@ describe("vehicle-questions-mixin", () => { const { wrapper } = setupMocks({}); // Act - const hasChildPartQuestions = wrapper.vm.hasChildPartQuestions([]) + const hasChildPartQuestions = wrapper.vm.hasChildPartQuestions([]); // Assert expect(hasChildPartQuestions).toBe(false); - }); test("has child part questions => return true", () => { @@ -198,26 +211,27 @@ describe("vehicle-questions-mixin", () => { { childPartQuestions: [ { - "questionSequence": 1, - "questionText": "Does the rubber seal around your windshield have a chrome strip running through it?", - "answers": [ + questionSequence: 1, + questionText: + "Does the rubber seal around your windshield have a chrome strip running through it?", + answers: [ { - "answerResult": "WKT D1106 C", - "answerText": "Yes", - "nextQuestionSequence": null + answerResult: "WKT D1106 C", + answerText: "Yes", + nextQuestionSequence: null, }, { - "answerResult": "WKT D1106 B", - "answerText": "No", - "nextQuestionSequence": null - } - ] - } - ] - } - ] - } - ]) + answerResult: "WKT D1106 B", + answerText: "No", + nextQuestionSequence: null, + }, + ], + }, + ], + }, + ], + }, + ]); // Assert expect(hasChildPartQuestions).toBe(true); @@ -234,11 +248,11 @@ describe("vehicle-questions-mixin", () => { { parts: [ { - requiresCapabilityQuestions: false - } - ] - } - ]) + requiresCapabilityQuestions: false, + }, + ], + }, + ]); // Assert expect(hasCapabilityQuestions).toBe(false); @@ -249,7 +263,7 @@ describe("vehicle-questions-mixin", () => { const { wrapper } = setupMocks({}); // Act - const hasCapabilityQuestions = wrapper.vm.hasCapabilityQuestions([]) + const hasCapabilityQuestions = wrapper.vm.hasCapabilityQuestions([]); // Assert expect(hasCapabilityQuestions).toBe(false); @@ -264,11 +278,11 @@ describe("vehicle-questions-mixin", () => { { parts: [ { - requiresCapabilityQuestions: true - } - ] - } - ]) + requiresCapabilityQuestions: true, + }, + ], + }, + ]); // Assert expect(hasCapabilityQuestions).toBe(true); @@ -276,33 +290,40 @@ describe("vehicle-questions-mixin", () => { }); describe("currentPageComesBeforePage", () => { - const testCases = [[fmgPageValues.PART_QUESTIONS, fmgPageValues.VEHICLE_PARTS, true], + const testCases = [ + [fmgPageValues.PART_QUESTIONS, fmgPageValues.VEHICLE_PARTS, true], [fmgPageValues.VEHICLE_PARTS, fmgPageValues.VEHICLE_PARTS, false], [fmgPageValues.QUOTE, fmgPageValues.VEHICLE_PARTS, false], [fmgPageValues.QUOTE, fmgPageValues.QUOTE, false], [fmgPageValues.VEHICLE_PARTS, fmgPageValues.PART_QUESTIONS, false], [fmgPageValues.PART_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, true], - [fmgPageValues.MOLDING_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, true],]; - test.each(testCases)("%s comes before %s is %s", (currentPage, nextPage, expectedResult) => { - // Arrange - const { wrapper } = setupMocks({}); + [fmgPageValues.MOLDING_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, true], + ]; + test.each(testCases)( + "%s comes before %s is %s", + (currentPage, nextPage, expectedResult) => { + // Arrange + const { wrapper } = setupMocks({}); - // Act - const result = wrapper.vm.currentPageComesBeforePage(currentPage, nextPage); + // Act + const result = wrapper.vm.currentPageComesBeforePage(currentPage, nextPage); - // Assert - expect(result).toEqual(expectedResult); - }) + // Assert + expect(result).toEqual(expectedResult); + } + ); }); describe("currentPageComesAfterPage", () => { - const testCases = [[fmgPageValues.PART_QUESTIONS, fmgPageValues.VEHICLE_PARTS, false], + const testCases = [ + [fmgPageValues.PART_QUESTIONS, fmgPageValues.VEHICLE_PARTS, false], [fmgPageValues.VEHICLE_PARTS, fmgPageValues.VEHICLE_PARTS, false], [fmgPageValues.QUOTE, fmgPageValues.VEHICLE_PARTS, true], [fmgPageValues.QUOTE, fmgPageValues.QUOTE, false], [fmgPageValues.VEHICLE_PARTS, fmgPageValues.PART_QUESTIONS, true], [fmgPageValues.PART_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, false], - [fmgPageValues.MOLDING_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, false],]; + [fmgPageValues.MOLDING_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, false], + ]; test.each(testCases)("%s comes after %s is %s", (currentPage, nextPage, expectedResult) => { // Arrange const { wrapper } = setupMocks({}); @@ -312,7 +333,7 @@ describe("vehicle-questions-mixin", () => { // Assert expect(result).toEqual(expectedResult); - }) + }); }); describe("setupInitialData", () => { @@ -321,16 +342,16 @@ describe("vehicle-questions-mixin", () => { // Arrange const { wrapper } = setupMocks({}); const glass = { - "name": "Single", - "location": "Windshield", + name: "Single", + location: "Windshield", }; const i = 0; // Act const returnedGlass = await wrapper.vm.setupInitialData(glass, i); - + // Assert - expect(returnedGlass).toMatchObject({"key": "Windshield-Single"}); + expect(returnedGlass).toMatchObject({ key: "Windshield-Single" }); }); }); @@ -338,17 +359,17 @@ describe("vehicle-questions-mixin", () => { test("should return glass with answerData of null", async () => { // Arrange const glass = { - "name": "Single", - "location": "Windshield" + name: "Single", + location: "Windshield", }; const i = 0; const { wrapper } = setupMocks({}); // Act const returnedGlass = await wrapper.vm.setupInitialData(glass, i); - + // Assert - expect(returnedGlass).toMatchObject({"answerData": null}); + expect(returnedGlass).toMatchObject({ answerData: null }); }); }); @@ -356,49 +377,55 @@ describe("vehicle-questions-mixin", () => { test("should return glass with answerResult within answerData", async () => { // Arrange const glass = { - "name": "Single", - "location": "Windshield", - "questions": [ + name: "Single", + location: "Windshield", + questions: [ { - "questionSequence": 1, - "questionText": "Does the rubber seal around your windshield have a chrome strip running through it?", - "answers": [ + questionSequence: 1, + questionText: + "Does the rubber seal around your windshield have a chrome strip running through it?", + answers: [ { - "answerResult": "WKT D1106 C", - "answerText": "Yes", - "nextQuestionSequence": null + answerResult: "WKT D1106 C", + answerText: "Yes", + nextQuestionSequence: null, }, { - "answerResult": "WKT D1106 B", - "answerText": "No", - "nextQuestionSequence": null - } - ] - } + answerResult: "WKT D1106 B", + answerText: "No", + nextQuestionSequence: null, + }, + ], + }, ], }; const i = 0; const alreadyAnsweredQuestions = [ { - "location": "Windshield", - "name": "Single", - "partNum": "WKT D1106 C", - "answeredQuestions": [ + location: "Windshield", + name: "Single", + partNum: "WKT D1106 C", + answeredQuestions: [ { - "questionText": "Does the rubber seal around your windshield have a chrome strip running through it?", - "selectedAnswerText": "Yes", - "questionNum": 1 - } - ] - } + questionText: + "Does the rubber seal around your windshield have a chrome strip running through it?", + selectedAnswerText: "Yes", + questionNum: 1, + }, + ], + }, ]; const { wrapper } = setupMocks({}); // Act - const returnedGlass = await wrapper.vm.setupInitialData(glass, i, alreadyAnsweredQuestions); - + const returnedGlass = await wrapper.vm.setupInitialData( + glass, + i, + alreadyAnsweredQuestions + ); + // Assert - expect(returnedGlass.answerData).toMatchObject({"answerResult": "WKT D1106 C"}); + expect(returnedGlass.answerData).toMatchObject({ answerResult: "WKT D1106 C" }); }); }); }); @@ -409,32 +436,33 @@ describe("vehicle-questions-mixin", () => { // Arrange const partsOrQuestions = [ { - "name": "Single", - "location": "Windshield", - "parts": null, - "partQuestions": [ + name: "Single", + location: "Windshield", + parts: null, + partQuestions: [ { - "questionSequence": 1, - "questionText": "Is there a line running across the bottom, driver's side then up the center of the Windshield?", - "answers": [ + questionSequence: 1, + questionText: + "Is there a line running across the bottom, driver's side then up the center of the Windshield?", + answers: [ { - "answerText": "Yes", - "nextQuestionSequence": null, - "answerResult": "DW01144" + answerText: "Yes", + nextQuestionSequence: null, + answerResult: "DW01144", }, { - "answerText": "No", - "nextQuestionSequence": null, - "answerResult": "DW01143" - } - ] - } - ] - } + answerText: "No", + nextQuestionSequence: null, + answerResult: "DW01143", + }, + ], + }, + ], + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -442,107 +470,114 @@ describe("vehicle-questions-mixin", () => { // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.HAS_PART_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions }); + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.HAS_PART_QUESTIONS, + wrapper.vm.$route, + {}, + {}, + { partsOrQuestions } + ); }); test("multiple glass locations have part questions => go to parts-questions", async () => { // Arrange const partsOrQuestions = [ { - "name": "Single", - "location": "Windshield", - "parts": null, - "partQuestions": [ + name: "Single", + location: "Windshield", + parts: null, + partQuestions: [ { - "questionSequence": 1, - "questionText": "Is there a line running across the bottom, driver's side then up the center of the Windshield?", - "answers": [ + questionSequence: 1, + questionText: + "Is there a line running across the bottom, driver's side then up the center of the Windshield?", + answers: [ { - "answerText": "Yes", - "nextQuestionSequence": null, - "answerResult": "DW01144" + answerText: "Yes", + nextQuestionSequence: null, + answerResult: "DW01144", }, { - "answerText": "No", - "nextQuestionSequence": null, - "answerResult": "DW01143" - } - ] - } - ] - }, - { - "name": "Front", - "location": "Driver", - "parts": [ - { - "partNumber": "DD08158GTYN", - "description": "driver side, front", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + answerText: "No", + nextQuestionSequence: null, + answerResult: "DW01143", + }, + ], + }, ], - "partQuestions": null }, { - "name": "Quarter", - "location": "Driver", - "parts": [ + name: "Front", + location: "Driver", + parts: [ { - "partNumber": "DQ08162GTYN", - "description": "driver side, 1 hole", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DD08158GTYN", + description: "driver side, front", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "SideDoor", - "location": "Driver", - "parts": null, - "partQuestions": [ + name: "Quarter", + location: "Driver", + parts: [ { - "questionSequence": 2, - "questionText": "Is this a super awesome question?", - "answers": [ + partNumber: "DQ08162GTYN", + description: "driver side, 1 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, + ], + partQuestions: null, + }, + { + name: "SideDoor", + location: "Driver", + parts: null, + partQuestions: [ + { + questionSequence: 2, + questionText: "Is this a super awesome question?", + answers: [ { - "answerText": "Yes", - "nextQuestionSequence": null, - "answerResult": "DW01144000" + answerText: "Yes", + nextQuestionSequence: null, + answerResult: "DW01144000", }, { - "answerText": "Super yes", - "nextQuestionSequence": null, - "answerResult": "DW01143001" - } - ] - } - ] + answerText: "Super yes", + nextQuestionSequence: null, + answerResult: "DW01143001", + }, + ], + }, + ], }, { - "name": "Stationary", - "location": "Rear", - "parts": [ + name: "Stationary", + location: "Rear", + parts: [ { - "partNumber": "DB08165GTNN", - "description": "heated glass, stationary", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DB08165GTNN", + description: "heated glass, stationary", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null - } + partQuestions: null, + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -550,99 +585,106 @@ describe("vehicle-questions-mixin", () => { // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.HAS_PART_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions }); + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.HAS_PART_QUESTIONS, + wrapper.vm.$route, + {}, + {}, + { partsOrQuestions } + ); }); test("multiple glass locations selected, one has part question => go to parts-questions", async () => { // Arrange const partsOrQuestions = [ { - "name": "Single", - "location": "Windshield", - "parts": null, - "partQuestions": [ + name: "Single", + location: "Windshield", + parts: null, + partQuestions: [ { - "questionSequence": 1, - "questionText": "Is there a line running across the bottom, driver's side then up the center of the Windshield?", - "answers": [ + questionSequence: 1, + questionText: + "Is there a line running across the bottom, driver's side then up the center of the Windshield?", + answers: [ { - "answerText": "Yes", - "nextQuestionSequence": null, - "answerResult": "DW01144" + answerText: "Yes", + nextQuestionSequence: null, + answerResult: "DW01144", }, { - "answerText": "No", - "nextQuestionSequence": null, - "answerResult": "DW01143" - } - ] - } - ] + answerText: "No", + nextQuestionSequence: null, + answerResult: "DW01143", + }, + ], + }, + ], }, { - "name": "Front", - "location": "Driver", - "parts": [ + name: "Front", + location: "Driver", + parts: [ { - "partNumber": "DD08158GTYN", - "description": "driver side, front", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DD08158GTYN", + description: "driver side, front", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Quarter", - "location": "Driver", - "parts": [ + name: "Quarter", + location: "Driver", + parts: [ { - "partNumber": "DQ08162GTYN", - "description": "driver side, 1 hole", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DQ08162GTYN", + description: "driver side, 1 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "SideDoor", - "location": "Driver", - "parts": [ + name: "SideDoor", + location: "Driver", + parts: [ { - "partNumber": "DD08160GTYN", - "description": "driver side, body side, 1 hole", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DD08160GTYN", + description: "driver side, body side, 1 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Stationary", - "location": "Rear", - "parts": [ + name: "Stationary", + location: "Rear", + parts: [ { - "partNumber": "DB08165GTNN", - "description": "heated glass, stationary", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DB08165GTNN", + description: "heated glass, stationary", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null - } + partQuestions: null, + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -650,147 +692,154 @@ describe("vehicle-questions-mixin", () => { // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.HAS_PART_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions }); + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.HAS_PART_QUESTIONS, + wrapper.vm.$route, + {}, + {}, + { partsOrQuestions } + ); }); test("a selected glass location has part questions and multiple parts => go to parts-questions", async () => { // Arrange const partsOrQuestions = [ { - "name": "Single", - "location": "Windshield", - "parts": null, - "partQuestions": [ + name: "Single", + location: "Windshield", + parts: null, + partQuestions: [ { - "questionSequence": 1, - "questionText": "Is there a line running across the bottom, driver's side then up the center of the Windshield?", - "answers": [ + questionSequence: 1, + questionText: + "Is there a line running across the bottom, driver's side then up the center of the Windshield?", + answers: [ { - "answerText": "Yes", - "nextQuestionSequence": null, - "answerResult": "DW01144" + answerText: "Yes", + nextQuestionSequence: null, + answerResult: "DW01144", }, { - "answerText": "No", - "nextQuestionSequence": null, - "answerResult": "DW01143" - } - ] - } - ] + answerText: "No", + nextQuestionSequence: null, + answerResult: "DW01143", + }, + ], + }, + ], }, { - "name": "Front", - "location": "Driver", - "parts": [ + name: "Front", + location: "Driver", + parts: [ { - "partNumber": "DD08158GTYN", - "description": "driver side, front", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DD08158GTYN", + description: "driver side, front", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Quarter", - "location": "Driver", - "parts": [ + name: "Quarter", + location: "Driver", + parts: [ { - "partNumber": "DQ08162GTYN", - "description": "driver side, 1 hole", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DQ08162GTYN", + description: "driver side, 1 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DQ08162YPYN", - "description": "driver side, 1 hole", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DQ08162YPYN", + description: "driver side, 1 hole", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "SideDoor", - "location": "Driver", - "parts": [ + name: "SideDoor", + location: "Driver", + parts: [ { - "partNumber": "DD08160GTYN", - "description": "driver side, body side, 1 hole", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DD08160GTYN", + description: "driver side, body side, 1 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DD08160YPYN", - "description": "driver side, body side, 1 hole", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DD08160YPYN", + description: "driver side, body side, 1 hole", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Stationary", - "location": "Rear", - "parts": [ + name: "Stationary", + location: "Rear", + parts: [ { - "partNumber": "DB08165GTNN", - "description": "heated glass, stationary", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DB08165GTNN", + description: "heated glass, stationary", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DB08165YPNN", - "description": "heated glass, stationary", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DB08165YPNN", + description: "heated glass, stationary", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DB08166GTNN", - "description": "stationary", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DB08166GTNN", + description: "stationary", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DB08167GTNN", - "description": "heated glass, movable, 8 hole", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DB08167GTNN", + description: "heated glass, movable, 8 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DB08167YPNN", - "description": "heated glass, movable, 8 hole", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DB08167YPNN", + description: "heated glass, movable, 8 hole", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null - } + partQuestions: null, + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -798,7 +847,13 @@ describe("vehicle-questions-mixin", () => { // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.HAS_PART_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions }); + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.HAS_PART_QUESTIONS, + wrapper.vm.$route, + {}, + {}, + { partsOrQuestions } + ); }); }); @@ -807,32 +862,32 @@ describe("vehicle-questions-mixin", () => { // Arrange const partsOrQuestions = [ { - "name": "Stationary", - "location": "Rear", - "parts": [ + name: "Stationary", + location: "Rear", + parts: [ { - "partNumber": "FB25724GTYN", - "description": "heated glass, solar, antenna", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "FB25724GTYN", + description: "heated glass, solar, antenna", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "FB25759GTYN", - "description": "heated glass, solar, antenna, w/diversity antenna", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "FB25759GTYN", + description: "heated glass, solar, antenna, w/diversity antenna", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null - } + partQuestions: null, + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -840,105 +895,111 @@ describe("vehicle-questions-mixin", () => { // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, wrapper.vm.$route, {}, {}, { partsOrQuestions }); + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, + wrapper.vm.$route, + {}, + {}, + { partsOrQuestions } + ); }); test("multiple glass locations selected, one of them has multiple parts => go to vehicle parts", async () => { // Arrange const partsOrQuestions = [ { - "name": "Single", - "location": "Windshield", - "parts": [ + name: "Single", + location: "Windshield", + parts: [ { - "partNumber": "FW03647GTNN", - "description": "solar, 3rd visor band", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": [ + partNumber: "FW03647GTNN", + description: "solar, 3rd visor band", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: [ { - "partNumber": "MWF03647", - "partType": "MOULDING", - "description": "Upper " - } - ] - } + partNumber: "MWF03647", + partType: "MOULDING", + description: "Upper ", + }, + ], + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Back", - "location": "Driver", - "parts": [ + name: "Back", + location: "Driver", + parts: [ { - "partNumber": "FD25747GTYN", - "description": "solar, driver side, rear, ex models and above", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "FD25747GTYN", + description: "solar, driver side, rear, ex models and above", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Front", - "location": "Driver", - "parts": [ + name: "Front", + location: "Driver", + parts: [ { - "partNumber": "FD25719GTYN", - "description": "solar, driver side, front, ex models and above", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "FD25719GTYN", + description: "solar, driver side, front, ex models and above", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Vent", - "location": "Driver", - "parts": [ + name: "Vent", + location: "Driver", + parts: [ { - "partNumber": "FV25749GTNN", - "description": "solar, driver side, rear", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "FV25749GTNN", + description: "solar, driver side, rear", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Stationary", - "location": "Rear", - "parts": [ + name: "Stationary", + location: "Rear", + parts: [ { - "partNumber": "FB25724GTYN", - "description": "heated glass, solar, antenna", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "FB25724GTYN", + description: "heated glass, solar, antenna", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "FB25759GTYN", - "description": "heated glass, solar, antenna, w/diversity antenna", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "FB25759GTYN", + description: "heated glass, solar, antenna, w/diversity antenna", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null - } + partQuestions: null, + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -946,171 +1007,177 @@ describe("vehicle-questions-mixin", () => { // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, wrapper.vm.$route, {}, {}, { partsOrQuestions }); + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, + wrapper.vm.$route, + {}, + {}, + { partsOrQuestions } + ); }); test("multiple glass locations selected, multiple have multiple parts => go to vehicle-parts", async () => { // Arrange const partsOrQuestions = [ { - "name": "Single", - "location": "Windshield", - "parts": [ + name: "Single", + location: "Windshield", + parts: [ { - "partNumber": "DW02101GTYN", - "description": "solar", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DW02101GTYN", + description: "solar", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Back", - "location": "Driver", - "parts": [ + name: "Back", + location: "Driver", + parts: [ { - "partNumber": "DD12202GTYN", - "description": "solar, driver side, rear", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DD12202GTYN", + description: "solar, driver side, rear", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DD12202YPYN", - "description": "solar, driver side, rear", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DD12202YPYN", + description: "solar, driver side, rear", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Front", - "location": "Driver", - "parts": [ + name: "Front", + location: "Driver", + parts: [ { - "partNumber": "DD12198GTYN", - "description": "solar, driver side, front", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DD12198GTYN", + description: "solar, driver side, front", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DD12200GTYN", - "description": "solar, driver side, front, laminated, soundproofing", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DD12200GTYN", + description: "solar, driver side, front, laminated, soundproofing", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Quarter", - "location": "Driver", - "parts": [ + name: "Quarter", + location: "Driver", + parts: [ { - "partNumber": "DQ12204GTYNOEM", - "description": "solar, driver side, encap", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DQ12204GTYNOEM", + description: "solar, driver side, encap", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DQ12204YPYNOEM", - "description": "solar, driver side, encap", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DQ12204YPYNOEM", + description: "solar, driver side, encap", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DQ12205GTYNOEM", - "description": "solar, antenna, driver side, encap", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DQ12205GTYNOEM", + description: "solar, antenna, driver side, encap", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DQ12205YPYNOEM", - "description": "solar, antenna, driver side, encap", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DQ12205YPYNOEM", + description: "solar, antenna, driver side, encap", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DQ12207GTYN", - "description": "solar, driver side, encap, chrome molding", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DQ12207GTYN", + description: "solar, driver side, encap, chrome molding", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DQ12207YPYNOEM", - "description": "solar, driver side, encap, chrome molding", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DQ12207YPYNOEM", + description: "solar, driver side, encap, chrome molding", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DQ12208GTYNOEM", - "description": "solar, antenna, driver side, encap, chrome molding", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DQ12208GTYNOEM", + description: "solar, antenna, driver side, encap, chrome molding", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DQ12208YPYNOEM", - "description": "solar, antenna, driver side, encap, chrome molding", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DQ12208YPYNOEM", + description: "solar, antenna, driver side, encap, chrome molding", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Stationary", - "location": "Rear", - "parts": [ + name: "Stationary", + location: "Rear", + parts: [ { - "partNumber": "DB12209GTYN", - "description": "heated glass, solar, 1 hole", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null + partNumber: "DB12209GTYN", + description: "heated glass, solar, 1 hole", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, }, { - "partNumber": "DB12209YPYN", - "description": "heated glass, solar, 1 hole", - "color": "Gray Tint Privacy", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null - } + partNumber: "DB12209YPYN", + description: "heated glass, solar, 1 hole", + color: "Gray Tint Privacy", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + }, ], - "partQuestions": null - } + partQuestions: null, + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -1118,7 +1185,13 @@ describe("vehicle-questions-mixin", () => { // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, wrapper.vm.$route, {}, {}, { partsOrQuestions }); + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, + wrapper.vm.$route, + {}, + {}, + { partsOrQuestions } + ); }); }); @@ -1127,43 +1200,43 @@ describe("vehicle-questions-mixin", () => { // Arrange const partsOrQuestions = [ { - "name": "Stationary", - "location": "Rear", - "parts": [ + name: "Stationary", + location: "Rear", + parts: [ { - "partNumber": "FB25724GTYN", - "description": "heated glass, solar, antenna", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null, - "childPartQuestions": [ + partNumber: "FB25724GTYN", + description: "heated glass, solar, antenna", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + childPartQuestions: [ { - "questionSequence": 1, - "questionText": "Does the rubber seal around your windshield have a chrome strip running through it?", - "answers": [ + questionSequence: 1, + questionText: + "Does the rubber seal around your windshield have a chrome strip running through it?", + answers: [ { - "answerResult": "WKT D1106 C", - "answerText": "Yes", - "nextQuestionSequence": null + answerResult: "WKT D1106 C", + answerText: "Yes", + nextQuestionSequence: null, }, { - "answerResult": "WKT D1106 B", - "answerText": "No", - "nextQuestionSequence": null - } - ] - } - ] - - } + answerResult: "WKT D1106 B", + answerText: "No", + nextQuestionSequence: null, + }, + ], + }, + ], + }, ], - "partQuestions": null - } + partQuestions: null, + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -1171,7 +1244,13 @@ describe("vehicle-questions-mixin", () => { // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.HAS_MOLDING_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions }); + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.HAS_MOLDING_QUESTIONS, + wrapper.vm.$route, + {}, + {}, + { partsOrQuestions } + ); }); }); @@ -1180,26 +1259,26 @@ describe("vehicle-questions-mixin", () => { // Arrange const partsOrQuestions = [ { - "name": "Single", - "location": "Windshield", - "parts": [ + name: "Single", + location: "Windshield", + parts: [ { - "partNumber": "FB25724GTYN", - "description": "heated glass, solar, antenna", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": true, - "childParts": null, - "childPartQuestions": null - } + partNumber: "FB25724GTYN", + description: "heated glass, solar, antenna", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: true, + childParts: null, + childPartQuestions: null, + }, ], - "capabilityQuestions": [], - "partQuestions": null - } + capabilityQuestions: [], + partQuestions: null, + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -1207,7 +1286,13 @@ describe("vehicle-questions-mixin", () => { // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(navigationScenarios.HAS_CAPABILITY_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions }); + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( + navigationScenarios.HAS_CAPABILITY_QUESTIONS, + wrapper.vm.$route, + {}, + {}, + { partsOrQuestions } + ); }); }); @@ -1216,30 +1301,30 @@ describe("vehicle-questions-mixin", () => { // Arrange const partsOrQuestions = [ { - "name": "Single", - "location": "Windshield", - "parts": [ + name: "Single", + location: "Windshield", + parts: [ { - "partNumber": "FW04186GTYN", - "description": "solar, soundproofing, lane keep assist", - "color": "Green Tint", - "requiresRecalibration": true, - "requiresCapabilityQuestions": false, - "childParts": [ + partNumber: "FW04186GTYN", + description: "solar, soundproofing, lane keep assist", + color: "Green Tint", + requiresRecalibration: true, + requiresCapabilityQuestions: false, + childParts: [ { - "partNumber": "GGG 3563 KIT", - "partType": "MOULDING", - "description": "Kit, Top & Sides " - } - ] - } + partNumber: "GGG 3563 KIT", + partType: "MOULDING", + description: "Kit, Top & Sides ", + }, + ], + }, ], - "partQuestions": null - } - ] + partQuestions: null, + }, + ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); // Act @@ -1253,90 +1338,90 @@ describe("vehicle-questions-mixin", () => { // Arrange const partsOrQuestions = [ { - "name": "Single", - "location": "Windshield", - "parts": [ + name: "Single", + location: "Windshield", + parts: [ { - "partNumber": "FW04186GTYN", - "description": "solar, soundproofing, lane keep assist", - "color": "Green Tint", - "requiresRecalibration": true, - "requiresCapabilityQuestions": false, - "childParts": [ + partNumber: "FW04186GTYN", + description: "solar, soundproofing, lane keep assist", + color: "Green Tint", + requiresRecalibration: true, + requiresCapabilityQuestions: false, + childParts: [ { - "partNumber": "GGG 3563 KIT", - "partType": "MOULDING", - "description": "Kit, Top & Sides " - } - ] - } + partNumber: "GGG 3563 KIT", + partType: "MOULDING", + description: "Kit, Top & Sides ", + }, + ], + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Back", - "location": "Driver", - "parts": [ + name: "Back", + location: "Driver", + parts: [ { - "partNumber": "FD25457GTYN", - "description": "solar, driver side, rear", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": false, - "childParts": null - } + partNumber: "FD25457GTYN", + description: "solar, driver side, rear", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Front", - "location": "Driver", - "parts": [ + name: "Front", + location: "Driver", + parts: [ { - "partNumber": "FD27090GTYN", - "description": "solar, driver side, front", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": false, - "childParts": null - } + partNumber: "FD27090GTYN", + description: "solar, driver side, front", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Vent", - "location": "Driver", - "parts": [ + name: "Vent", + location: "Driver", + parts: [ { - "partNumber": "FV25459GTNN", - "description": "solar, driver side, rear", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": false, - "childParts": null - } + partNumber: "FV25459GTNN", + description: "solar, driver side, rear", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null, + }, ], - "partQuestions": null + partQuestions: null, }, { - "name": "Stationary", - "location": "Rear", - "parts": [ + name: "Stationary", + location: "Rear", + parts: [ { - "partNumber": "FB25460GTYN", - "description": "heated glass, solar", - "color": "Green Tint", - "requiresRecalibration": false, - "requiresCapabilityQuestions": false, - "childParts": null - } + partNumber: "FB25460GTYN", + description: "heated glass, solar", + color: "Green Tint", + requiresRecalibration: false, + requiresCapabilityQuestions: false, + childParts: null, + }, ], - "partQuestions": null - } + partQuestions: null, + }, ]; const { wrapper } = setupMocks({ - partsOrQuestions: partsOrQuestions + partsOrQuestions: partsOrQuestions, }); wrapper.vm.$store.commit = jest.fn(); @@ -1346,10 +1431,12 @@ describe("vehicle-questions-mixin", () => { // Act await wrapper.vm.navigateForward(partsOrQuestions); - // Assert expect(wrapper.vm.$store.commit).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$store.commit).toHaveBeenCalledWith(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts) + expect(wrapper.vm.$store.commit).toHaveBeenCalledWith( + storeMutations.UPDATE_GLASS_PARTS, + collectedGlassParts + ); expect(wrapper.vm.$refs.loadingModal.showModal).toHaveBeenCalledTimes(1); expect(navigateToHeritageFunnel).toHaveBeenCalledTimes(1); }); @@ -1365,8 +1452,11 @@ describe("vehicle-questions-mixin", () => { wrapper.vm.backButtonAction(); // Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, {"query": {"fmgPage": fmgPageValues.QUOTE}}) - }) + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith( + navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, + { query: { fmgPage: fmgPageValues.QUOTE } } + ); + }); test("current page is quote and there are capability questions => go to capability questions", () => { // Arrange @@ -1377,8 +1467,11 @@ describe("vehicle-questions-mixin", () => { wrapper.vm.backButtonAction(); // Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_CAPABILITY_QUESTIONS, {"query": {"fmgPage": fmgPageValues.QUOTE}}) - }) + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith( + navigationScenarios.CLICKED_BACK_WITH_CAPABILITY_QUESTIONS, + { query: { fmgPage: fmgPageValues.QUOTE } } + ); + }); test("current page is quote and there are part questions and molding questions => go to molding questions", () => { // Arrange @@ -1390,8 +1483,11 @@ describe("vehicle-questions-mixin", () => { wrapper.vm.backButtonAction(); // Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS, {"query": {"fmgPage": fmgPageValues.QUOTE}}) - }) + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith( + navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS, + { query: { fmgPage: fmgPageValues.QUOTE } } + ); + }); test("current page is molding questions and there are part questions, multiple parts to choose, and capability questions => go to vehicle-parts", () => { // Arrange @@ -1405,8 +1501,11 @@ describe("vehicle-questions-mixin", () => { wrapper.vm.backButtonAction(); // Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE, {"query": {"fmgPage": fmgPageValues.MOLDING_QUESTIONS}}) - }) + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith( + navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE, + { query: { fmgPage: fmgPageValues.MOLDING_QUESTIONS } } + ); + }); test("current page is molding questions and there are part questions and capability questions => go to part-questions", () => { // Arrange @@ -1420,41 +1519,48 @@ describe("vehicle-questions-mixin", () => { wrapper.vm.backButtonAction(); // Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, {"query": {"fmgPage": fmgPageValues.MOLDING_QUESTIONS}}) - }) + expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith( + navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, + { query: { fmgPage: fmgPageValues.MOLDING_QUESTIONS } } + ); + }); }); }); function setupMocks({ fmgPage = fmgPageValues.VIN_LOOKUP }) { const baseMixin = setupMocksForJsFiles({ - actionList: [{ - actionName: storeActions.GET_CAPABILITY_QUESTIONS, - data: [ - { - answers: [ - { - answerResult1: "testing" - } - ] - } - ], - }] + actionList: [ + { + actionName: storeActions.GET_CAPABILITY_QUESTIONS, + data: [ + { + answers: [ + { + answerResult1: "testing", + }, + ], + }, + ], + }, + ], }); const mocks = getMountOptions({ router: { - navigate: jest.fn(), navigateWithSaving: jest.fn(), navigateWithoutSaving: jest.fn() + navigate: jest.fn(), + navigateWithSaving: jest.fn(), + navigateWithoutSaving: jest.fn(), }, store: { commit: jest.fn(), dispatch: jest.fn(), - getters: getters + getters: getters, }, route: { query: { - fmgPage - } - } + fmgPage, + }, + }, }); // store.dispatch = jest.fn(); @@ -1462,7 +1568,7 @@ function setupMocks({ fmgPage = fmgPageValues.VIN_LOOKUP }) { const mockVehicleQuestionComponent = { components: { loadingModal }, template: '', - mixins: [vehicleQuestionsMixin, baseMixin.baseMixin] + mixins: [vehicleQuestionsMixin, baseMixin.baseMixin], }; const wrapper = shallowMount(mockVehicleQuestionComponent, mocks); @@ -1470,4 +1576,4 @@ function setupMocks({ fmgPage = fmgPageValues.VIN_LOOKUP }) { wrapper.vm.$refs.loadingModal.showModal = jest.fn(); return { wrapper }; -} \ No newline at end of file +} diff --git a/src/mixins/vin-pages-mixin.js b/src/mixins/vin-pages-mixin.js index 00ef6ab05..e6e41aad5 100644 --- a/src/mixins/vin-pages-mixin.js +++ b/src/mixins/vin-pages-mixin.js @@ -10,11 +10,11 @@ export default { if (!store.getters.applicationUser.savedSessionId) { saveSession(); } - + const result = await this.dispatchStoreAction(storeActions.GET_PARTS_OR_QUESTIONS); const partsOrQuestions = result.data.partsOrQuestions; vehicleQuestionsMixin.methods.navigateForward(partsOrQuestions, this); }, - } -} \ No newline at end of file + }, +}; diff --git a/src/mixins/vin-pages-mixin.spec.js b/src/mixins/vin-pages-mixin.spec.js index 13c376c35..340cb16fa 100644 --- a/src/mixins/vin-pages-mixin.spec.js +++ b/src/mixins/vin-pages-mixin.spec.js @@ -2,34 +2,34 @@ import vinPagesMixin from "@/mixins/vin-pages-mixin"; import { shallowMount } from "@vue/test-utils"; import { setupMocksForJsFiles, getMountOptions } from "@/helpers/unit-test-helper.js"; import { storeActions } from "@/constants/store-actions"; -import loadingModal from '@/common-components/loading-modal/loading-modal.vue'; +import loadingModal from "@/common-components/loading-modal/loading-modal.vue"; import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({ - navigateForward: jest.fn() + navigateForward: jest.fn(), })); jest.mock("@/helpers/heritage-integration/order-helper.js", () => ({ - saveSession: jest.fn() + saveSession: jest.fn(), })); describe("vin-pages-mixin", () => { afterEach(() => { jest.clearAllMocks(); - }) + }); describe("navigateForwardWithSingleCarMatch", () => { test("should navigateForward", async () => { // Arrange const { wrapper } = setupMocks({}); vehicleQuestionsMixin.methods.navigateForward = jest.fn(); - + // Act await wrapper.vm.navigateForwardWithSingleCarMatch(); - // Assert + // Assert expect(vehicleQuestionsMixin.methods.navigateForward).toHaveBeenCalled(); - }) + }); }); }); @@ -39,30 +39,32 @@ function setupMocks({ partsOrQuestions = [] }) { { actionName: storeActions.GET_PARTS_OR_QUESTIONS, data: { - partsOrQuestions: partsOrQuestions - } + partsOrQuestions: partsOrQuestions, + }, }, ], }); const mocks = getMountOptions({ router: { - navigate: jest.fn(), navigateWithSaving: jest.fn(), navigateWithoutSaving: jest.fn(), + navigate: jest.fn(), + navigateWithSaving: jest.fn(), + navigateWithoutSaving: jest.fn(), }, store: { commit: jest.fn(), getters: { applicationUser: { - savedSessionId: 1 - } - } - } + savedSessionId: 1, + }, + }, + }, }); const mockVinComponent = { components: { loadingModal }, template: '', - mixins: [vinPagesMixin, baseMixin.baseMixin] + mixins: [vinPagesMixin, baseMixin.baseMixin], }; const wrapper = shallowMount(mockVinComponent, mocks); @@ -70,4 +72,4 @@ function setupMocks({ partsOrQuestions = [] }) { wrapper.vm.$refs.loadingModal.showModal = jest.fn(); return { wrapper }; -} \ No newline at end of file +} diff --git a/src/router/dynamic-routing/component-loader.js b/src/router/dynamic-routing/component-loader.js index fc8a02427..5e25fb2ce 100644 --- a/src/router/dynamic-routing/component-loader.js +++ b/src/router/dynamic-routing/component-loader.js @@ -1,3 +1,3 @@ export function lazyLoadComponent(componentName) { - return () => import(`@/layouts/${componentName}/${componentName}.vue`); // /src/layouts/folder/component.vue + return () => import(`@/layouts/${componentName}/${componentName}.vue`); // /src/layouts/folder/component.vue } diff --git a/src/router/index.js b/src/router/index.js index a11dae75c..9ab27a8ac 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -10,9 +10,16 @@ import { getDeviceIdValue } from "@/helpers/heritage-integration/cookie-helper"; // Heritage integration import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper"; -import { updateOrCreateFunnelCookie, getFunnelCookie, updateSessionIdCookie } from "@/helpers/heritage-integration/cookie-helper"; +import { + updateOrCreateFunnelCookie, + getFunnelCookie, + updateSessionIdCookie, +} from "@/helpers/heritage-integration/cookie-helper"; import { loadSessionIfPresent, saveSession } from "@/helpers/heritage-integration/order-helper"; -import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; +import { + getPageToRouteExistingOrderTo, + navigateToHeritageFunnel, +} from "@/helpers/heritage-integration/navigation-helper"; import baseMixin from "@/mixins/base-mixin"; import eventBus from "@/helpers/event-bus/event-bus"; @@ -26,286 +33,334 @@ import quote from "@/layouts/quote/quote.vue"; import datePicker from "@/common-components/date-picker/date-picker.vue"; const routes = [ - { - path: "/quote", // This is a temporary route for testing. - name: "quote", - component: quote, - }, - { - path: "/date-picker", // This is a temporary route for testing. - name: "date-picker", - component: datePicker, - }, - { - path: "/", - name: "root", - async beforeEnter(to, from, next) { - // If we have no query string, or we don't have the FmgPage query string. - try { - if (analyticsMixin.methods.noSession()) { - await analyticsMixin.methods.initSession(); - } - else { - updateSessionIdCookie(); - } - - if (getFunnelCookie()?.SuppressConceptFunnel) { - await navigateToHeritageFunnel(false); - return next(false); - } - - // If the saved session has timed out, clear the session, execute 404 logic. - if (getFunnelCookie() !== null && !isSavedSessionStillActive()) { - await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE); - await GoToFunnelStartOn404(next); - } - - // On entering the funnel "fresh", read cookie information, decide what to do next. - if (from.redirectedFrom === undefined) { - // clear the saveSessionPromise - if it exists in the vuex store but a new instance was created - // the saveSessionPromise will no longer point to a valid promise - baseMixin.methods.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); - const loadSessionResponse = await loadSessionIfPresent(); - const pageToRedirectTo = await getPageToRouteExistingOrderTo(to, loadSessionResponse); - - // If getPageToRouteExistingOrderTo determines that the return user needs to - // go back to heritage funnel, send them there and stop our current navigation. - if (pageToRedirectTo === 'heritage') { - await navigateToHeritageFunnel(); - return next(false); - } - - // Assign our fmgPage so it will load normally like the other pages. - to.query.fmgPage = pageToRedirectTo; - } - - await runExperiments(to.query.fmgPage); - - // Process funnel cookie. - updateOrCreateFunnelCookie(); - - // If we already have our route, go to it. - if (router.hasRoute(to.query.fmgPage)) { - // Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function. - let component = router.getRoutes().filter((x) => x.name === to.query.fmgPage)[0].components; - - // If the component hasn't been loaded fully, load it before we check prerequisites. - if (component.default.methods === undefined) { - component = await component.default(); - } - - if (!arePagePrerequisitesValid(component)) { - await GoToFunnelStartOn404(next); - } - - return next({ name: to.query.fmgPage, query: to.query, params: to.params }); - } - - // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms. - const routeData = await GetRouteInfoFromPageName(to.query.fmgPage); - - // Add our dynamic route. - router.addRoute({ - path: routeData[0].path, // Always the same path, because we control it with query strings. - name: routeData[0].name, - component: routeData[0].component, - }); - - // Call the next components arePagePrerequisitesValid method before load. - // If it returns false, use the 404 logic. - const nextComponent = await router.getRoutes().filter((x) => x.name === routeData[0].name)[0].components.default(); - - if (!arePagePrerequisitesValid(nextComponent)) { - await GoToFunnelStartOn404(next); - } - - // Assign current query string parameters, as well as our fmgPage one. - next({ - name: routeData[0].name, - query: Object.assign(to.query, { fmgPage: routeData[0].name }), - params: to.params - }); - } catch (error) { - console.log(error); - - // If we don't have a route, go to our 404 page. - await GoToFunnelStartOn404(next); - } + { + path: "/quote", // This is a temporary route for testing. + name: "quote", + component: quote, + }, + { + path: "/date-picker", // This is a temporary route for testing. + name: "date-picker", + component: datePicker, + }, + { + path: "/", + name: "root", + async beforeEnter(to, from, next) { + // If we have no query string, or we don't have the FmgPage query string. + try { + if (analyticsMixin.methods.noSession()) { + await analyticsMixin.methods.initSession(); + } else { + updateSessionIdCookie(); + } + + if (getFunnelCookie()?.SuppressConceptFunnel) { + await navigateToHeritageFunnel(false); + return next(false); + } + + // If the saved session has timed out, clear the session, execute 404 logic. + if (getFunnelCookie() !== null && !isSavedSessionStillActive()) { + await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE); + await GoToFunnelStartOn404(next); + } + + // On entering the funnel "fresh", read cookie information, decide what to do next. + if (from.redirectedFrom === undefined) { + // clear the saveSessionPromise - if it exists in the vuex store but a new instance was created + // the saveSessionPromise will no longer point to a valid promise + baseMixin.methods.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE); + const loadSessionResponse = await loadSessionIfPresent(); + const pageToRedirectTo = await getPageToRouteExistingOrderTo( + to, + loadSessionResponse + ); + + // If getPageToRouteExistingOrderTo determines that the return user needs to + // go back to heritage funnel, send them there and stop our current navigation. + if (pageToRedirectTo === "heritage") { + await navigateToHeritageFunnel(); + return next(false); + } + + // Assign our fmgPage so it will load normally like the other pages. + to.query.fmgPage = pageToRedirectTo; + } + + await runExperiments(to.query.fmgPage); + + // Process funnel cookie. + updateOrCreateFunnelCookie(); + + // If we already have our route, go to it. + if (router.hasRoute(to.query.fmgPage)) { + // Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function. + let component = router + .getRoutes() + .filter((x) => x.name === to.query.fmgPage)[0].components; + + // If the component hasn't been loaded fully, load it before we check prerequisites. + if (component.default.methods === undefined) { + component = await component.default(); + } + + if (!arePagePrerequisitesValid(component)) { + await GoToFunnelStartOn404(next); + } + + return next({ name: to.query.fmgPage, query: to.query, params: to.params }); + } + + // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms. + const routeData = await GetRouteInfoFromPageName(to.query.fmgPage); + + // Add our dynamic route. + router.addRoute({ + path: routeData[0].path, // Always the same path, because we control it with query strings. + name: routeData[0].name, + component: routeData[0].component, + }); + + // Call the next components arePagePrerequisitesValid method before load. + // If it returns false, use the 404 logic. + const nextComponent = await router + .getRoutes() + .filter((x) => x.name === routeData[0].name)[0] + .components.default(); + + if (!arePagePrerequisitesValid(nextComponent)) { + await GoToFunnelStartOn404(next); + } + + // Assign current query string parameters, as well as our fmgPage one. + next({ + name: routeData[0].name, + query: Object.assign(to.query, { fmgPage: routeData[0].name }), + params: to.params, + }); + } catch (error) { + console.log(error); + + // If we don't have a route, go to our 404 page. + await GoToFunnelStartOn404(next); + } + }, }, - }, ]; const router = createRouter({ - history: createWebHistory("/fmg/"), - routes, + history: createWebHistory("/fmg/"), + routes, }); //---------------------------------------------------------- Router Functions ---------------------------------------------------------- router.afterEach((to, from) => { - // Update lastPageVisited in the store - store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name); + // Update lastPageVisited in the store + store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name); - // If saving on navigation is requested, check for saved SessionId or EmailAddress to determine if saving is appropriate - if (eval(to.params.isSavingNavigation)) { - if (store.getters.applicationUser.savedSessionId || store.getters.order.customer?.emailAddress) { - saveSession(); + // If saving on navigation is requested, check for saved SessionId or EmailAddress to determine if saving is appropriate + if (eval(to.params.isSavingNavigation)) { + if ( + store.getters.applicationUser.savedSessionId || + store.getters.order.customer?.emailAddress + ) { + saveSession(); + } } - } - // Push page view to GA - analyticsMixin.methods.pushPageViewToGA(); - - // Push experiments to Data Layer - analyticsMixin.methods.pushExperimentsToDataLayer(); + // Push page view to GA + analyticsMixin.methods.pushPageViewToGA(); + // Push experiments to Data Layer + analyticsMixin.methods.pushExperimentsToDataLayer(); }); -router.navigateWithoutSaving = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => { - navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData); -} +router.navigateWithoutSaving = ( + scenario, + currentRoute, + optionalQuery = {}, + optionalParams = {}, + optionalPageData = {} +) => { + navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData); +}; -router.navigateWithSaving = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => { - navigate(scenario, currentRoute, true, optionalQuery, optionalParams, optionalPageData); -} +router.navigateWithSaving = ( + scenario, + currentRoute, + optionalQuery = {}, + optionalParams = {}, + optionalPageData = {} +) => { + navigate(scenario, currentRoute, true, optionalQuery, optionalParams, optionalPageData); +}; router.navigateToExternalUrl = (url, optionalQuery = {}) => { - navigateToUrl(url, optionalQuery); -} + navigateToUrl(url, optionalQuery); +}; //Use this navigation when you need to call next() explicitly. beforeRouteEnter is a good example. -router.overrideNavigation = (scenario, currentRoute, next, isSavingNavigation, optionalQuery = {}, optionalParams = {}, optionalPageData) => { - navigate(scenario, currentRoute, isSavingNavigation, optionalQuery, optionalParams, optionalPageData); - next(); -} +router.overrideNavigation = ( + scenario, + currentRoute, + next, + isSavingNavigation, + optionalQuery = {}, + optionalParams = {}, + optionalPageData +) => { + navigate( + scenario, + currentRoute, + isSavingNavigation, + optionalQuery, + optionalParams, + optionalPageData + ); + next(); +}; // PRIVATE FUNCTIONS // Navigate to the next route, depending on the scenario. -async function navigate(scenario, currentRoute, isSavingNavigation, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) { - if (!scenario) { - console.error("No scenario provided. Please review the routing table."); - return; - } +async function navigate( + scenario, + currentRoute, + isSavingNavigation, + optionalQuery = {}, + optionalParams = {}, + optionalPageData = {} +) { + if (!scenario) { + console.error("No scenario provided. Please review the routing table."); + return; + } - // Match our maps up and navigate if we have a destination. - const matchingScenarioMap = getNavigationMap(scenario, currentRoute); - const destinationFmgPageValue = matchingScenarioMap.destinationFmgPageValue; + // Match our maps up and navigate if we have a destination. + const matchingScenarioMap = getNavigationMap(scenario, currentRoute); + const destinationFmgPageValue = matchingScenarioMap.destinationFmgPageValue; - if (destinationFmgPageValue !== undefined) { - // We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one. + if (destinationFmgPageValue !== undefined) { + // We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one. - // Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object - const existingPageDataForPage = store.getters.pageData(destinationFmgPageValue); - baseMixin.methods.savePageDataToStore(destinationFmgPageValue, Object.keys(optionalPageData).length > 0 ? optionalPageData : existingPageDataForPage ?? {}); + // Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object + const existingPageDataForPage = store.getters.pageData(destinationFmgPageValue); + baseMixin.methods.savePageDataToStore( + destinationFmgPageValue, + Object.keys(optionalPageData).length > 0 + ? optionalPageData + : existingPageDataForPage ?? {} + ); - optionalParams.isSavingNavigation = isSavingNavigation; + optionalParams.isSavingNavigation = isSavingNavigation; - router.push({ - name: "root", - query: Object.assign(optionalQuery, { - fmgPage: destinationFmgPageValue, - }), - params: optionalParams - }); - } else if (matchingScenarioMap.destinationUrl !== undefined) { - navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery); - } + router.push({ + name: "root", + query: Object.assign(optionalQuery, { + fmgPage: destinationFmgPageValue, + }), + params: optionalParams, + }); + } else if (matchingScenarioMap.destinationUrl !== undefined) { + navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery); + } } // Get navigation map depending on the scenario and the current 'page' you're on. function getNavigationMap(scenario, currentRoute) { - const fmgPageValue = currentRoute.query.fmgPage; - const matchedQueryValue = routingTable(store) - .filter( - (item) => - item.fmgPageValue === fmgPageValue && - item.maps.filter((map) => map.scenario === scenario).length > 0 - ) - .map((m) => m.maps.filter((map) => map.scenario === scenario))[0] - .filter(x => x.filter === true || x.filter === undefined); + const fmgPageValue = currentRoute.query.fmgPage; + const matchedQueryValue = routingTable(store) + .filter( + (item) => + item.fmgPageValue === fmgPageValue && + item.maps.filter((map) => map.scenario === scenario).length > 0 + ) + .map((m) => m.maps.filter((map) => map.scenario === scenario))[0] + .filter((x) => x.filter === true || x.filter === undefined); - return matchedQueryValue[0]; + return matchedQueryValue[0]; } //---------------------------------------------------------- Private Functions ---------------------------------------------------------- // Navigate to an external url. function navigateToUrl(url, optionalQuery = {}) { - // possibly show some loading screen in the future here. - var externalUrl = new URL(url); + // possibly show some loading screen in the future here. + var externalUrl = new URL(url); - for (const queryKey in optionalQuery) { - externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]); - } - - window.location.assign(externalUrl); + for (const queryKey in optionalQuery) { + externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]); + } + + window.location.assign(externalUrl); } // Get route information by page name. // This will reach out to the Cms and there is a 1:1 relationship between page names and route names. async function GetRouteInfoFromPageName(pageName) { - const response = await store.dispatch(storeActions.GET_ROUTE_INFO_ACTION, { - pageName: pageName, - }); - const jsonFromResponse = JSON.parse(response.data.Result); - let routeData = []; - - // Add our route data and return our array. - Object.keys(jsonFromResponse).forEach((key) => { - routeData.push({ - path: "/", - name: `${key}`, - component: lazyLoadComponent(jsonFromResponse[key].LayoutName), + const response = await store.dispatch(storeActions.GET_ROUTE_INFO_ACTION, { + pageName: pageName, }); - }); + const jsonFromResponse = JSON.parse(response.data.Result); + let routeData = []; - return routeData; + // Add our route data and return our array. + Object.keys(jsonFromResponse).forEach((key) => { + routeData.push({ + path: "/", + name: `${key}`, + component: lazyLoadComponent(jsonFromResponse[key].LayoutName), + }); + }); + + return routeData; } // Go to our start page on a 404. async function GoToFunnelStartOn404(next) { - const apiResponse = await store.dispatch(storeActions.GET_HOMEPAGE_NAME); - const homepageName = apiResponse.data.Result; + const apiResponse = await store.dispatch(storeActions.GET_HOMEPAGE_NAME); + const homepageName = apiResponse.data.Result; - // Put item on the bus - eventBus.addEventToBus( - globalEvents.Categories.GLOBAL_ALERT, - globalEvents.SubCategories.PAGE_NOT_FOUND, - { - isDismissible: true, - messageCopy: "You can get a quote by starting on this page.", - messageHeadline: "We're sorry, something went wrong.", - type: globalEventTypes.Danger, - } - ); + // Put item on the bus + eventBus.addEventToBus( + globalEvents.Categories.GLOBAL_ALERT, + globalEvents.SubCategories.PAGE_NOT_FOUND, + { + isDismissible: true, + messageCopy: "You can get a quote by starting on this page.", + messageHeadline: "We're sorry, something went wrong.", + type: globalEventTypes.Danger, + } + ); - next({ - path: "/", - query: { fmgPage: homepageName }, - }); + next({ + path: "/", + query: { fmgPage: homepageName }, + }); } // Checks arePagePrerequisitesValid on the component passed in. function arePagePrerequisitesValid(component) { - return component.default.methods.arePagePrerequisitesValid(); + return component.default.methods.arePagePrerequisitesValid(); } // Run SiteEntry and PageEntry triggers for experiments async function runExperiments(nextPage) { - if (!store.getters.applicationUser.triggeredSiteEntry) { - await baseMixin.methods.dispatchStoreAction(storeActions.RUN_EXPERIMENTS_FOR_TRIGGER, { - userId: getDeviceIdValue(), - triggerEvent: experimentTriggers.SITE_ENTRY, - triggerValue: applicationConfig.SITE_ENTRY_TRIGGER_VALUE - }) - } + if (!store.getters.applicationUser.triggeredSiteEntry) { + await baseMixin.methods.dispatchStoreAction(storeActions.RUN_EXPERIMENTS_FOR_TRIGGER, { + userId: getDeviceIdValue(), + triggerEvent: experimentTriggers.SITE_ENTRY, + triggerValue: applicationConfig.SITE_ENTRY_TRIGGER_VALUE, + }); + } - await baseMixin.methods.dispatchStoreAction(storeActions.RUN_EXPERIMENTS_FOR_TRIGGER, { - userId: getDeviceIdValue(), - triggerEvent: experimentTriggers.PAGE_ENTRY, - triggerValue: nextPage - }) + await baseMixin.methods.dispatchStoreAction(storeActions.RUN_EXPERIMENTS_FOR_TRIGGER, { + userId: getDeviceIdValue(), + triggerEvent: experimentTriggers.PAGE_ENTRY, + triggerValue: nextPage, + }); } export default router; diff --git a/src/router/router-constants/externalUrl-values.js b/src/router/router-constants/externalUrl-values.js index a5b059480..7dad02b62 100644 --- a/src/router/router-constants/externalUrl-values.js +++ b/src/router/router-constants/externalUrl-values.js @@ -2,4 +2,4 @@ const externalUrls = { HERITAGE_FUNNEL: process.env.VUE_APP_HERITAGE_FUNNEL, }; -export { externalUrls }; \ No newline at end of file +export { externalUrls }; diff --git a/src/router/router-constants/fmgPage-values.js b/src/router/router-constants/fmgPage-values.js index d924e30de..c237d3c5b 100644 --- a/src/router/router-constants/fmgPage-values.js +++ b/src/router/router-constants/fmgPage-values.js @@ -1,21 +1,21 @@ const fmgPageValues = { - VEHICLE_YEAR: "vehicle-year", - VEHICLE_MAKE: "vehicle-make", - VEHICLE_MODEL: "vehicle-model", - VEHICLE_STYLE: "vehicle-style", - VEHICLE_DAMAGE: "vehicle-damage", - ADDRESS_LOOKUP: "address-lookup", - VIN_LOOKUP: "vin-lookup", - VEHICLE_PARTS: "vehicle-parts", - PART_QUESTIONS: "part-questions", - MOLDING_QUESTIONS: "molding-questions", - CAPABILITY_QUESTIONS: "capability-questions", - LICENSE_PLATE_LOOKUP: "license-plate-lookup", - REVEAL: "reveal", - ESTIMATE: "estimate", - ADDRESS_VEHICLES: "address-vehicles", - QUOTE: "quote", - HERITAGE: "heritage" + VEHICLE_YEAR: "vehicle-year", + VEHICLE_MAKE: "vehicle-make", + VEHICLE_MODEL: "vehicle-model", + VEHICLE_STYLE: "vehicle-style", + VEHICLE_DAMAGE: "vehicle-damage", + ADDRESS_LOOKUP: "address-lookup", + VIN_LOOKUP: "vin-lookup", + VEHICLE_PARTS: "vehicle-parts", + PART_QUESTIONS: "part-questions", + MOLDING_QUESTIONS: "molding-questions", + CAPABILITY_QUESTIONS: "capability-questions", + LICENSE_PLATE_LOOKUP: "license-plate-lookup", + REVEAL: "reveal", + ESTIMATE: "estimate", + ADDRESS_VEHICLES: "address-vehicles", + QUOTE: "quote", + HERITAGE: "heritage", }; export { fmgPageValues }; diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index 9ca7f9a30..91c657cac 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -1,34 +1,34 @@ const navigationScenarios = { - // General - CLICKED_BACK: "CLICKED_BACK", - CLICKED_FORWARD: "CLICKED_FORWARD", + // General + CLICKED_BACK: "CLICKED_BACK", + CLICKED_FORWARD: "CLICKED_FORWARD", - // YMMS - SELECTED_YEAR: "SELECTED_YEAR", - SELECTED_MODEL: "SELECTED_MODEL", - SELECTED_MAKE: "SELECTED_MAKE", - SELECTED_STYLE: "SELECTED_STYLE", - - // Vin pages - CLICKED_BACK_WITH_VIN: "CLICKED_BACK_WITH_VIN", - CLICKED_FORWARD_WITH_VIN: "CLICKED_FORWARD_WITH_VIN", - CLICKED_FORWARD_WITHOUT_VIN: "CLICKED_FORWARD_WITHOUT_VIN", - CONTINUING_WITH_MULTIPLE_VEHICLES: "CONTINUING_WITH_MULTIPLE_VEHICLES", - SELECTED_VIN_HAS_MISMATCHED_GLASS: "SELECTED_VIN_HAS_MISMATCHED_GLASS", - SELECTED_MANUAL_VIN: "SELECTED_MANUAL_VIN", - SELECTED_LICENSE_PLATE: "SELECTED_LICENSE_PLATE", - SELECTED_HOME_ADDRESS: "SELECTED_HOME_ADDRESS", + // YMMS + SELECTED_YEAR: "SELECTED_YEAR", + SELECTED_MODEL: "SELECTED_MODEL", + SELECTED_MAKE: "SELECTED_MAKE", + SELECTED_STYLE: "SELECTED_STYLE", - // Question pages - HAS_PART_QUESTIONS: "HAS_PART_QUESTIONS", - HAS_MULTIPLE_PARTS_TO_CHOOSE: "HAS_MULTIPLE_PARTS_TO_CHOOSE", - HAS_MOLDING_QUESTIONS: "HAS_MOLDING_QUESTIONS", - HAS_CAPABILITY_QUESTIONS: "HAS_CAPABILITY_QUESTIONS", - HAS_NO_MORE_QUESTIONS: "HAS_NO_MORE_QUESTIONS", - CLICKED_BACK_WITH_NO_QUESTIONS: "CLICKED_BACK_WITH_NO_QUESTIONS", - CLICKED_BACK_WITH_PART_QUESTIONS: "CLICKED_BACK_WITH_PART_QUESTIONS", - CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE: "CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE", - CLICKED_BACK_WITH_MOLDING_QUESTIONS: "CLICKED_BACK_WITH_MOLDING_QUESTIONS" + // Vin pages + CLICKED_BACK_WITH_VIN: "CLICKED_BACK_WITH_VIN", + CLICKED_FORWARD_WITH_VIN: "CLICKED_FORWARD_WITH_VIN", + CLICKED_FORWARD_WITHOUT_VIN: "CLICKED_FORWARD_WITHOUT_VIN", + CONTINUING_WITH_MULTIPLE_VEHICLES: "CONTINUING_WITH_MULTIPLE_VEHICLES", + SELECTED_VIN_HAS_MISMATCHED_GLASS: "SELECTED_VIN_HAS_MISMATCHED_GLASS", + SELECTED_MANUAL_VIN: "SELECTED_MANUAL_VIN", + SELECTED_LICENSE_PLATE: "SELECTED_LICENSE_PLATE", + SELECTED_HOME_ADDRESS: "SELECTED_HOME_ADDRESS", + + // Question pages + HAS_PART_QUESTIONS: "HAS_PART_QUESTIONS", + HAS_MULTIPLE_PARTS_TO_CHOOSE: "HAS_MULTIPLE_PARTS_TO_CHOOSE", + HAS_MOLDING_QUESTIONS: "HAS_MOLDING_QUESTIONS", + HAS_CAPABILITY_QUESTIONS: "HAS_CAPABILITY_QUESTIONS", + HAS_NO_MORE_QUESTIONS: "HAS_NO_MORE_QUESTIONS", + CLICKED_BACK_WITH_NO_QUESTIONS: "CLICKED_BACK_WITH_NO_QUESTIONS", + CLICKED_BACK_WITH_PART_QUESTIONS: "CLICKED_BACK_WITH_PART_QUESTIONS", + CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE: "CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE", + CLICKED_BACK_WITH_MOLDING_QUESTIONS: "CLICKED_BACK_WITH_MOLDING_QUESTIONS", }; export { navigationScenarios }; diff --git a/src/router/router-constants/router-params.js b/src/router/router-constants/router-params.js index 80ddb779d..3bebdc33e 100644 --- a/src/router/router-constants/router-params.js +++ b/src/router/router-constants/router-params.js @@ -1,5 +1,5 @@ const routerParams = { - DISPLAY_VEHICLE_CHANGE_ALERT: "displayVehicleChangeAlert" - }; - - export { routerParams }; \ No newline at end of file + DISPLAY_VEHICLE_CHANGE_ALERT: "displayVehicleChangeAlert", +}; + +export { routerParams }; diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 3c36af9f3..e1baa72ce 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -2,332 +2,332 @@ import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; // Get store from router/index.js instead of importing it here to get updated values -const routingTable = function(store) { - return [ - { - fmgPageValue: fmgPageValues.VEHICLE_YEAR, - maps: [ +const routingTable = function (store) { + return [ { - scenario: navigationScenarios.SELECTED_YEAR, - destinationFmgPageValue: fmgPageValues.VEHICLE_MAKE, - }, - ], - }, - { - fmgPageValue: fmgPageValues.VEHICLE_MAKE, - maps: [ - { - scenario: navigationScenarios.SELECTED_MAKE, - destinationFmgPageValue: fmgPageValues.VEHICLE_MODEL, + fmgPageValue: fmgPageValues.VEHICLE_YEAR, + maps: [ + { + scenario: navigationScenarios.SELECTED_YEAR, + destinationFmgPageValue: fmgPageValues.VEHICLE_MAKE, + }, + ], }, { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.VEHICLE_YEAR, - }, - ], - }, - { - fmgPageValue: fmgPageValues.VEHICLE_MODEL, - maps: [ - { - scenario: navigationScenarios.SELECTED_MODEL, - destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE, + fmgPageValue: fmgPageValues.VEHICLE_MAKE, + maps: [ + { + scenario: navigationScenarios.SELECTED_MAKE, + destinationFmgPageValue: fmgPageValues.VEHICLE_MODEL, + }, + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.VEHICLE_YEAR, + }, + ], }, { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.VEHICLE_MAKE, - }, - ], - }, - { - fmgPageValue: fmgPageValues.VEHICLE_STYLE, - maps: [ - { - scenario: navigationScenarios.SELECTED_STYLE, - destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + fmgPageValue: fmgPageValues.VEHICLE_MODEL, + maps: [ + { + scenario: navigationScenarios.SELECTED_MODEL, + destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE, + }, + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.VEHICLE_MAKE, + }, + ], }, { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.VEHICLE_MODEL, - }, - ], - }, - { - fmgPageValue: fmgPageValues.VEHICLE_DAMAGE, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE, + fmgPageValue: fmgPageValues.VEHICLE_STYLE, + maps: [ + { + scenario: navigationScenarios.SELECTED_STYLE, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + }, + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.VEHICLE_MODEL, + }, + ], }, { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_VIN, - destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, + fmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE, + }, + { + scenario: navigationScenarios.CLICKED_FORWARD_WITH_VIN, + destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, + }, + { + scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, + destinationFmgPageValue: fmgPageValues.ESTIMATE, + }, + ], }, { - scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, - destinationFmgPageValue: fmgPageValues.ESTIMATE, - }, - ], - }, - { - fmgPageValue: fmgPageValues.REVEAL, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, - }, - ], - }, - { - fmgPageValue: fmgPageValues.VIN_LOOKUP, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.ESTIMATE, + fmgPageValue: fmgPageValues.REVEAL, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + }, + ], }, { - scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, - destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + fmgPageValue: fmgPageValues.VIN_LOOKUP, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.ESTIMATE, + }, + { + scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + }, + { + scenario: navigationScenarios.CLICKED_BACK_WITH_VIN, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + }, + { + scenario: navigationScenarios.HAS_PART_QUESTIONS, + destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, + destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS, + }, + { + scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, + destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, + destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, + }, + ], }, { - scenario: navigationScenarios.CLICKED_BACK_WITH_VIN, - destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + fmgPageValue: fmgPageValues.LICENSE_PLATE_LOOKUP, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.ESTIMATE, + }, + { + scenario: navigationScenarios.CLICKED_FORWARD, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + }, + { + scenario: navigationScenarios.HAS_PART_QUESTIONS, + destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, + destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS, + }, + { + scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, + destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, + destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, + }, + ], }, { - scenario: navigationScenarios.HAS_PART_QUESTIONS, - destinationFmgPageValue: fmgPageValues.PART_QUESTIONS + fmgPageValue: fmgPageValues.ADDRESS_LOOKUP, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.ESTIMATE, + }, + { + scenario: navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES, + destinationFmgPageValue: fmgPageValues.ADDRESS_VEHICLES, + }, + { + scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + }, + { + scenario: navigationScenarios.HAS_PART_QUESTIONS, + destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, + destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS, + }, + { + scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, + destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, + destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, + }, + ], }, { - scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, - destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS + fmgPageValue: fmgPageValues.ADDRESS_VEHICLES, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP, + }, + { + scenario: navigationScenarios.CLICKED_FORWARD, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + }, + { + scenario: navigationScenarios.HAS_PART_QUESTIONS, + destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, + destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS, + }, + { + scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, + destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, + destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, + }, + ], }, { - scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, - destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS + fmgPageValue: fmgPageValues.ESTIMATE, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK, + destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + }, + { + scenario: navigationScenarios.SELECTED_MANUAL_VIN, + destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, + }, + { + scenario: navigationScenarios.SELECTED_LICENSE_PLATE, + destinationFmgPageValue: fmgPageValues.LICENSE_PLATE_LOOKUP, + }, + { + scenario: navigationScenarios.SELECTED_HOME_ADDRESS, + destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP, + }, + ], }, { - scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, - destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS - } - ], - }, - { - fmgPageValue: fmgPageValues.LICENSE_PLATE_LOOKUP, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.ESTIMATE, + fmgPageValue: fmgPageValues.PART_QUESTIONS, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, + destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, + }, + { + scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, + destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS, + }, + { + scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, + destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, + destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS, + destinationFmgPageValue: fmgPageValues.QUOTE, + }, + ], }, { - scenario: navigationScenarios.CLICKED_FORWARD, - destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, + fmgPageValue: fmgPageValues.VEHICLE_PARTS, + maps: [ + { + scenario: navigationScenarios.CLICKED_FORWARD, + destinationFmgPageValue: fmgPageValues.REVEAL, + }, + { + scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, + destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, + }, + { + scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, + destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, + }, + { + scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, + destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, + destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS, + destinationFmgPageValue: fmgPageValues.QUOTE, + }, + ], }, { - scenario: navigationScenarios.HAS_PART_QUESTIONS, - destinationFmgPageValue: fmgPageValues.PART_QUESTIONS + fmgPageValue: fmgPageValues.MOLDING_QUESTIONS, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, + destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, + }, + { + scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, + destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, + }, + { + scenario: navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE, + destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS, + }, + { + scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, + destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS, + destinationFmgPageValue: fmgPageValues.QUOTE, + }, + ], }, { - scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, - destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS + fmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, + destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, + }, + { + scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, + destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, + }, + { + scenario: navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE, + destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS, + }, + { + scenario: navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS, + destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, + }, + { + scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS, + destinationFmgPageValue: fmgPageValues.QUOTE, + }, + ], }, - { - scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, - destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS - }, - { - scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, - destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS - } - ], - }, - { - fmgPageValue: fmgPageValues.ADDRESS_LOOKUP, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.ESTIMATE, - }, - { - scenario: navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES, - destinationFmgPageValue: fmgPageValues.ADDRESS_VEHICLES, - }, - { - scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, - destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, - }, - { - scenario: navigationScenarios.HAS_PART_QUESTIONS, - destinationFmgPageValue: fmgPageValues.PART_QUESTIONS - }, - { - scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, - destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS - }, - { - scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, - destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS - }, - { - scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, - destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS - } - ], - }, - { - fmgPageValue: fmgPageValues.ADDRESS_VEHICLES, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP, - }, - { - scenario: navigationScenarios.CLICKED_FORWARD, - destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, - }, - { - scenario: navigationScenarios.HAS_PART_QUESTIONS, - destinationFmgPageValue: fmgPageValues.PART_QUESTIONS - }, - { - scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, - destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS - }, - { - scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, - destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS - }, - { - scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, - destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS - }, - ], - }, - { - fmgPageValue: fmgPageValues.ESTIMATE, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK, - destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE, - }, - { - scenario: navigationScenarios.SELECTED_MANUAL_VIN, - destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, - }, - { - scenario: navigationScenarios.SELECTED_LICENSE_PLATE, - destinationFmgPageValue: fmgPageValues.LICENSE_PLATE_LOOKUP, - }, - { - scenario: navigationScenarios.SELECTED_HOME_ADDRESS, - destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP, - }, - ], - }, - { - fmgPageValue: fmgPageValues.PART_QUESTIONS, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, - destinationFmgPageValue: fmgPageValues.VIN_LOOKUP - }, - { - scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, - destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS - }, - { - scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, - destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, - }, - { - scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, - destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, - }, - { - scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS, - destinationFmgPageValue: fmgPageValues.QUOTE, - }, - ] - }, - { - fmgPageValue: fmgPageValues.VEHICLE_PARTS, - maps: [ - { - scenario: navigationScenarios.CLICKED_FORWARD, - destinationFmgPageValue: fmgPageValues.REVEAL, - }, - { - scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, - destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, - }, - { - scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, - destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, - }, - { - scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, - destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, - }, - { - scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, - destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, - }, - { - scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS, - destinationFmgPageValue: fmgPageValues.QUOTE, - }, - ], - }, - { - fmgPageValue: fmgPageValues.MOLDING_QUESTIONS, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, - destinationFmgPageValue: fmgPageValues.VIN_LOOKUP - }, - { - scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, - destinationFmgPageValue: fmgPageValues.PART_QUESTIONS - }, - { - scenario: navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE, - destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS - }, - { - scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, - destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, - }, - { - scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS, - destinationFmgPageValue: fmgPageValues.QUOTE, - } - ] - }, - { - fmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, - maps: [ - { - scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, - destinationFmgPageValue: fmgPageValues.VIN_LOOKUP - }, - { - scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, - destinationFmgPageValue: fmgPageValues.PART_QUESTIONS - }, - { - scenario: navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE, - destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS - }, - { - scenario: navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS, - destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS - }, - { - scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS, - destinationFmgPageValue: fmgPageValues.QUOTE - }, - ] - }, - ]; -} + ]; +}; export { routingTable }; diff --git a/src/store/index.js b/src/store/index.js index ad8e74c89..5e55b00cb 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -12,1359 +12,1366 @@ import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; // Export State const getDefaultState = () => { - return { - order: { - vehicle: { - year: null, - make: null, - model: null, - style: null, - carId: null, - category: null, - vin: null, - imageUrl: null, - imageVifNumber: null, - imageColor: null, - registration: { - licensePlate: null, - address: null, - city: null, - state: null, - zipCode: null, - firstName: null, - lastName: null, + return { + order: { + vehicle: { + year: null, + make: null, + model: null, + style: null, + carId: null, + category: null, + vin: null, + imageUrl: null, + imageVifNumber: null, + imageColor: null, + registration: { + licensePlate: null, + address: null, + city: null, + state: null, + zipCode: null, + firstName: null, + lastName: null, + }, + }, + serviceLocation: { + address: null, + city: null, + state: null, + zipCode: null, + }, + customer: { + emailAddress: null, + }, + damage: { + isRepair: null, + numberOfChips: null, + glassToReplace: null, + partQuestionAnswers: null, + moldingQuestionAnswers: null, + capabilityQuestionAnswers: null, + }, + lineItems: { + glassParts: null, + }, + payment: { + isInsurance: null, + insuranceCoverage: { + isVerified: null, + }, + }, + referralNumber: null, + referralDate: null, + referralCorrelationId: null, + accountNumber: 0, + eon: null, }, - }, - serviceLocation: { - address: null, - city: null, - state: null, - zipCode: null, - }, - customer: { - emailAddress: null, - }, - damage: { - isRepair: null, - numberOfChips: null, - glassToReplace: null, - partQuestionAnswers: null, - moldingQuestionAnswers: null, - capabilityQuestionAnswers: null, - }, - lineItems: { - glassParts: null, - }, - payment: { - isInsurance: null, - insuranceCoverage: { - isVerified: null, + applicationUser: { + eventBus: [], + pageData: {}, + savedSessionTimeout: getDateForSavedSessionTimeout(), + saveSessionPromise: null, + savedSessionId: null, + crmCustomerId: null, + lastPageVisited: null, + experiments: [], + triggeredSiteEntry: false, }, - }, - referralNumber: null, - referralDate: null, - referralCorrelationId: null, - accountNumber: 0, - eon: null, - }, - applicationUser: { - eventBus: [], - pageData: {}, - savedSessionTimeout: getDateForSavedSessionTimeout(), - saveSessionPromise: null, - savedSessionId: null, - crmCustomerId: null, - lastPageVisited: null, - experiments: [], - triggeredSiteEntry: false, - }, - // gaClickInformation: { - // currentlySelectedValues: {}, - // firedGaClickEventValues: {}, - // lastFocusedInputGroup: "", - // wasLastFocusedInputMultiselect: undefined, - // }, - }; + // gaClickInformation: { + // currentlySelectedValues: {}, + // firedGaClickEventValues: {}, + // lastFocusedInputGroup: "", + // wasLastFocusedInputMultiselect: undefined, + // }, + }; }; export const state = getDefaultState(); // Export Mutations export const mutations = { - // VEHICLE MUTATIONS - updateYear(state, year) { - state.order.vehicle.year = year; - }, - updateMake(state, make) { - state.order.vehicle.make = make; - }, - updateModel(state, model) { - state.order.vehicle.model = model; - }, - updateStyle(state, style) { - state.order.vehicle.style = style; - }, - updateCarId(state, carId) { - state.order.vehicle.carId = carId; - }, - updateVehicleCategory(state, category) { - state.order.vehicle.category = category; - }, - updateVehicleImageUrl(state, imageUrl) { - state.order.vehicle.imageUrl = imageUrl; - }, - updateVehicleImageVifNumber(state, imageVifNumber) { - state.order.vehicle.imageVifNumber = imageVifNumber; - }, - updateVehicleImageColor(state, imageColor) { - state.order.vehicle.imageColor = imageColor; - }, - updateVehicleVin(state, vin) { - state.order.vehicle.vin = vin; - }, - updateIsRepair(state, isRepair) { - state.order.damage.isRepair = isRepair; - }, - updateNumberOfChips(state, numberOfChips) { - state.order.damage.numberOfChips = numberOfChips; - }, - updateGlassToReplace(state, glassToReplace) { - state.order.damage.glassToReplace = glassToReplace; - }, - updatePartQuestionAnswers(state, answersArray) { - state.order.damage.partQuestionAnswers = answersArray; - }, - updateMoldingQuestionAnswers(state, answersArray) { - state.order.damage.moldingQuestionAnswers = answersArray; - }, - updateCapabilityQuestionAnswers(state, answersArray) { - state.order.damage.capabilityQuestionAnswers = answersArray; - }, - updateGlassParts(state, partsData) { - state.order.lineItems.glassParts = partsData; - }, - updateOtherParts(state, partsData) { - state.order.lineItems.otherParts = partsData; - }, - updatePageData(state, pageData) { - state.applicationUser.pageData[pageData.page] = pageData.data; - }, - updateReferralCorrelationId(state, referralCorrelationId) { - state.order.referralCorrelationId = referralCorrelationId; - }, - updateReferralNumber(state, referralNumber) { - state.order.referralNumber = referralNumber; - }, - updateReferralDate(state, referralDate) { - state.order.referralDate = referralDate; - }, - updateParentAcctNumber(state, parentAcctNumber) { - state.order.accountNumber = parentAcctNumber; - }, - updateEON(state, eon) { - state.order.eon = eon; - }, - updateIsInsurance(state, isInsurance) { - state.order.payment.isInsurance = isInsurance; - }, - updateInsuranceVerifiedStatus(state, isVerified) { - state.order.payment.insuranceCoverage.isVerified = isVerified; - }, - updateRegistrationLicensePlate(state, licensePlate) { - state.order.vehicle.registration.licensePlate = licensePlate; - }, - updateRegistrationAddress(state, registrationAddress) { - state.order.vehicle.registration.address = registrationAddress; - }, - updateRegistrationCity(state, registrationCity) { - state.order.vehicle.registration.city = registrationCity; - }, - updateRegistrationState(state, registrationState) { - state.order.vehicle.registration.state = registrationState; - }, - updateRegistrationZipCode(state, registrationZipCode) { - state.order.vehicle.registration.zipCode = registrationZipCode; - }, - updateServiceLocationZipCode(state, serviceLocationZip) { - state.order.serviceLocation.zipCode = serviceLocationZip; - }, - updateServiceLocationState(state, serviceLocationState) { - state.order.serviceLocation.state = serviceLocationState; - }, - updateRegistrationFirstName(state, firstName) { - state.order.vehicle.registration.firstName = firstName; - }, - updateRegistrationLastName(state, lastName) { - state.order.vehicle.registration.lastName = lastName; - }, - updateCustomerEmailAddress(state, customerEmailAddress) { - state.order.customer.emailAddress = customerEmailAddress; - }, - updateVehicle(state, vehicleInfo) { - state.order.vehicle.year = vehicleInfo.year; - state.order.vehicle.make = vehicleInfo.make; - state.order.vehicle.model = vehicleInfo.model; - state.order.vehicle.style = vehicleInfo.style; - state.order.vehicle.carId = vehicleInfo.carId; - state.order.vehicle.category = vehicleInfo.category; - state.order.vehicle.vin = vehicleInfo.vin; + // VEHICLE MUTATIONS + updateYear(state, year) { + state.order.vehicle.year = year; + }, + updateMake(state, make) { + state.order.vehicle.make = make; + }, + updateModel(state, model) { + state.order.vehicle.model = model; + }, + updateStyle(state, style) { + state.order.vehicle.style = style; + }, + updateCarId(state, carId) { + state.order.vehicle.carId = carId; + }, + updateVehicleCategory(state, category) { + state.order.vehicle.category = category; + }, + updateVehicleImageUrl(state, imageUrl) { + state.order.vehicle.imageUrl = imageUrl; + }, + updateVehicleImageVifNumber(state, imageVifNumber) { + state.order.vehicle.imageVifNumber = imageVifNumber; + }, + updateVehicleImageColor(state, imageColor) { + state.order.vehicle.imageColor = imageColor; + }, + updateVehicleVin(state, vin) { + state.order.vehicle.vin = vin; + }, + updateIsRepair(state, isRepair) { + state.order.damage.isRepair = isRepair; + }, + updateNumberOfChips(state, numberOfChips) { + state.order.damage.numberOfChips = numberOfChips; + }, + updateGlassToReplace(state, glassToReplace) { + state.order.damage.glassToReplace = glassToReplace; + }, + updatePartQuestionAnswers(state, answersArray) { + state.order.damage.partQuestionAnswers = answersArray; + }, + updateMoldingQuestionAnswers(state, answersArray) { + state.order.damage.moldingQuestionAnswers = answersArray; + }, + updateCapabilityQuestionAnswers(state, answersArray) { + state.order.damage.capabilityQuestionAnswers = answersArray; + }, + updateGlassParts(state, partsData) { + state.order.lineItems.glassParts = partsData; + }, + updateOtherParts(state, partsData) { + state.order.lineItems.otherParts = partsData; + }, + updatePageData(state, pageData) { + state.applicationUser.pageData[pageData.page] = pageData.data; + }, + updateReferralCorrelationId(state, referralCorrelationId) { + state.order.referralCorrelationId = referralCorrelationId; + }, + updateReferralNumber(state, referralNumber) { + state.order.referralNumber = referralNumber; + }, + updateReferralDate(state, referralDate) { + state.order.referralDate = referralDate; + }, + updateParentAcctNumber(state, parentAcctNumber) { + state.order.accountNumber = parentAcctNumber; + }, + updateEON(state, eon) { + state.order.eon = eon; + }, + updateIsInsurance(state, isInsurance) { + state.order.payment.isInsurance = isInsurance; + }, + updateInsuranceVerifiedStatus(state, isVerified) { + state.order.payment.insuranceCoverage.isVerified = isVerified; + }, + updateRegistrationLicensePlate(state, licensePlate) { + state.order.vehicle.registration.licensePlate = licensePlate; + }, + updateRegistrationAddress(state, registrationAddress) { + state.order.vehicle.registration.address = registrationAddress; + }, + updateRegistrationCity(state, registrationCity) { + state.order.vehicle.registration.city = registrationCity; + }, + updateRegistrationState(state, registrationState) { + state.order.vehicle.registration.state = registrationState; + }, + updateRegistrationZipCode(state, registrationZipCode) { + state.order.vehicle.registration.zipCode = registrationZipCode; + }, + updateServiceLocationZipCode(state, serviceLocationZip) { + state.order.serviceLocation.zipCode = serviceLocationZip; + }, + updateServiceLocationState(state, serviceLocationState) { + state.order.serviceLocation.state = serviceLocationState; + }, + updateRegistrationFirstName(state, firstName) { + state.order.vehicle.registration.firstName = firstName; + }, + updateRegistrationLastName(state, lastName) { + state.order.vehicle.registration.lastName = lastName; + }, + updateCustomerEmailAddress(state, customerEmailAddress) { + state.order.customer.emailAddress = customerEmailAddress; + }, + updateVehicle(state, vehicleInfo) { + state.order.vehicle.year = vehicleInfo.year; + state.order.vehicle.make = vehicleInfo.make; + state.order.vehicle.model = vehicleInfo.model; + state.order.vehicle.style = vehicleInfo.style; + state.order.vehicle.carId = vehicleInfo.carId; + state.order.vehicle.category = vehicleInfo.category; + state.order.vehicle.vin = vehicleInfo.vin; - state.order.vehicle.imageUrl = vehicleInfo.imageUrl; - state.order.vehicle.imageVifNumber = vehicleInfo.imageVifNumber; - state.order.vehicle.imageColor = vehicleInfo.imageVifColor; - }, - updateRegistration(state, registrationInfo) { - state.order.vehicle.registration.licensePlate = - registrationInfo?.licensePlate; - state.order.vehicle.registration.address = registrationInfo?.address; - state.order.vehicle.registration.city = registrationInfo?.city; - state.order.vehicle.registration.state = registrationInfo?.state; - state.order.vehicle.registration.zipCode = registrationInfo?.zipCode; - state.order.vehicle.registration.firstName = registrationInfo?.firstName; - state.order.vehicle.registration.lastName = registrationInfo?.lastName; - }, - updateServiceLocation(state, serviceLocationInfo) { - state.order.serviceLocation.address = serviceLocationInfo.address; - state.order.serviceLocation.city = serviceLocationInfo.city; - state.order.serviceLocation.state = serviceLocationInfo.state; - state.order.serviceLocation.zipCode = serviceLocationInfo.zipCode; - }, + state.order.vehicle.imageUrl = vehicleInfo.imageUrl; + state.order.vehicle.imageVifNumber = vehicleInfo.imageVifNumber; + state.order.vehicle.imageColor = vehicleInfo.imageVifColor; + }, + updateRegistration(state, registrationInfo) { + state.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate; + state.order.vehicle.registration.address = registrationInfo?.address; + state.order.vehicle.registration.city = registrationInfo?.city; + state.order.vehicle.registration.state = registrationInfo?.state; + state.order.vehicle.registration.zipCode = registrationInfo?.zipCode; + state.order.vehicle.registration.firstName = registrationInfo?.firstName; + state.order.vehicle.registration.lastName = registrationInfo?.lastName; + }, + updateServiceLocation(state, serviceLocationInfo) { + state.order.serviceLocation.address = serviceLocationInfo.address; + state.order.serviceLocation.city = serviceLocationInfo.city; + state.order.serviceLocation.state = serviceLocationInfo.state; + state.order.serviceLocation.zipCode = serviceLocationInfo.zipCode; + }, - // applicationUser MUTATIONS - updateSaveSessionPromise(state, saveSessionPromise) { - state.applicationUser.saveSessionPromise = saveSessionPromise; - }, - updateSavedSessionId(state, savedSessionId) { - state.applicationUser.savedSessionId = savedSessionId; - }, - updateCrmCustomerId(state, crmCustomerId) { - state.applicationUser.crmCustomerId = crmCustomerId; - }, - updateLastPageVisited(state, lastPageVisited) { - state.applicationUser.lastPageVisited = lastPageVisited; - }, - // EVENT BUS MUTATIONS - addEventToBus(state, event) { - state.applicationUser.eventBus.push(event); - }, - removeEventFromBus(state, eventData) { - const matchedEvent = state.applicationUser.eventBus.find( - ({ category, subCategory }) => - category === eventData.category && subCategory === eventData.subCategory - ); - const itemIndex = state.applicationUser.eventBus.indexOf(matchedEvent); + // applicationUser MUTATIONS + updateSaveSessionPromise(state, saveSessionPromise) { + state.applicationUser.saveSessionPromise = saveSessionPromise; + }, + updateSavedSessionId(state, savedSessionId) { + state.applicationUser.savedSessionId = savedSessionId; + }, + updateCrmCustomerId(state, crmCustomerId) { + state.applicationUser.crmCustomerId = crmCustomerId; + }, + updateLastPageVisited(state, lastPageVisited) { + state.applicationUser.lastPageVisited = lastPageVisited; + }, + // EVENT BUS MUTATIONS + addEventToBus(state, event) { + state.applicationUser.eventBus.push(event); + }, + removeEventFromBus(state, eventData) { + const matchedEvent = state.applicationUser.eventBus.find( + ({ category, subCategory }) => + category === eventData.category && subCategory === eventData.subCategory + ); + const itemIndex = state.applicationUser.eventBus.indexOf(matchedEvent); - // If the item exists, remove it. - if (itemIndex > -1) { - state.applicationUser.eventBus.splice(itemIndex, 1); - } - }, + // If the item exists, remove it. + if (itemIndex > -1) { + state.applicationUser.eventBus.splice(itemIndex, 1); + } + }, - // RESET DEPENDENCY MUTATIONS - resetVehicleState(state) { - state.order.vehicle.year = null; - state.order.vehicle.make = null; - state.order.vehicle.model = null; - state.order.vehicle.style = null; - state.order.vehicle.carId = null; - state.order.vehicle.category = null; - state.order.vehicle.vin = null; - state.order.vehicle.imageUrl = null; - state.order.vehicle.imageVifNumber = null; - state.order.vehicle.imageColor = null; - }, - resetDamageState(state) { - state.order.damage.isRepair = null; - state.order.damage.numberOfChips = null; - state.order.damage.glassToReplace = null; - }, - resetRegistrationState(state) { - state.order.vehicle.registration.licensePlate = null; - state.order.vehicle.registration.address = null; - state.order.vehicle.registration.city = null; - state.order.vehicle.registration.state = null; - state.order.vehicle.registration.zipCode = null; - state.order.vehicle.registration.firstName = null; - state.order.vehicle.registration.lastName = null; - }, - resetGlassPartsState(state) { - state.order.lineItems.glassParts = null; - state.order.damage.partQuestionAnswers = null; - state.order.damage.moldingQuestionAnswers = null; - state.order.damage.capabilityQuestionAnswers = null; - state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null; - state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null; - state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null; - state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null; - }, - resetState(state) { - Object.assign(state, getDefaultState()); - }, - resetSaveSessionPromise(state) { - state.applicationUser.saveSessionPromise = null; - }, - // Misc Mutations - updateStateWithOrderInformation(state, orderInformation) { - state.order.referralNumber = orderInformation.referralNumber; - state.order.referralDate = orderInformation.referralDate; - state.order.referralCorrelationId = orderInformation.referralCorrelationId; - state.order.eon = orderInformation.eon; + // RESET DEPENDENCY MUTATIONS + resetVehicleState(state) { + state.order.vehicle.year = null; + state.order.vehicle.make = null; + state.order.vehicle.model = null; + state.order.vehicle.style = null; + state.order.vehicle.carId = null; + state.order.vehicle.category = null; + state.order.vehicle.vin = null; + state.order.vehicle.imageUrl = null; + state.order.vehicle.imageVifNumber = null; + state.order.vehicle.imageColor = null; + }, + resetDamageState(state) { + state.order.damage.isRepair = null; + state.order.damage.numberOfChips = null; + state.order.damage.glassToReplace = null; + }, + resetRegistrationState(state) { + state.order.vehicle.registration.licensePlate = null; + state.order.vehicle.registration.address = null; + state.order.vehicle.registration.city = null; + state.order.vehicle.registration.state = null; + state.order.vehicle.registration.zipCode = null; + state.order.vehicle.registration.firstName = null; + state.order.vehicle.registration.lastName = null; + }, + resetGlassPartsState(state) { + state.order.lineItems.glassParts = null; + state.order.damage.partQuestionAnswers = null; + state.order.damage.moldingQuestionAnswers = null; + state.order.damage.capabilityQuestionAnswers = null; + state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null; + state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null; + state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null; + state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null; + }, + resetState(state) { + Object.assign(state, getDefaultState()); + }, + resetSaveSessionPromise(state) { + state.applicationUser.saveSessionPromise = null; + }, + // Misc Mutations + updateStateWithOrderInformation(state, orderInformation) { + state.order.referralNumber = orderInformation.referralNumber; + state.order.referralDate = orderInformation.referralDate; + state.order.referralCorrelationId = orderInformation.referralCorrelationId; + state.order.eon = orderInformation.eon; - if (state.order.vehicle.vin !== orderInformation.vehicle?.vin) { - state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null; - state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null; - state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null; - state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null; - } + if (state.order.vehicle.vin !== orderInformation.vehicle?.vin) { + state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null; + state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null; + state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null; + state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null; + } - state.order.vehicle = Object.assign(state.order.vehicle, { - year: orderInformation.vehicle?.year, - make: orderInformation.vehicle?.make, - model: orderInformation.vehicle?.model, - style: orderInformation.vehicle?.style, - vin: orderInformation.vehicle?.vin, - carId: orderInformation.vehicle?.carId, - category: orderInformation.vehicle?.category, - imageUrl: orderInformation.vehicle?.imageUrl, - imageVifNumber: orderInformation.vehicle?.imageVifNumber, - imageColor: orderInformation.vehicle?.imageVifColor, - registration: { - firstName: orderInformation.vehicle.registration.firstName, - lastName: orderInformation.vehicle.registration.lastName, - address: orderInformation.vehicle.registration.streetAddress, - city: orderInformation.vehicle.registration.city, - state: orderInformation.vehicle.registration.state, - zipCode: orderInformation.vehicle.registration.zipCode, - licensePlate: orderInformation.vehicle.registration.licensePlateNumber, - }, - }); + state.order.vehicle = Object.assign(state.order.vehicle, { + year: orderInformation.vehicle?.year, + make: orderInformation.vehicle?.make, + model: orderInformation.vehicle?.model, + style: orderInformation.vehicle?.style, + vin: orderInformation.vehicle?.vin, + carId: orderInformation.vehicle?.carId, + category: orderInformation.vehicle?.category, + imageUrl: orderInformation.vehicle?.imageUrl, + imageVifNumber: orderInformation.vehicle?.imageVifNumber, + imageColor: orderInformation.vehicle?.imageVifColor, + registration: { + firstName: orderInformation.vehicle.registration.firstName, + lastName: orderInformation.vehicle.registration.lastName, + address: orderInformation.vehicle.registration.streetAddress, + city: orderInformation.vehicle.registration.city, + state: orderInformation.vehicle.registration.state, + zipCode: orderInformation.vehicle.registration.zipCode, + licensePlate: orderInformation.vehicle.registration.licensePlateNumber, + }, + }); - state.order.damage.glassToReplace = orderInformation.damage.glassToReplace; - state.order.damage.isRepair = orderInformation.damage.isRepair; - state.order.damage.numberOfChips = orderInformation.damage.numberOfChips; + state.order.damage.glassToReplace = orderInformation.damage.glassToReplace; + state.order.damage.isRepair = orderInformation.damage.isRepair; + state.order.damage.numberOfChips = orderInformation.damage.numberOfChips; - state.order.lineItems.glassParts = orderInformation.parts; - state.order.accountNumber = orderInformation.accountNumber; - (state.order.serviceLocation.address = - orderInformation.serviceLocation.streetAddress), - (state.order.serviceLocation.city = - orderInformation.serviceLocation.city), - (state.order.serviceLocation.state = - orderInformation.serviceLocation.state), - (state.order.serviceLocation.zipCode = - orderInformation.serviceLocation.zipCode); + state.order.lineItems.glassParts = orderInformation.parts; + state.order.accountNumber = orderInformation.accountNumber; + (state.order.serviceLocation.address = orderInformation.serviceLocation.streetAddress), + (state.order.serviceLocation.city = orderInformation.serviceLocation.city), + (state.order.serviceLocation.state = orderInformation.serviceLocation.state), + (state.order.serviceLocation.zipCode = orderInformation.serviceLocation.zipCode); - state.order.payment.isInsurance = orderInformation.IsInsuranceOrder; - state.order.payment.insuranceCoverage.isVerified = - orderInformation?.insuranceInfo.coverageVerified; + state.order.payment.isInsurance = orderInformation.IsInsuranceOrder; + state.order.payment.insuranceCoverage.isVerified = + orderInformation?.insuranceInfo.coverageVerified; - state.order.customer.emailAddress = orderInformation.customer.emailAddress; - state.applicationUser.experiments = orderInformation.experiments; - }, - updateExperiments(state, experiments) { - state.applicationUser.experiments = experiments; - }, - updateTriggeredSiteEntry(state, wasSiteEntryTriggered) { - state.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered; - }, - // START GA click event mutations - // updateCurrentlySelectedValues(state, groupName, value) { - // state.gaClickInformation.currentlySelectedValues[groupName] = value; - // }, - // updateFiredGaClickEventValues(state, groupName, value) { - // state.gaClickInformation.firedGaClickEventValues[groupName] = value; - // }, - // updateLastFocusedInputGroup(state, groupName) { - // state.gaClickInformation.lastFocusedInputGroup = groupName; - // }, - // updateWasLastFocusedInputMultiselect(state, wasLastFocusedInputMultiselect) { - // state.gaClickInformation.wasLastFocusedInputMultiselect = - // wasLastFocusedInputMultiselect; - // }, - // END GA click event mutations + state.order.customer.emailAddress = orderInformation.customer.emailAddress; + state.applicationUser.experiments = orderInformation.experiments; + }, + updateExperiments(state, experiments) { + state.applicationUser.experiments = experiments; + }, + updateTriggeredSiteEntry(state, wasSiteEntryTriggered) { + state.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered; + }, + // START GA click event mutations + // updateCurrentlySelectedValues(state, groupName, value) { + // state.gaClickInformation.currentlySelectedValues[groupName] = value; + // }, + // updateFiredGaClickEventValues(state, groupName, value) { + // state.gaClickInformation.firedGaClickEventValues[groupName] = value; + // }, + // updateLastFocusedInputGroup(state, groupName) { + // state.gaClickInformation.lastFocusedInputGroup = groupName; + // }, + // updateWasLastFocusedInputMultiselect(state, wasLastFocusedInputMultiselect) { + // state.gaClickInformation.wasLastFocusedInputMultiselect = + // wasLastFocusedInputMultiselect; + // }, + // END GA click event mutations }; // Export Getters export const getters = { - vehicle: (state) => state.order.vehicle, - eventBusItem: (state) => (eventCategory, eventSubCategory) => { - const matchedEvent = state.applicationUser.eventBus.find( - ({ category, subCategory }) => - category === eventCategory && subCategory === eventSubCategory - ); + vehicle: (state) => state.order.vehicle, + eventBusItem: (state) => (eventCategory, eventSubCategory) => { + const matchedEvent = state.applicationUser.eventBus.find( + ({ category, subCategory }) => + category === eventCategory && subCategory === eventSubCategory + ); - return matchedEvent !== undefined ? matchedEvent.eventValue : undefined; - }, - eventBus: (state) => state.applicationUser.eventBus, - damage: (state) => state.order.damage, - hasAnyNonWindshieldGlassParts: (state) => { - const nonWindshieldItems = state.order.damage.glassToReplace.filter(glassToReplace => glassToReplace.glassLocation != "Windshield"); - return !!nonWindshieldItems.length; - }, - lineItems: (state) => state.order.lineItems, - pageData: (state) => (page) => { - return state.applicationUser.pageData[page]; - }, - applicationUser: (state) => state.applicationUser, - order: (state) => state.order, - payment: (state) => state.order.payment, - experimentOrder: (state) => { - return { - funnelVehicleYear: state.order.vehicle.year, - funnelVehicleMake: state.order.vehicle.make, - funnelVehicleModel: state.order.vehicle.model, - funnelVehicleStyle: state.order.vehicle.style, - funnelIsRepair: state.order.damage.isRepair, - funnelNumberOfChips: state.order.damage.numberOfChips, - funnelCarId: state.order.vehicle.carId, - funnelServiceCity: state.order.serviceLocation.city, - funnelServiceState: state.order.serviceLocation.state, - funnelServiceZipCode: state.order.serviceLocation.zipCode, - funnelParentAccountNumber: state.order.accountNumber, - funnelIsCoverageVerified: - state.order.payment.insuranceCoverage.isVerified, - funnelHasRecalibrationPart: getHasRecalibrationPart(state), - funnelSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1, - funnelSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.WINDSHIELD), - funnelSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.REAR), - funnelSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.DRIVER), - funnelSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.PASSENGER), + return matchedEvent !== undefined ? matchedEvent.eventValue : undefined; + }, + eventBus: (state) => state.applicationUser.eventBus, + damage: (state) => state.order.damage, + hasAnyNonWindshieldGlassParts: (state) => { + const nonWindshieldItems = state.order.damage.glassToReplace.filter( + (glassToReplace) => glassToReplace.glassLocation != "Windshield" + ); + return !!nonWindshieldItems.length; + }, + lineItems: (state) => state.order.lineItems, + pageData: (state) => (page) => { + return state.applicationUser.pageData[page]; + }, + applicationUser: (state) => state.applicationUser, + order: (state) => state.order, + payment: (state) => state.order.payment, + experimentOrder: (state) => { + return { + funnelVehicleYear: state.order.vehicle.year, + funnelVehicleMake: state.order.vehicle.make, + funnelVehicleModel: state.order.vehicle.model, + funnelVehicleStyle: state.order.vehicle.style, + funnelIsRepair: state.order.damage.isRepair, + funnelNumberOfChips: state.order.damage.numberOfChips, + funnelCarId: state.order.vehicle.carId, + funnelServiceCity: state.order.serviceLocation.city, + funnelServiceState: state.order.serviceLocation.state, + funnelServiceZipCode: state.order.serviceLocation.zipCode, + funnelParentAccountNumber: state.order.accountNumber, + funnelIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified, + funnelHasRecalibrationPart: getHasRecalibrationPart(state), + funnelSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1, + funnelSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.damage.glassToReplace, + "glassLocation" + ).includes(damageLocationsSelected.WINDSHIELD), + funnelSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.damage.glassToReplace, + "glassLocation" + ).includes(damageLocationsSelected.REAR), + funnelSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.damage.glassToReplace, + "glassLocation" + ).includes(damageLocationsSelected.DRIVER), + funnelSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.damage.glassToReplace, + "glassLocation" + ).includes(damageLocationsSelected.PASSENGER), - funnelOrderPartNumbers: [...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "partNumber"), ...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.otherParts, "partNumber")], + funnelOrderPartNumbers: [ + ...getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.lineItems.glassParts, + "partNumber" + ), + ...getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.lineItems.otherParts, + "partNumber" + ), + ], - funnelOrderPartTypes: [...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "recalibrationType"), ...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.otherParts, "recalibrationType")], - } - }, - experimentSettings: (state) => - state.applicationUser.experiments - .map((x) => x.settings) - .reduce((r, c) => Object.assign(r, c), {}) ?? {}, - // gaClickInformation: (state) => state.gaClickInformation, + funnelOrderPartTypes: [ + ...getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.lineItems.glassParts, + "recalibrationType" + ), + ...getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.lineItems.otherParts, + "recalibrationType" + ), + ], + }; + }, + experimentSettings: (state) => + state.applicationUser.experiments + .map((x) => x.settings) + .reduce((r, c) => Object.assign(r, c), {}) ?? {}, + // gaClickInformation: (state) => state.gaClickInformation, }; function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) { - return (array ?? []).map(x => x[propertyName]).filter(x => x); + return (array ?? []).map((x) => x[propertyName]).filter((x) => x); } // Export Actions export const actions = { - // Vehicle API Actions - getVehicleYears(context) { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleYears.method, - endpoint: endpoints.GetVehicleYears.url, - payload: {}, - }); - }, - lookupVehicleByYmms(context, { year, make, model, style }) { - return globalMethods.callHttpClient({ - method: endpoints.LookupVehicleByYmms.method, - endpoint: `${endpoints.LookupVehicleByYmms.url}/${year}/${make}/${model}/${style}`, - payload: {}, - }); - }, - lookupVehicleByVin(context, { vin }) { - return globalMethods.callHttpClient({ - method: endpoints.LookupVehicleByVin.method, - endpoint: endpoints.LookupVehicleByVin.url, - payload: { - vin: vin, // EX "1J4GW58S4XC541166" - }, - }); - }, - lookupVinByPlate(context, { licensePlate, licenseState }) { - return globalMethods.callHttpClient({ - method: endpoints.LookupVinByPlate.method, - endpoint: endpoints.LookupVinByPlate.url, - payload: { - licensePlate: licensePlate, - licenseState: licenseState, - }, - }); - }, - lookupVinByAddress( - context, - { licenseLastName, licenseStreetAddress, licenseZip, licenseState } - ) { - return globalMethods.callHttpClient({ - method: endpoints.LookupVinByAddress.method, - endpoint: endpoints.LookupVinByAddress.url, - payload: { - licenseLastName: licenseLastName, - licenseStreetAddress: licenseStreetAddress, - licenseZip: licenseZip, - licenseState: licenseState, - }, - }); - }, - getVehicleMakes(context, { year }) { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleMakes.method, - endpoint: `${endpoints.GetVehicleMakes.url}/${year}`, - payload: {}, - }); - }, - getVehicleModels(context, { year, make }) { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleModels.method, - endpoint: `${endpoints.GetVehicleModels.url}/${year}/${make}`, - payload: {}, - }); - }, - getVehicleStyles(context, { year, make, model }) { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleStyles.method, - endpoint: `${endpoints.GetVehicleStyles.url}/${year}/${make}/${model}`, - payload: {}, - }); - }, - setVehicle(context, { year, make, model, style }) { - return globalMethods - .callHttpClient({ - methods: endpoints.GetVehicle.method, - endpoint: `${endpoints.GetVehicle.url}/${year}/${make}/${model}/${style}`, - payload: {}, - }) - .then((response) => { - context.commit(storeMutations.UPDATE_CAR_ID, response.data.carId); - context.commit( - storeMutations.UPDATE_VEHICLE_CATEGORY, - response.data.category - ); - context.commit( - storeMutations.UPDATE_VEHICLE_IMAGE_URL, - response.data.imageUrl - ); - context.commit( - storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, - response.data.imageVifNumber - ); - context.commit( - storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, - response.data.imageVifColor - ); - return response; - }); - }, - getDamageOptions(context, { carId }) { - return globalMethods.callHttpClient({ - methods: endpoints.GetDamageOptions.method, - endpoint: `${endpoints.GetDamageOptions.url}/${carId}`, - payload: {}, - }); - }, - validateZip(context, { zip }) { - return globalMethods.callHttpClient({ - methods: endpoints.ValidateZip.method, - endpoint: `${endpoints.ValidateZip.url}/${zip}`, - }); - }, + // Vehicle API Actions + getVehicleYears(context) { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleYears.method, + endpoint: endpoints.GetVehicleYears.url, + payload: {}, + }); + }, + lookupVehicleByYmms(context, { year, make, model, style }) { + return globalMethods.callHttpClient({ + method: endpoints.LookupVehicleByYmms.method, + endpoint: `${endpoints.LookupVehicleByYmms.url}/${year}/${make}/${model}/${style}`, + payload: {}, + }); + }, + lookupVehicleByVin(context, { vin }) { + return globalMethods.callHttpClient({ + method: endpoints.LookupVehicleByVin.method, + endpoint: endpoints.LookupVehicleByVin.url, + payload: { + vin: vin, // EX "1J4GW58S4XC541166" + }, + }); + }, + lookupVinByPlate(context, { licensePlate, licenseState }) { + return globalMethods.callHttpClient({ + method: endpoints.LookupVinByPlate.method, + endpoint: endpoints.LookupVinByPlate.url, + payload: { + licensePlate: licensePlate, + licenseState: licenseState, + }, + }); + }, + lookupVinByAddress( + context, + { licenseLastName, licenseStreetAddress, licenseZip, licenseState } + ) { + return globalMethods.callHttpClient({ + method: endpoints.LookupVinByAddress.method, + endpoint: endpoints.LookupVinByAddress.url, + payload: { + licenseLastName: licenseLastName, + licenseStreetAddress: licenseStreetAddress, + licenseZip: licenseZip, + licenseState: licenseState, + }, + }); + }, + getVehicleMakes(context, { year }) { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleMakes.method, + endpoint: `${endpoints.GetVehicleMakes.url}/${year}`, + payload: {}, + }); + }, + getVehicleModels(context, { year, make }) { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleModels.method, + endpoint: `${endpoints.GetVehicleModels.url}/${year}/${make}`, + payload: {}, + }); + }, + getVehicleStyles(context, { year, make, model }) { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleStyles.method, + endpoint: `${endpoints.GetVehicleStyles.url}/${year}/${make}/${model}`, + payload: {}, + }); + }, + setVehicle(context, { year, make, model, style }) { + return globalMethods + .callHttpClient({ + methods: endpoints.GetVehicle.method, + endpoint: `${endpoints.GetVehicle.url}/${year}/${make}/${model}/${style}`, + payload: {}, + }) + .then((response) => { + context.commit(storeMutations.UPDATE_CAR_ID, response.data.carId); + context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, response.data.category); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, response.data.imageUrl); + context.commit( + storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, + response.data.imageVifNumber + ); + context.commit( + storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, + response.data.imageVifColor + ); + return response; + }); + }, + getDamageOptions(context, { carId }) { + return globalMethods.callHttpClient({ + methods: endpoints.GetDamageOptions.method, + endpoint: `${endpoints.GetDamageOptions.url}/${carId}`, + payload: {}, + }); + }, + validateZip(context, { zip }) { + return globalMethods.callHttpClient({ + methods: endpoints.ValidateZip.method, + endpoint: `${endpoints.ValidateZip.url}/${zip}`, + }); + }, - // Dependency Actions - resetVehicleAndDependencies(context) { - 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_STATE); - context.commit(storeMutations.RESET_GLASS_PARTS_STATE); - }, - resetRegistrationAndDependencies(context) { - context.commit(storeMutations.RESET_REGISTRATION_STATE); - context.commit(storeMutations.RESET_GLASS_PARTS_STATE); - }, - resetPartsAndDependencies(context) { - context.commit(storeMutations.RESET_GLASS_PARTS_STATE); - }, - resetState(context) { - context.commit(storeMutations.RESET_STATE); - }, - resetSaveSessionPromise(context) { - context.commit(storeMutations.RESET_SAVE_SESSION_PROMISE); - }, - - // Content API Actions - getRouteInfo(context, { pageName }) { - return globalMethods.callHttpClient({ - method: endpoints.GetRouteInfo.method, - endpoint: endpoints.GetRouteInfo.url(applicationConfig.APPLICATION_ABBREVIATION), - payload: { - pageName: pageName, - }, - }); - }, - getHomepageName(context) { - return globalMethods.callHttpClient({ - method: endpoints.GetHomepageInfo.method, - endpoint: endpoints.GetHomepageInfo.url(applicationConfig.APPLICATION_ABBREVIATION), - }); - }, - getPageData(context, { pageName }) { - return globalMethods.callHttpClient({ - method: endpoints.GetPageData.method, - endpoint: endpoints.GetPageData.url(applicationConfig.APPLICATION_ABBREVIATION, pageName), - payload: {}, - }); - }, - - // Analytics Actions - logExperimentExposure(context, { userId, sessionKey, pageName, experiment }) { - return globalMethods.callHttpClient({ - method: endpoints.LogExperimentExposureIfAssigned.method, - endpoint: endpoints.LogExperimentExposureIfAssigned.url, - payload: { - experimentForLogging: { - userId: userId, - experimentUniverseId: experiment.universeId, - experimentUniverseName: experiment.universeName, - experimentTestId: experiment.testId, - experimentTestName: experiment.testName, - experimentVariationId: experiment.variationId, - experimentVariationName: experiment.variationName, - enabled: experiment.isActive, - isExposed: experiment.isExposed, - userPartitionNumber: experiment.userPartitionNumber, - assignmentId: experiment.assignmentId, - sessionKey: sessionKey, - pageName: pageName, - }, - }, - }); - }, - - // Misc Actions - updateStoreWithSaveSessionResponse(context, { referralNumber, referralDate, referralCorrelationId, eon, accountNumber, savedSessionId, crmCustomerId }) { - context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber); - context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate); - context.commit( - storeMutations.UPDATE_REFERRAL_CORRELATION_ID, - referralCorrelationId - ); - context.commit(storeMutations.UPDATE_EON, eon); - context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, accountNumber); - context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId); - context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId); - }, - logPageView( - context, - { - userId, - sessionKey, - pageName, - sessionId, - action, - event, - shouldUseSessionId, - experimentsForUser, - } - ) { - var payload = { - userId: userId, - sessionKey: sessionKey, - sessionId: sessionId, - pageName: pageName, - applicationName: applicationConfig.APPLICATION_NAME, - action: action, - event: event, - shouldUseSessionId: shouldUseSessionId, - experimentsForUser: experimentsForUser, - }; - - return globalMethods.callHttpClient({ - method: endpoints.LogPageView.method, - endpoint: endpoints.LogPageView.url, - payload: payload, - logApiCall: false, - }); - }, - logCustomEvent( - context, - { - userId, - sessionKey, - pageName, - sessionId, - category, - action, - label, - value, - shouldUseSessionId, - experimentsForUser, - } - ) { - var payload = { - userId: userId, - sessionKey: sessionKey, - sessionId: sessionId, - pageName: pageName, - applicationName: applicationConfig.APPLICATION_NAME, - category: category, - action: action, - label: label, - value: value, - shouldUseSessionId: shouldUseSessionId, - experimentsForUser: experimentsForUser, - }; - - return globalMethods.callHttpClient({ - method: endpoints.LogCustomEvent.method, - endpoint: endpoints.LogCustomEvent.url, - payload: payload, - logApiCall: false, - }); - }, - initializeSession(context, { userId, sessionId, userAgent, referrer }) { - var payload = { - applicationName: applicationConfig.APPLICATION_NAME, - userId: userId, - deviceId: userId, - sessionId: sessionId, - userAgent: userAgent, - operatorId: "WEB", - userName: "SafeliteConceptFunnel", - referrer: referrer, - }; - - return globalMethods.callHttpClient({ - method: endpoints.InitializeSession.method, - endpoint: endpoints.InitializeSession.url, - payload: payload, - logApiCall: false, - }); - }, - - // Misc Actions - setReferralInformation( - context, - { referralNumber, referralDate, referralCorrelationId, eon } - ) { - context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber); - context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate); - context.commit( - storeMutations.UPDATE_REFERRAL_CORRELATION_ID, - referralCorrelationId - ); - context.commit(storeMutations.UPDATE_EON, eon); - }, - - GetExperimentsByUser(context, { userId }) { - return globalMethods.callHttpClient({ - method: endpoints.GetExperimentsByUser.method, - endpoint: `${endpoints.GetExperimentsByUser.url}/${userId}`, - payload: {}, - }); - }, - - async runExperimentsForTrigger( - context, - { userId, triggerEvent, triggerValue } - ) { - if (triggerEvent == experimentTriggers.SITE_ENTRY) { - context.commit(storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, true); - } - - var payload = { - applicationName: applicationConfig.APPLICATION_NAME, - userId: userId, - triggerEvent: triggerEvent, - triggerValue: triggerValue, - experimentOrder: context.getters.experimentOrder, - }; - - const response = await globalMethods.callHttpClient({ - method: endpoints.RunExperimentsForTrigger.method, - endpoint: endpoints.RunExperimentsForTrigger.url, - payload: payload, - }); - - context.commit( - storeMutations.UPDATE_EXPERIMENTS, - response.data.experiments - ); - }, - - getEvoxImage(context, { relativeUrl }) { - return globalMethods.callHttpClient({ - method: endpoints.GetPageData.method, - endpoint: relativeUrl, - payload: {}, - }); - }, - - // PartsOrQuestions API Actions - async getPartsOrQuestions(context) { - const vehicle = context.getters.vehicle; - const damage = context.getters.damage; - const order = context.state.order; - - const carId = vehicle.carId; - const glassArray = damage.glassToReplace; - const zipCode = order.serviceLocation.zipCode; - const vin = vehicle.vin; - - const response = await globalMethods.callHttpClient({ - method: endpoints.GetPartsOrQuestions.method, - endpoint: endpoints.GetPartsOrQuestions.url, - payload: { - carId: carId, - glassPieces: glassArray ?? [], - zip: zipCode, - vin: vin, - }, - }); - - // Flatten location and name properties - response.data.partsOrQuestions.map(glass => { - glass.location = glass.glassPiece.location; - glass.name = glass.glassPiece.name; - delete glass.glassPiece; - return glass; - }); - - return response; - }, - - // Parts API Actions - async getParts(context) { - const vehicle = context.getters.vehicle; - const damage = context.getters.damage; - const order = context.state.order; - - const carId = vehicle.carId; - const glassArray = damage.glassToReplace; - const resultsArray = damage.partQuestionAnswers; - const zipCode = order.serviceLocation.zipCode; - const vin = vehicle.vin; - - // Flatten location and name properties - const response = await globalMethods.callHttpClient({ - method: endpoints.GetParts.method, - endpoint: endpoints.GetParts.url, - payload: { - carId: carId, - glassPieces: glassArray, - answerResults: resultsArray, - zip: zipCode, - vin: vin, - }, - }); - - response.data.glassPieceParts.map(glass => { - glass.location = glass.glassPiece.location; - glass.name = glass.glassPiece.name; - delete glass.glassPiece; - return glass; - }); - - return response; - }, - - getCapabilityQuestions(context, { carId, partNumber }) { - return globalMethods.callHttpClient({ - method: endpoints.GetCapabilityQuestions.method, - endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}`, - }); - }, - - getPartFromCapabilityQuestionAnswer(context, glassLocation) { - const pageData = context.getters.pageData( - fmgPageValues.CAPABILITY_QUESTIONS - ); - - const part = pageData.partsOrQuestions.find( - (x) => x.location === glassLocation - ).parts[0]; - const capabilityQuestionAnswers = - context.getters.damage.capabilityQuestionAnswers; - const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find( - (x) => x.location === glassLocation - ); - - return globalMethods.callHttpClient({ - method: endpoints.GetPartFromCapabilityAnswer.method, - endpoint: endpoints.GetPartFromCapabilityAnswer.url, - payload: { - part, - capabilityAnswerResults: capabilityQuestionAnswersForPart, - }, - }); - }, - - // Session API Actions - saveSession(context) { - const vehicle = context.getters.vehicle; - const damage = context.getters.damage; - const order = context.state.order; - const applicationUser = context.getters.applicationUser; - const lineItems = context.state.order.lineItems; - - return globalMethods.callHttpClient({ - method: endpoints.SaveSession.method, - endpoint: endpoints.SaveSession.url, - payload: { - vehicle: { - carId: vehicle.carId, - year: vehicle.year, - make: vehicle.make, - model: vehicle.model, - style: vehicle.style, - vin: vehicle.vin, - registration: { - firstName: vehicle.registration.firstName, - lastName: vehicle.registration.lastName, - streetAddress: vehicle.registration.address, - city: vehicle.registration.city, - state: vehicle.registration.state, - zipCode: vehicle.registration.zipCode, - licensePlateNumber: vehicle.registration.licensePlate, - }, - }, - damage: { - numberOfChips: damage.numberOfChips, - glassToReplace: damage.glassToReplace, - isRepair: damage.isRepair, - }, - customer: { - emailAddress: order.customer.emailAddress, - }, - lineItems: { - glassParts: lineItems.glassParts, - }, - serviceLocation: { - streetAddress: order.serviceLocation.address, - city: order.serviceLocation.city, - state: order.serviceLocation.state, - zipCode: order.serviceLocation.zipCode, - }, - referralNumber: order.referralNumber?.toString(), // TODO It'd be nice to save these as strings in the first place - referralDate: order.referralDate, - accountNumber: order.accountNumber?.toString(), - existingPromoCode: null, - lastPage: applicationUser.lastPageVisited, - crmCustomerId: applicationUser.crmCustomerId, - savedSessionId: applicationUser.savedSessionId, - experiments: applicationUser.experiments, - }, - }); - }, - loadSession(context, { referralNumber, referralDate, referralCorrelationId, accountNumber }) { - return globalMethods.callHttpClient({ - method: endpoints.LoadSession.method, - endpoint: endpoints.LoadSession.url, - payload: { - referralNumber: referralNumber?.toString(), - referralDate: referralDate, - referralCorrelationId: referralCorrelationId, - accountNumber: accountNumber?.toString() - }, - }).then((response) => { - // clear the state if the existing EON does not equal what is returned from loadSession - if (context.state.order.eon && context.state.order.eon != response.data.eon) { + // Dependency Actions + resetVehicleAndDependencies(context) { + 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_STATE); + context.commit(storeMutations.RESET_GLASS_PARTS_STATE); + }, + resetRegistrationAndDependencies(context) { + context.commit(storeMutations.RESET_REGISTRATION_STATE); + context.commit(storeMutations.RESET_GLASS_PARTS_STATE); + }, + resetPartsAndDependencies(context) { + context.commit(storeMutations.RESET_GLASS_PARTS_STATE); + }, + resetState(context) { context.commit(storeMutations.RESET_STATE); - } - context.commit(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, response.data); - return response; - }); - }, + }, + resetSaveSessionPromise(context) { + context.commit(storeMutations.RESET_SAVE_SESSION_PROMISE); + }, - // Business domain actions + // Content API Actions + getRouteInfo(context, { pageName }) { + return globalMethods.callHttpClient({ + method: endpoints.GetRouteInfo.method, + endpoint: endpoints.GetRouteInfo.url(applicationConfig.APPLICATION_ABBREVIATION), + payload: { + pageName: pageName, + }, + }); + }, + getHomepageName(context) { + return globalMethods.callHttpClient({ + method: endpoints.GetHomepageInfo.method, + endpoint: endpoints.GetHomepageInfo.url(applicationConfig.APPLICATION_ABBREVIATION), + }); + }, + getPageData(context, { pageName }) { + return globalMethods.callHttpClient({ + method: endpoints.GetPageData.method, + endpoint: endpoints.GetPageData.url( + applicationConfig.APPLICATION_ABBREVIATION, + pageName + ), + payload: {}, + }); + }, - // Vehicle - saveVehicleYear(context, year) { - //Reset dependent state when changing - if (context.state.order.vehicle.year !== year) { - context.commit(storeMutations.UPDATE_MAKE, null); - context.commit(storeMutations.UPDATE_MODEL, null); - context.commit(storeMutations.UPDATE_STYLE, null); - context.commit(storeMutations.UPDATE_CAR_ID, null); - context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); - context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + // Analytics Actions + logExperimentExposure(context, { userId, sessionKey, pageName, experiment }) { + return globalMethods.callHttpClient({ + method: endpoints.LogExperimentExposureIfAssigned.method, + endpoint: endpoints.LogExperimentExposureIfAssigned.url, + payload: { + experimentForLogging: { + userId: userId, + experimentUniverseId: experiment.universeId, + experimentUniverseName: experiment.universeName, + experimentTestId: experiment.testId, + experimentTestName: experiment.testName, + experimentVariationId: experiment.variationId, + experimentVariationName: experiment.variationName, + enabled: experiment.isActive, + isExposed: experiment.isExposed, + userPartitionNumber: experiment.userPartitionNumber, + assignmentId: experiment.assignmentId, + sessionKey: sessionKey, + pageName: pageName, + }, + }, + }); + }, - context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + // Misc Actions + updateStoreWithSaveSessionResponse( + context, + { + referralNumber, + referralDate, + referralCorrelationId, + eon, + accountNumber, + savedSessionId, + crmCustomerId, + } + ) { + context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber); + context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate); + context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId); + context.commit(storeMutations.UPDATE_EON, eon); + context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, accountNumber); + context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId); + context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId); + }, + logPageView( + context, + { + userId, + sessionKey, + pageName, + sessionId, + action, + event, + shouldUseSessionId, + experimentsForUser, + } + ) { + var payload = { + userId: userId, + sessionKey: sessionKey, + sessionId: sessionId, + pageName: pageName, + applicationName: applicationConfig.APPLICATION_NAME, + action: action, + event: event, + shouldUseSessionId: shouldUseSessionId, + experimentsForUser: experimentsForUser, + }; - //Save new values - context.commit(storeMutations.UPDATE_YEAR, year); - } - }, - saveVehicleMake(context, make) { - //Reset dependent state when changing - if (context.state.order.vehicle.make !== make) { - context.commit(storeMutations.UPDATE_MODEL, null); - context.commit(storeMutations.UPDATE_STYLE, null); - context.commit(storeMutations.UPDATE_CAR_ID, null); - context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); - context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + return globalMethods.callHttpClient({ + method: endpoints.LogPageView.method, + endpoint: endpoints.LogPageView.url, + payload: payload, + logApiCall: false, + }); + }, + logCustomEvent( + context, + { + userId, + sessionKey, + pageName, + sessionId, + category, + action, + label, + value, + shouldUseSessionId, + experimentsForUser, + } + ) { + var payload = { + userId: userId, + sessionKey: sessionKey, + sessionId: sessionId, + pageName: pageName, + applicationName: applicationConfig.APPLICATION_NAME, + category: category, + action: action, + label: label, + value: value, + shouldUseSessionId: shouldUseSessionId, + experimentsForUser: experimentsForUser, + }; - context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + return globalMethods.callHttpClient({ + method: endpoints.LogCustomEvent.method, + endpoint: endpoints.LogCustomEvent.url, + payload: payload, + logApiCall: false, + }); + }, + initializeSession(context, { userId, sessionId, userAgent, referrer }) { + var payload = { + applicationName: applicationConfig.APPLICATION_NAME, + userId: userId, + deviceId: userId, + sessionId: sessionId, + userAgent: userAgent, + operatorId: "WEB", + userName: "SafeliteConceptFunnel", + referrer: referrer, + }; - //Save new values - context.commit(storeMutations.UPDATE_MAKE, make); - } - }, - saveVehicleModel(context, model) { - //Reset dependent state when changing - if (context.state.order.vehicle.model !== model) { - context.commit(storeMutations.UPDATE_STYLE, null); - context.commit(storeMutations.UPDATE_CAR_ID, null); - context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); - context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + return globalMethods.callHttpClient({ + method: endpoints.InitializeSession.method, + endpoint: endpoints.InitializeSession.url, + payload: payload, + logApiCall: false, + }); + }, - context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + // Misc Actions + setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId, eon }) { + context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber); + context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate); + context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId); + context.commit(storeMutations.UPDATE_EON, eon); + }, - //Save new values - context.commit(storeMutations.UPDATE_MODEL, model); - } - }, - saveVehicleStyle(context, style) { - //Reset dependent state when changing - if (context.state.order.vehicle.style !== style) { - context.commit(storeMutations.UPDATE_CAR_ID, null); - context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); - context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + GetExperimentsByUser(context, { userId }) { + return globalMethods.callHttpClient({ + method: endpoints.GetExperimentsByUser.method, + endpoint: `${endpoints.GetExperimentsByUser.url}/${userId}`, + payload: {}, + }); + }, - context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + async runExperimentsForTrigger(context, { userId, triggerEvent, triggerValue }) { + if (triggerEvent == experimentTriggers.SITE_ENTRY) { + context.commit(storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, true); + } - //Save new values - context.commit(storeMutations.UPDATE_STYLE, style); - } - }, - saveVehicleDamage( - context, - { isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount } - ) { - const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort(); - const isGlassToReplaceTheSame = - context.state.order.damage.glassToReplace?.length === - selectedGlassToReplace.length && - context.state.order.damage.glassToReplace - .slice() - .sort() - .every( - (obj, index) => - obj.glassLocation === - selectedGlassPassedInSorted[index].glassLocation && - obj.glassName === selectedGlassPassedInSorted[index].glassName + var payload = { + applicationName: applicationConfig.APPLICATION_NAME, + userId: userId, + triggerEvent: triggerEvent, + triggerValue: triggerValue, + experimentOrder: context.getters.experimentOrder, + }; + + const response = await globalMethods.callHttpClient({ + method: endpoints.RunExperimentsForTrigger.method, + endpoint: endpoints.RunExperimentsForTrigger.url, + payload: payload, + }); + + context.commit(storeMutations.UPDATE_EXPERIMENTS, response.data.experiments); + }, + + getEvoxImage(context, { relativeUrl }) { + return globalMethods.callHttpClient({ + method: endpoints.GetPageData.method, + endpoint: relativeUrl, + payload: {}, + }); + }, + + // PartsOrQuestions API Actions + async getPartsOrQuestions(context) { + const vehicle = context.getters.vehicle; + const damage = context.getters.damage; + const order = context.state.order; + + const carId = vehicle.carId; + const glassArray = damage.glassToReplace; + const zipCode = order.serviceLocation.zipCode; + const vin = vehicle.vin; + + const response = await globalMethods.callHttpClient({ + method: endpoints.GetPartsOrQuestions.method, + endpoint: endpoints.GetPartsOrQuestions.url, + payload: { + carId: carId, + glassPieces: glassArray ?? [], + zip: zipCode, + vin: vin, + }, + }); + + // Flatten location and name properties + response.data.partsOrQuestions.map((glass) => { + glass.location = glass.glassPiece.location; + glass.name = glass.glassPiece.name; + delete glass.glassPiece; + return glass; + }); + + return response; + }, + + // Parts API Actions + async getParts(context) { + const vehicle = context.getters.vehicle; + const damage = context.getters.damage; + const order = context.state.order; + + const carId = vehicle.carId; + const glassArray = damage.glassToReplace; + const resultsArray = damage.partQuestionAnswers; + const zipCode = order.serviceLocation.zipCode; + const vin = vehicle.vin; + + // Flatten location and name properties + const response = await globalMethods.callHttpClient({ + method: endpoints.GetParts.method, + endpoint: endpoints.GetParts.url, + payload: { + carId: carId, + glassPieces: glassArray, + answerResults: resultsArray, + zip: zipCode, + vin: vin, + }, + }); + + response.data.glassPieceParts.map((glass) => { + glass.location = glass.glassPiece.location; + glass.name = glass.glassPiece.name; + delete glass.glassPiece; + return glass; + }); + + return response; + }, + + getCapabilityQuestions(context, { carId, partNumber }) { + return globalMethods.callHttpClient({ + method: endpoints.GetCapabilityQuestions.method, + endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}`, + }); + }, + + getPartFromCapabilityQuestionAnswer(context, glassLocation) { + const pageData = context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS); + + const part = pageData.partsOrQuestions.find((x) => x.location === glassLocation).parts[0]; + const capabilityQuestionAnswers = context.getters.damage.capabilityQuestionAnswers; + const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find( + (x) => x.location === glassLocation ); - const isWindshieldRepairTheSame = - isWindshieldRepair === context.state.order.damage.isRepair; - const isChipCountTheSame = selectedWindshieldChipCount === context.state.order.damage.numberOfChips; + return globalMethods.callHttpClient({ + method: endpoints.GetPartFromCapabilityAnswer.method, + endpoint: endpoints.GetPartFromCapabilityAnswer.url, + payload: { + part, + capabilityAnswerResults: capabilityQuestionAnswersForPart, + }, + }); + }, - const isDamageChanging = - !isGlassToReplaceTheSame || - !isWindshieldRepairTheSame || - (isWindshieldRepair && !isChipCountTheSame); + // Session API Actions + saveSession(context) { + const vehicle = context.getters.vehicle; + const damage = context.getters.damage; + const order = context.state.order; + const applicationUser = context.getters.applicationUser; + const lineItems = context.state.order.lineItems; - if (isDamageChanging) { - //Reset dependent state when changing - context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + return globalMethods.callHttpClient({ + method: endpoints.SaveSession.method, + endpoint: endpoints.SaveSession.url, + payload: { + vehicle: { + carId: vehicle.carId, + year: vehicle.year, + make: vehicle.make, + model: vehicle.model, + style: vehicle.style, + vin: vehicle.vin, + registration: { + firstName: vehicle.registration.firstName, + lastName: vehicle.registration.lastName, + streetAddress: vehicle.registration.address, + city: vehicle.registration.city, + state: vehicle.registration.state, + zipCode: vehicle.registration.zipCode, + licensePlateNumber: vehicle.registration.licensePlate, + }, + }, + damage: { + numberOfChips: damage.numberOfChips, + glassToReplace: damage.glassToReplace, + isRepair: damage.isRepair, + }, + customer: { + emailAddress: order.customer.emailAddress, + }, + lineItems: { + glassParts: lineItems.glassParts, + }, + serviceLocation: { + streetAddress: order.serviceLocation.address, + city: order.serviceLocation.city, + state: order.serviceLocation.state, + zipCode: order.serviceLocation.zipCode, + }, + referralNumber: order.referralNumber?.toString(), // TODO It'd be nice to save these as strings in the first place + referralDate: order.referralDate, + accountNumber: order.accountNumber?.toString(), + existingPromoCode: null, + lastPage: applicationUser.lastPageVisited, + crmCustomerId: applicationUser.crmCustomerId, + savedSessionId: applicationUser.savedSessionId, + experiments: applicationUser.experiments, + }, + }); + }, + loadSession(context, { referralNumber, referralDate, referralCorrelationId, accountNumber }) { + return globalMethods + .callHttpClient({ + method: endpoints.LoadSession.method, + endpoint: endpoints.LoadSession.url, + payload: { + referralNumber: referralNumber?.toString(), + referralDate: referralDate, + referralCorrelationId: referralCorrelationId, + accountNumber: accountNumber?.toString(), + }, + }) + .then((response) => { + // clear the state if the existing EON does not equal what is returned from loadSession + if (context.state.order.eon && context.state.order.eon != response.data.eon) { + context.commit(storeMutations.RESET_STATE); + } + context.commit(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, response.data); + return response; + }); + }, - // Save new values - context.commit(storeMutations.UPDATE_IS_REPAIR, isWindshieldRepair); - context.commit( - storeMutations.UPDATE_NUMBER_OF_CHIPS, - isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null - ); - context.commit( - storeMutations.UPDATE_GLASS_TO_REPLACE, - selectedGlassToReplace - ); - } - }, + // Business domain actions - // Vin lookup - saveVinLookup( - context, - { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo } - ) { - //Reset dependent state when changing - if (vehicleInfo.vin !== context.state.order.vehicle.vin) { - context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + // Vehicle + saveVehicleYear(context, year) { + //Reset dependent state when changing + if (context.state.order.vehicle.year !== year) { + context.commit(storeMutations.UPDATE_MAKE, null); + context.commit(storeMutations.UPDATE_MODEL, null); + context.commit(storeMutations.UPDATE_STYLE, null); + context.commit(storeMutations.UPDATE_CAR_ID, null); + context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); - if (!isSelectedGlassAvailableForVehicle) { - context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - } + context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); - //Save new values - context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); - context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); - } - }, - saveRegistrationLicensePlateLookup( - context, - { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo } - ) { - //Reset dependent state when changing - if ( - registrationInfo?.licensePlate !== - context.state.order.vehicle.registration?.licensePlate + //Save new values + context.commit(storeMutations.UPDATE_YEAR, year); + } + }, + saveVehicleMake(context, make) { + //Reset dependent state when changing + if (context.state.order.vehicle.make !== make) { + context.commit(storeMutations.UPDATE_MODEL, null); + context.commit(storeMutations.UPDATE_STYLE, null); + context.commit(storeMutations.UPDATE_CAR_ID, null); + context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + + context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + + //Save new values + context.commit(storeMutations.UPDATE_MAKE, make); + } + }, + saveVehicleModel(context, model) { + //Reset dependent state when changing + if (context.state.order.vehicle.model !== model) { + context.commit(storeMutations.UPDATE_STYLE, null); + context.commit(storeMutations.UPDATE_CAR_ID, null); + context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + + context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + + //Save new values + context.commit(storeMutations.UPDATE_MODEL, model); + } + }, + saveVehicleStyle(context, style) { + //Reset dependent state when changing + if (context.state.order.vehicle.style !== style) { + context.commit(storeMutations.UPDATE_CAR_ID, null); + context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + + context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + + //Save new values + context.commit(storeMutations.UPDATE_STYLE, style); + } + }, + saveVehicleDamage( + context, + { isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount } ) { - context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); + const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort(); + const isGlassToReplaceTheSame = + context.state.order.damage.glassToReplace?.length === selectedGlassToReplace.length && + context.state.order.damage.glassToReplace + .slice() + .sort() + .every( + (obj, index) => + obj.glassLocation === selectedGlassPassedInSorted[index].glassLocation && + obj.glassName === selectedGlassPassedInSorted[index].glassName + ); + const isWindshieldRepairTheSame = + isWindshieldRepair === context.state.order.damage.isRepair; - if (!isSelectedGlassAvailableForVehicle) { - context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); - } + const isChipCountTheSame = + selectedWindshieldChipCount === context.state.order.damage.numberOfChips; - //Save new values - context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); - context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); - } - }, - saveRegistrationAddressLookup( - context, - { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo } - ) { - //Reset dependent state when changing - if ( - registrationInfo?.address !== - context.state.order.vehicle.registration?.address || - registrationInfo?.city !== - context.state.order.vehicle.registration?.city || - registrationInfo?.state !== - context.state.order.vehicle.registration?.state || - registrationInfo?.zipCode !== - context.state.order.vehicle.registration?.zipCode || - registrationInfo?.firstName !== - context.state.order.vehicle.registration?.firstName || - registrationInfo?.lastName !== - context.state.order.vehicle.registration?.lastName + const isDamageChanging = + !isGlassToReplaceTheSame || + !isWindshieldRepairTheSame || + (isWindshieldRepair && !isChipCountTheSame); + + if (isDamageChanging) { + //Reset dependent state when changing + context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + + // Save new values + context.commit(storeMutations.UPDATE_IS_REPAIR, isWindshieldRepair); + context.commit( + storeMutations.UPDATE_NUMBER_OF_CHIPS, + isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null + ); + context.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, selectedGlassToReplace); + } + }, + + // Vin lookup + saveVinLookup(context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) { + //Reset dependent state when changing + if (vehicleInfo.vin !== context.state.order.vehicle.vin) { + context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + + if (!isSelectedGlassAvailableForVehicle) { + context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); + } + + //Save new values + context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); + context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); + } + }, + saveRegistrationLicensePlateLookup( + context, + { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo } ) { - context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); + //Reset dependent state when changing + if ( + registrationInfo?.licensePlate !== + context.state.order.vehicle.registration?.licensePlate + ) { + context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - if (!isSelectedGlassAvailableForVehicle) { - context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); - } + if (!isSelectedGlassAvailableForVehicle) { + context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + } - //Save new values - context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); - context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); - } - }, - savePartQuestionAnswers(context, partQuestionAnswersArray) { - // if part question answers have changed, reset subsequent question answers - const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( - context.getters.damage.partQuestionAnswers, - "result" - ); - const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue( - partQuestionAnswersArray, - "result" - ); - const havePartQuestionAnswersChanged = - sortedPreviousResultsArray?.length !== - sortedPartQuestionAnswersArray.length || - !sortedPreviousResultsArray?.every( - (x, i) => x.result === sortedPartQuestionAnswersArray[i].result - ); + //Save new values + context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); + context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); + } + }, + saveRegistrationAddressLookup( + context, + { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo } + ) { + //Reset dependent state when changing + if ( + registrationInfo?.address !== context.state.order.vehicle.registration?.address || + registrationInfo?.city !== context.state.order.vehicle.registration?.city || + registrationInfo?.state !== context.state.order.vehicle.registration?.state || + registrationInfo?.zipCode !== context.state.order.vehicle.registration?.zipCode || + registrationInfo?.firstName !== context.state.order.vehicle.registration?.firstName || + registrationInfo?.lastName !== context.state.order.vehicle.registration?.lastName + ) { + context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - if (havePartQuestionAnswersChanged) { - context.commit(storeMutations.UPDATE_GLASS_PARTS, null); - context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null); - context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); - context.commit(storeMutations.UPDATE_PAGE_DATA, { - page: fmgPageValues.VEHICLE_PARTS, - data: null, - }); - context.commit(storeMutations.UPDATE_PAGE_DATA, { - page: fmgPageValues.MOLDING_QUESTIONS, - data: null, - }); - context.commit(storeMutations.UPDATE_PAGE_DATA, { - page: fmgPageValues.CAPABILITY_QUESTIONS, - data: null, - }); - } + if (!isSelectedGlassAvailableForVehicle) { + context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + } - //Save new values - context.commit( - storeMutations.UPDATE_PART_QUESTION_ANSWERS, - partQuestionAnswersArray - ); - }, - resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, matchedParts) { - const partsOrQuestionsDataToCompareWith = - context.getters.pageData(fmgPageValues.MOLDING_QUESTIONS) - ?.partsOrQuestions ?? - context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS) - ?.partsOrQuestions ?? - []; + //Save new values + context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); + context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); + } + }, + savePartQuestionAnswers(context, partQuestionAnswersArray) { + // if part question answers have changed, reset subsequent question answers + const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( + context.getters.damage.partQuestionAnswers, + "result" + ); + const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue( + partQuestionAnswersArray, + "result" + ); + const havePartQuestionAnswersChanged = + sortedPreviousResultsArray?.length !== sortedPartQuestionAnswersArray.length || + !sortedPreviousResultsArray?.every( + (x, i) => x.result === sortedPartQuestionAnswersArray[i].result + ); - function getAllPartNumbers(partsOrQuestions) { - return partsOrQuestions[0]?.parts - ? [...partsOrQuestions] - .map((glass) => glass.parts) - .flat() - .map((part) => part.partNumber) - .filter((partNumber) => !partNumber.toUpperCase().includes("FEE")) - .sort() - .join(",") - : []; - } + if (havePartQuestionAnswersChanged) { + context.commit(storeMutations.UPDATE_GLASS_PARTS, null); + context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null); + context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); + context.commit(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.VEHICLE_PARTS, + data: null, + }); + context.commit(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: null, + }); + context.commit(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: null, + }); + } - const previouslySelectedPartNumbers = getAllPartNumbers( - partsOrQuestionsDataToCompareWith - ); - const currentlySelectedPartNumbers = getAllPartNumbers(matchedParts); + //Save new values + context.commit(storeMutations.UPDATE_PART_QUESTION_ANSWERS, partQuestionAnswersArray); + }, + resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, matchedParts) { + const partsOrQuestionsDataToCompareWith = + context.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)?.partsOrQuestions ?? + context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS)?.partsOrQuestions ?? + []; - const haveSelectedVehiclePartsChanged = - previouslySelectedPartNumbers !== currentlySelectedPartNumbers; + function getAllPartNumbers(partsOrQuestions) { + return partsOrQuestions[0]?.parts + ? [...partsOrQuestions] + .map((glass) => glass.parts) + .flat() + .map((part) => part.partNumber) + .filter((partNumber) => !partNumber.toUpperCase().includes("FEE")) + .sort() + .join(",") + : []; + } - if (haveSelectedVehiclePartsChanged) { - context.commit(storeMutations.UPDATE_GLASS_PARTS, null); - context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null); - context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); - context.commit(storeMutations.UPDATE_PAGE_DATA, { - page: fmgPageValues.MOLDING_QUESTIONS, - data: null, - }); - context.commit(storeMutations.UPDATE_PAGE_DATA, { - page: fmgPageValues.CAPABILITY_QUESTIONS, - data: null, - }); - } - }, - saveMoldingQuestionAnswers(context, moldingQuestionAnswers) { - const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( - context.getters.damage.moldingQuestionAnswers, - "partNum" - ); - const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue( - moldingQuestionAnswers, - "partNum" - ); - const haveMoldingQuestionAnswersChanged = - sortedPreviousResultsArray?.length !== - sortedMoldingQuestionAnswersArray.length || - !sortedPreviousResultsArray?.every( - (x, i) => x.partNum === sortedMoldingQuestionAnswersArray[i].partNum - ); + const previouslySelectedPartNumbers = getAllPartNumbers(partsOrQuestionsDataToCompareWith); + const currentlySelectedPartNumbers = getAllPartNumbers(matchedParts); - if (haveMoldingQuestionAnswersChanged) { - context.commit(storeMutations.UPDATE_GLASS_PARTS, null); - context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); - context.commit(storeMutations.UPDATE_PAGE_DATA, { - page: fmgPageValues.CAPABILITY_QUESTIONS, - data: null, - }); - } + const haveSelectedVehiclePartsChanged = + previouslySelectedPartNumbers !== currentlySelectedPartNumbers; - //Save new values - context.commit( - storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, - moldingQuestionAnswers - ); - }, - saveCapabilityQuestionAnswers(context, capabilityQuestionAnswers) { - const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( - context.getters.damage.capabilityQuestionAnswers, - "result" - ); - const sortedCapabilityQuestionAnswersArray = - sortArrayOfObjectsByPropertyValue(capabilityQuestionAnswers, "result"); - const haveCapabilityQuestionAnswersChanged = - sortedPreviousResultsArray?.length !== - sortedCapabilityQuestionAnswersArray.length || - !sortedPreviousResultsArray?.every( - (x, i) => x.result === sortedCapabilityQuestionAnswersArray[i].result - ); + if (haveSelectedVehiclePartsChanged) { + context.commit(storeMutations.UPDATE_GLASS_PARTS, null); + context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null); + context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); + context.commit(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: null, + }); + context.commit(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: null, + }); + } + }, + saveMoldingQuestionAnswers(context, moldingQuestionAnswers) { + const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( + context.getters.damage.moldingQuestionAnswers, + "partNum" + ); + const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue( + moldingQuestionAnswers, + "partNum" + ); + const haveMoldingQuestionAnswersChanged = + sortedPreviousResultsArray?.length !== sortedMoldingQuestionAnswersArray.length || + !sortedPreviousResultsArray?.every( + (x, i) => x.partNum === sortedMoldingQuestionAnswersArray[i].partNum + ); - if (haveCapabilityQuestionAnswersChanged) { - context.commit(storeMutations.UPDATE_GLASS_PARTS, null); - } + if (haveMoldingQuestionAnswersChanged) { + context.commit(storeMutations.UPDATE_GLASS_PARTS, null); + context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); + context.commit(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: null, + }); + } - //Save new values - context.commit( - storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, - capabilityQuestionAnswers - ); - }, - // Misc order actions - saveServiceLocation(context, serviceLocationInfo) { - context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo); - }, - saveEmail(context, email) { - context.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, email); - }, - saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) { - //Reset dependent state when changing - if (vehicleInfo.vin !== context.state.order.vehicle.vin) { - if (!isSelectedGlassAvailableForVehicle) { - context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); - } + //Save new values + context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, moldingQuestionAnswers); + }, + saveCapabilityQuestionAnswers(context, capabilityQuestionAnswers) { + const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( + context.getters.damage.capabilityQuestionAnswers, + "result" + ); + const sortedCapabilityQuestionAnswersArray = sortArrayOfObjectsByPropertyValue( + capabilityQuestionAnswers, + "result" + ); + const haveCapabilityQuestionAnswersChanged = + sortedPreviousResultsArray?.length !== sortedCapabilityQuestionAnswersArray.length || + !sortedPreviousResultsArray?.every( + (x, i) => x.result === sortedCapabilityQuestionAnswersArray[i].result + ); - //Save new values - context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); - } - }, - saveGlassParts(context, parts) { - context.commit(storeMutations.UPDATE_GLASS_PARTS, parts); - }, - clearVin(context) { - context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); - }, + if (haveCapabilityQuestionAnswersChanged) { + context.commit(storeMutations.UPDATE_GLASS_PARTS, null); + } + + //Save new values + context.commit( + storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, + capabilityQuestionAnswers + ); + }, + // Misc order actions + saveServiceLocation(context, serviceLocationInfo) { + context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo); + }, + saveEmail(context, email) { + context.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, email); + }, + saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) { + //Reset dependent state when changing + if (vehicleInfo.vin !== context.state.order.vehicle.vin) { + if (!isSelectedGlassAvailableForVehicle) { + context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); + context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); + } + + //Save new values + context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); + } + }, + saveGlassParts(context, parts) { + context.commit(storeMutations.UPDATE_GLASS_PARTS, parts); + }, + clearVin(context) { + context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); + }, }; export default createStore({ - plugins: [createPersistedState()], - // IMPORTANT: Be VERY careful when modifying these fields for at least a few reasons: - // * The CMS can reference the fields by name - // * Return users may have a previous "version" of the model, and we don't want - // them to have a breaking experience, because the model might have changed. - state, - mutations, - getters, - actions, + plugins: [createPersistedState()], + // IMPORTANT: Be VERY careful when modifying these fields for at least a few reasons: + // * The CMS can reference the fields by name + // * Return users may have a previous "version" of the model, and we don't want + // them to have a breaking experience, because the model might have changed. + state, + mutations, + getters, + actions, }); // Private Functions function getHasRecalibrationPart(state) { - var hasRequiresRecalibration = getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "requiresRecalibration")?.length > 0; - var hasRecalibrationType = getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "recalibrationType")?.length > 0; + var hasRequiresRecalibration = + getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.lineItems.glassParts, + "requiresRecalibration" + )?.length > 0; + var hasRecalibrationType = + getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.lineItems.glassParts, + "recalibrationType" + )?.length > 0; - if (hasRequiresRecalibration) { - if (hasRecalibrationType) { // Has both 'requiresRecalibration' and 'recalibrationType' and 'recalibrationType' - return getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "recalibrationType")[0].toLowerCase() != "unknown"; - } else { // Has 'requiresRecalibration' but no 'recalibrationType' at all - return true; + if (hasRequiresRecalibration) { + if (hasRecalibrationType) { + // Has both 'requiresRecalibration' and 'recalibrationType' and 'recalibrationType' + return ( + getNonFalseValuesOfPropertyInArrayOfObjects( + state.order.lineItems.glassParts, + "recalibrationType" + )[0].toLowerCase() != "unknown" + ); + } else { + // Has 'requiresRecalibration' but no 'recalibrationType' at all + return true; + } + } else { + // Does not have 'requiresRecalibration' + return false; } - } else { - // Does not have 'requiresRecalibration' - return false; - } } function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) { - if (!arrayOfObjects) return null; + if (!arrayOfObjects) return null; - return arrayOfObjects.sort((a, b) => { - if (a[propertyName] < b[propertyName]) return -1; - else if (a[propertyName] > b[propertyName]) return 1; - else return 0; - }); + return arrayOfObjects.sort((a, b) => { + if (a[propertyName] < b[propertyName]) return -1; + else if (a[propertyName] > b[propertyName]) return 1; + else return 0; + }); } diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 6ad366989..4409964b4 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -3,2285 +3,2665 @@ import { mutations, state, actions, getters } from "@/store"; import { storeMutations } from "@/constants/store-mutations"; import { storeActions } from "@/constants/store-actions"; import { experimentTriggers } from "@/constants/experiments"; -import { fmgPageValues } from "@/router/router-constants/fmgPage-values" +import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; // Mock global method globalMethods.callHttpClient = jest.fn(); - describe("Mutations", () => { + it("Updates vehicle year in state", () => { + // Arrange + const storeState = state; - it("Updates vehicle year in state", () => { - // Arrange - const storeState = state; + // Act + mutations.updateYear(storeState, "2019"); - // Act - mutations.updateYear(storeState, "2019"); - - // Assert - expect(storeState.order.vehicle.year).toEqual("2019"); - }); - - it("Updates vehicle make in state", () => { - // Arrange - const storeState = state; - - // Act - mutations.updateMake(storeState, "Acura"); - - // Assert - expect(storeState.order.vehicle.make).toEqual("Acura"); - }); - - it("Updates vehicle model in state", () => { - // Arrange - const storeState = state; - - // Act - mutations.updateModel(storeState, "ILX"); - - // Assert - expect(storeState.order.vehicle.model).toEqual("ILX"); - }); - - it("Updates vehicle style in state", () => { - // Arrange - const storeState = state; - - // Act - mutations.updateStyle(storeState, "4 DOOR SEDAN"); - - // Assert - expect(storeState.order.vehicle.style).toEqual("4 DOOR SEDAN"); - }); - - it("Updates vehicle carId in state", () => { - // Arrange - const storeState = state; - - // Act - mutations.updateCarId(storeState, "C0000001"); - - // Assert - expect(storeState.order.vehicle.carId).toEqual("C0000001"); - }); - - it("Updates vehicle vehicle category in state", () => { - // Arrange - const storeState = state; - - // Act - mutations.updateVehicleCategory(storeState, "CAR"); - - // Assert - expect(storeState.order.vehicle.category).toEqual("CAR"); - }); - - it("Remove item to eventBus in state", () => { - // Arrange - const storeState = state; - const event = { category: "CategoryOne", subCategory: "SubCategoryOne" } - - // Act / Assert - mutations.addEventToBus(storeState, event); - expect(storeState.applicationUser.eventBus).toEqual([event]); - - // Act / Assert - mutations.removeEventFromBus(storeState, event); - expect(storeState.applicationUser.eventBus).toEqual([]); - - }); - - it("Adds item to eventBus in state", () => { - // Arrange - const storeState = state; - - // Act - mutations.addEventToBus(storeState, { EventOne: "ValueOne" }); - - // Assert - expect(storeState.applicationUser.eventBus).toEqual([{ EventOne: "ValueOne" }]); - }); - - it("resetVehicleState, should set fields to null", () => { - // Arrange - const storeState = state; - - mutations.updateYear(storeState, "2019"); - mutations.updateMake(storeState, "Acura"); - mutations.updateModel(storeState, "ILX"); - mutations.updateStyle(storeState, "4 DOOR SEDAN"); - mutations.updateCarId(storeState, "C0000001"); - mutations.updateVehicleCategory(storeState, "CAR"); - - // Expect - expect(storeState.order.vehicle.year).toEqual("2019"); - expect(storeState.order.vehicle.make).toEqual("Acura"); - expect(storeState.order.vehicle.model).toEqual("ILX"); - expect(storeState.order.vehicle.style).toEqual("4 DOOR SEDAN"); - expect(storeState.order.vehicle.carId).toEqual("C0000001"); - expect(storeState.order.vehicle.category).toEqual("CAR"); - - // Act - mutations.resetVehicleState(storeState); - - // Expect - expect(storeState.order.vehicle.year).toEqual(null); - expect(storeState.order.vehicle.make).toEqual(null); - expect(storeState.order.vehicle.model).toEqual(null); - expect(storeState.order.vehicle.style).toEqual(null); - expect(storeState.order.vehicle.carId).toEqual(null); - expect(storeState.order.vehicle.category).toEqual(null); - - }); - - it("resetDamageState, should set fields to null", () => { - // Arrange - const storeState = state; - - storeState.order.damage = { - isRepair: true, - numberOfChips: 2, - glassToReplace: [{ location: 'Rear', name: 'Stationary' }] - } - - // Expect - expect(storeState.order.damage.isRepair).toEqual(true); - expect(storeState.order.damage.numberOfChips).toEqual(2); - expect(storeState.order.damage.glassToReplace).toStrictEqual([{ location: 'Rear', name: 'Stationary' }]); - - // Act - mutations.resetDamageState(storeState); - - // Expect - expect(storeState.order.damage.isRepair).toEqual(null); - expect(storeState.order.damage.numberOfChips).toEqual(null); - expect(storeState.order.damage.glassToReplace).toEqual(null); - }); - - 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.updateGlassParts(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({}); - }); - - it("updateStateWithOrderInformation, should set order information in state", () => { - // Arrange - const storeState = state; - - // Act - mutations.updateStateWithOrderInformation(storeState, { - referralNumber: 123, - referralDate: new Date().toUTCString(), - referralCorrelationId: "xxx-xxx-xxx", - vehicle: { - year: "2019", - make: "Acura", - model: "ILX", - style: "4 DOOR SEDAN", - carId: "C0000001", - category: "CAR", - registration: {} - }, - damage: { - glassToReplace: ["Windshield"], - isRepair: false, - numberOfChips: 0, - }, - parts: [], - accountNumber: "123456789", - insuranceInfo: {}, - serviceLocation: {}, - customer: {} + // Assert + expect(storeState.order.vehicle.year).toEqual("2019"); }); - // Assert - expect(storeState.order.referralNumber).toEqual(123); - expect(storeState.order.referralCorrelationId).toEqual("xxx-xxx-xxx"); - expect(storeState.order.vehicle.year).toEqual("2019"); - expect(storeState.order.vehicle.make).toEqual("Acura"); - expect(storeState.order.vehicle.model).toEqual("ILX"); - }); + it("Updates vehicle make in state", () => { + // Arrange + const storeState = state; - it("updateInsuranceVerifiedStatus, should set isVerified flag", () => { - // Arrange - const storeState = state; + // Act + mutations.updateMake(storeState, "Acura"); - // Act - mutations.updateInsuranceVerifiedStatus(storeState, true); + // Assert + expect(storeState.order.vehicle.make).toEqual("Acura"); + }); - // Assert - expect(storeState.order.payment.insuranceCoverage.isVerified).toEqual(true); - }); + it("Updates vehicle model in state", () => { + // Arrange + const storeState = state; - it("updateExperiments, should set experiments", () => { - // Arrange - const storeState = state; - const mockExperimentsList = [ - { - universeName: "XYZ", - settings: { - ExperimentSetting: "ExperimentValue" - } - } - ] + // Act + mutations.updateModel(storeState, "ILX"); - // Act - mutations.updateExperiments(storeState, mockExperimentsList); + // Assert + expect(storeState.order.vehicle.model).toEqual("ILX"); + }); - // Assert - expect(storeState.applicationUser.experiments).toEqual(mockExperimentsList); - }); + it("Updates vehicle style in state", () => { + // Arrange + const storeState = state; - it("updateTriggeredSiteEntry, should set triggeredSiteEntry", () => { - // Arrange - const storeState = state; + // Act + mutations.updateStyle(storeState, "4 DOOR SEDAN"); - // Act - mutations.updateTriggeredSiteEntry(storeState, true); + // Assert + expect(storeState.order.vehicle.style).toEqual("4 DOOR SEDAN"); + }); - // Assert - expect(storeState.applicationUser.triggeredSiteEntry).toEqual(true); - }); + it("Updates vehicle carId in state", () => { + // Arrange + const storeState = state; + // Act + mutations.updateCarId(storeState, "C0000001"); + + // Assert + expect(storeState.order.vehicle.carId).toEqual("C0000001"); + }); + + it("Updates vehicle vehicle category in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateVehicleCategory(storeState, "CAR"); + + // Assert + expect(storeState.order.vehicle.category).toEqual("CAR"); + }); + + it("Remove item to eventBus in state", () => { + // Arrange + const storeState = state; + const event = { category: "CategoryOne", subCategory: "SubCategoryOne" }; + + // Act / Assert + mutations.addEventToBus(storeState, event); + expect(storeState.applicationUser.eventBus).toEqual([event]); + + // Act / Assert + mutations.removeEventFromBus(storeState, event); + expect(storeState.applicationUser.eventBus).toEqual([]); + }); + + it("Adds item to eventBus in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.addEventToBus(storeState, { EventOne: "ValueOne" }); + + // Assert + expect(storeState.applicationUser.eventBus).toEqual([{ EventOne: "ValueOne" }]); + }); + + it("resetVehicleState, should set fields to null", () => { + // Arrange + const storeState = state; + + mutations.updateYear(storeState, "2019"); + mutations.updateMake(storeState, "Acura"); + mutations.updateModel(storeState, "ILX"); + mutations.updateStyle(storeState, "4 DOOR SEDAN"); + mutations.updateCarId(storeState, "C0000001"); + mutations.updateVehicleCategory(storeState, "CAR"); + + // Expect + expect(storeState.order.vehicle.year).toEqual("2019"); + expect(storeState.order.vehicle.make).toEqual("Acura"); + expect(storeState.order.vehicle.model).toEqual("ILX"); + expect(storeState.order.vehicle.style).toEqual("4 DOOR SEDAN"); + expect(storeState.order.vehicle.carId).toEqual("C0000001"); + expect(storeState.order.vehicle.category).toEqual("CAR"); + + // Act + mutations.resetVehicleState(storeState); + + // Expect + expect(storeState.order.vehicle.year).toEqual(null); + expect(storeState.order.vehicle.make).toEqual(null); + expect(storeState.order.vehicle.model).toEqual(null); + expect(storeState.order.vehicle.style).toEqual(null); + expect(storeState.order.vehicle.carId).toEqual(null); + expect(storeState.order.vehicle.category).toEqual(null); + }); + + it("resetDamageState, should set fields to null", () => { + // Arrange + const storeState = state; + + storeState.order.damage = { + isRepair: true, + numberOfChips: 2, + glassToReplace: [{ location: "Rear", name: "Stationary" }], + }; + + // Expect + expect(storeState.order.damage.isRepair).toEqual(true); + expect(storeState.order.damage.numberOfChips).toEqual(2); + expect(storeState.order.damage.glassToReplace).toStrictEqual([ + { location: "Rear", name: "Stationary" }, + ]); + + // Act + mutations.resetDamageState(storeState); + + // Expect + expect(storeState.order.damage.isRepair).toEqual(null); + expect(storeState.order.damage.numberOfChips).toEqual(null); + expect(storeState.order.damage.glassToReplace).toEqual(null); + }); + + 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.updateGlassParts(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({}); + }); + + it("updateStateWithOrderInformation, should set order information in state", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateStateWithOrderInformation(storeState, { + referralNumber: 123, + referralDate: new Date().toUTCString(), + referralCorrelationId: "xxx-xxx-xxx", + vehicle: { + year: "2019", + make: "Acura", + model: "ILX", + style: "4 DOOR SEDAN", + carId: "C0000001", + category: "CAR", + registration: {}, + }, + damage: { + glassToReplace: ["Windshield"], + isRepair: false, + numberOfChips: 0, + }, + parts: [], + accountNumber: "123456789", + insuranceInfo: {}, + serviceLocation: {}, + customer: {}, + }); + + // Assert + expect(storeState.order.referralNumber).toEqual(123); + expect(storeState.order.referralCorrelationId).toEqual("xxx-xxx-xxx"); + expect(storeState.order.vehicle.year).toEqual("2019"); + expect(storeState.order.vehicle.make).toEqual("Acura"); + expect(storeState.order.vehicle.model).toEqual("ILX"); + }); + + it("updateInsuranceVerifiedStatus, should set isVerified flag", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateInsuranceVerifiedStatus(storeState, true); + + // Assert + expect(storeState.order.payment.insuranceCoverage.isVerified).toEqual(true); + }); + + it("updateExperiments, should set experiments", () => { + // Arrange + const storeState = state; + const mockExperimentsList = [ + { + universeName: "XYZ", + settings: { + ExperimentSetting: "ExperimentValue", + }, + }, + ]; + + // Act + mutations.updateExperiments(storeState, mockExperimentsList); + + // Assert + expect(storeState.applicationUser.experiments).toEqual(mockExperimentsList); + }); + + it("updateTriggeredSiteEntry, should set triggeredSiteEntry", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateTriggeredSiteEntry(storeState, true); + + // Assert + expect(storeState.applicationUser.triggeredSiteEntry).toEqual(true); + }); }); describe("Actions", () => { - it("getVehicleYears action, should return years array", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: [2023, 2022, 2021] }); - }); - - // Assert - const response = await actions.getVehicleYears(context) - - expect(response.data).toEqual([2023, 2022, 2021]); - }); - - it("lookupVehicleByYmms action, should return car data", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { carId: "C00000001" } }); - }); - - // Assert - const response = await actions.lookupVehicleByYmms(context, "2019", "Acura", "ILX", "4 DOOR SEDAN") - - expect(response.data).toEqual({ carId: "C00000001" }); - }); - - it("lookupVehicleByVin action, should return car data", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { carId: "C00000001" } }); - }); - - // Assert - const response = await actions.lookupVehicleByVin(context, "12345678901234567") - - expect(response.data).toEqual({ carId: "C00000001" }); - }); - - it("lookupVinByPlate action, should return car data", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { carId: "C00000001" } }); - }); - - // Assert - const response = await actions.lookupVinByPlate(context, "12345678901234567") - - expect(response.data).toEqual({ carId: "C00000001" }); - }); - - it("getVehicleMakes action, should return makes list", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: ["Acura", "Honda"] }); - }); - - // Assert - const response = await actions.getVehicleMakes(context, "2019") - - expect(response.data).toEqual(["Acura", "Honda"]); - }); - - it("getVehicleModels action, should return models list", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: ["ILX", "RDX"] }); - }); - - // Assert - const response = await actions.getVehicleModels(context, "2019", "Acura") - - expect(response.data).toEqual(["ILX", "RDX"]); - }); - - it("getVehicleStyles action, should return models list", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { style: "4 DOOR SEDAN" } }); - }); - - // Assert - const response = await actions.getVehicleStyles(context, "2019", "Acura", "ILX") - - expect(response.data).toEqual({ style: "4 DOOR SEDAN" }); - }); - - it("setVehicle action, should get vehicle data and set carId and vehicle category", async () => { - - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { carId: "C00000000", category: "CAR" } }); - }); - - // Assert - const response = await actions.setVehicle(context, "C00000000") - - expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, "C00000000"); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, "CAR"); - expect(response.data).toEqual({ carId: "C00000000", category: "CAR" }); - }); - - it("getDamageOptions action", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: ["Windshield", "DriversFrontDoor"] }); - }); - - const response = await actions.getDamageOptions(context, "C00000000") - - // Assert - expect(response.data).toEqual(["Windshield", "DriversFrontDoor"]); - }); - - it("validateZip action", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: "43201" }); - }); - - const response = await actions.validateZip(context, "C00000000") - - // Assert - expect(response.data).toEqual("43201"); - }); - - it("resetVehicleAndDependencies action", async () => { - - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - await actions.resetVehicleAndDependencies(context) - - expect(commit).toBeCalledWith(storeMutations.RESET_VEHICLE_STATE); - expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_STATE); - expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_STATE); - - }); - - it("resetDamageAndDependencies action", async () => { - - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - await actions.resetDamageAndDependencies(context) - - expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_STATE); - expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE); - - }); - - it("resetRegistrationAndDependencies action", async () => { - - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - await actions.resetRegistrationAndDependencies(context) - - expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_STATE); - expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE); - - }); - - it("resetPartsAndDependencies action", async () => { - - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - await actions.resetPartsAndDependencies(context) - - expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE); - - }); - - it("resetState action", async () => { - - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - await actions.resetState(context) - - expect(commit).toBeCalledWith(storeMutations.RESET_STATE); - - }); - - it("getRouteInfo action, returns route info", async () => { - - // Arrange - const context = state; - - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { Widget: "Data" } }); - }); - - // Act - const response = await actions.getRouteInfo(context, "vehicle-year") - - - expect(response.data).toEqual({ Widget: "Data" }); - - }); - - it("getHomepageName action, returns homepage name", async () => { - - // Arrange - const context = state; - - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { Name: "vehicle-year" } }); - }); - - // Act - const response = await actions.getHomepageName(context) - - - expect(response.data).toEqual({ Name: "vehicle-year" }); - - }); - - it("getPageData action, returns page data", async () => { - - // Arrange - const context = state; - - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { Results: [{ Widget: "Data" }] } }); - }); - - // Act - const response = await actions.getPageData(context, "vehicle-year") - - - expect(response.data).toEqual({ Results: [{ Widget: "Data" }] }); - - }); - - it("getEvoxImage action, returns image url", async () => { - // Arrange - const context = state; - - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { imageUrl: "https://test.com" } }); - }); - - // Act - const response = await actions.getEvoxImage(context, { relativeUrl: "https://relativeurl.com" }); - - - expect(response.data).toEqual({ imageUrl: "https://test.com" }); - }); - - it("saveSession action, returns order information", async () => { - // Arrange - const context = state; - - context.getters = { - vehicle: { - registration: {} - }, - damage: {}, - applicationUser: { - lastPageVisited: "test-page", - crmCustomerId: "xxx-xxx-xxx", - savedSessionId: "xxx-xxx-xxx" - }, - }; - context.state = { - order: { - serviceLocation: {}, - customer: {}, - lineItems: {} - }, - }; - - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { referralNumber: 123 } }); - }); - - // Act - const response = await actions.saveSession(context); - - // Assert - expect(response.data).toEqual({ referralNumber: 123 }); - }); - - - it("loadSession action, returns order information, calls mutation", async () => { - // Arrange - const context = state; - - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { referralNumber: 123 } }); - }); - - const commit = jest.fn(); - - context.commit = commit; - - // Act - const response = await actions.loadSession(context, { referralNumber: "123", referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx" }); - - // Assert - expect(response.data).toEqual({ referralNumber: 123 }); - expect(commit).toBeCalledWith(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, { "referralNumber": 123 }); - }); - - it("loadSession: state doesn't have EON => do not reset state", async () => { - // Arrange - const context = state; - - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { eon: "123" } }); - }); - - context.commit = jest.fn(); - context.state = { - order: { - } - } - - // Act - const response = await actions.loadSession(context, { referralNumber: "123", referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx" }); - - // Assert - expect(response.data.eon).toEqual("123"); - expect(context.commit).not.toBeCalledWith(storeMutations.RESET_STATE); - }); - - it("loadSession eon doesn't match eon in state => reset state", async () => { - // Arrange - const context = state; - - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({ data: { eon: "123" } }); - }); - - context.commit = jest.fn(); - context.state = { - order: { - eon: "456" - } - } - - // Act - const response = await actions.loadSession(context, { referralNumber: "123", referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx" }); - - // Assert - expect(response.data.eon).toEqual("123"); - expect(context.commit).toBeCalledWith(storeMutations.RESET_STATE); - }); - - it("updateStoreWithSaveSessionResponse, should call commit six times", () => { - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - actions.updateStoreWithSaveSessionResponse(context, - { - referralNumber: "123", - referralDate: new Date().toUTCString(), - referralCorrelationId: "xxx-xxx-xxx", - accountNumber: "167132", - savedSessionId: "xxx-xxx-xxx", - crmCustomerId: "xxx-xxx-xxx", - }); - - // Assert - expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_NUMBER, "123"); - expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_DATE, new Date().toUTCString()); - expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, "xxx-xxx-xxx"); - expect(commit).toBeCalledWith(storeMutations.UPDATE_PARENT_ACCT_NUMBER, "167132"); - expect(commit).toBeCalledWith(storeMutations.UPDATE_SAVED_SESSION_ID, "xxx-xxx-xxx"); - expect(commit).toBeCalledWith(storeMutations.UPDATE_CRM_CUSTOMER_ID, "xxx-xxx-xxx"); - }); - - it("logPageView action, should return nothing", async () => { - - // Arrange - const context = state; - var pageEvent = { - action: "", - event: "ENTRY", - } - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({}); - }); - - // Assert - const response = await actions.logPageView(context, { userId: "userId", sessionKey: "sessionKey", pageName: "pageName", sessionId: "sessionId", pageEvent: pageEvent, shouldUseSessionId: false }); - expect(response).toEqual({}); - }); - - it("logCustomEvent action, should return nothing", async () => { - - // Arrange - const context = state; - var customEvent = { - category: "tstCat", - action: "click", - label: "damage", - value: "psych" - }; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({}); - }); - - // Assert - const response = await actions.logCustomEvent(context, { userId: "userId", sessionKey: "sessionKey", pageName: "pageName", sessionId: "sessionId", customEvent: customEvent, shouldUseSessionId: false }); - expect(response).toEqual({}); - }); - - it("initializeSession action, should return nothing", async () => { - - // Arrange - const context = state; - - // Act - globalMethods.callHttpClient.mockImplementation(() => { - return Promise.resolve({}); - }); - - // Assert - const response = await actions.initializeSession(context, { userId: "userId", sessionId: "", userAgent: "", referrer: "", shouldUseSessionId: false }); - expect(response).toEqual({}); - }); - - it("saveVin, should call mutation when CarId is different and selectedGlass is not available for vehicle", () => { - // Arrange - const context = state; - - context.state = { - order: { - vehicle: { - vin: "YYYYY" - } - } - }; - - const commit = jest.fn(); - const dispatch = jest.fn(); - - context.commit = commit; - context.dispatch = dispatch; - - // Act - actions.saveVin(context, { isCarIdDifferent: true, isSelectedGlassAvailableForVehicle: false, vehicleInfo: { carId: 'C010101', vin: "XXXXX" } }); - - // Assert - expect(dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE, { carId: 'C010101', vin: "XXXXX" }); - - }); - - it("saveEmail, should call mutation", () => { - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - actions.saveEmail(context, 'test@safelite.com'); - - // Assert - expect(commit).toBeCalledWith(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, 'test@safelite.com'); - }); - - it("saveServiceLocation, should call mutation", () => { - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - actions.saveServiceLocation(context, { zipCode: "80020" }); - - // Assert - expect(commit).toBeCalledWith(storeMutations.UPDATE_SERVICE_LOCATION, { zipCode: "80020" }); - }); - - it("saveGlassParts, should call mutation", () => { - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - actions.saveGlassParts(context, { glassParts: {} }); - - // Assert - expect(commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, { glassParts: {} }); - }); - - it("clearVin, should call mutation", () => { - // Arrange - const context = state; - const commit = jest.fn(); - - context.commit = commit; - - // Act - actions.clearVin(context); - - // Assert - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); - }); - - it("saveVinLookup, should call mutation if vin is different", () => { - // Arrange - const context = state; - const commit = jest.fn(); - const dispatch = jest.fn(); - - - context.commit = commit; - context.dispatch = dispatch; - - - // Act - const payload = { - isCarIdDifferent: true, - isSelectedGlassAvailableForVehicle: false, - vehicleInfo: { - carId: 'C010101', vin: "XXXXX" - }, - registrationInfo: { - zipCode: "80020" - }, - serviceLocationInfo: { - state: "CO" - }, - customerEmail: "test@safleite.com" - }; - - actions.saveVinLookup(context, payload); - - // Assert - expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - expect(dispatch).toHaveBeenNthCalledWith(2, storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); - expect(dispatch).toHaveBeenNthCalledWith(3, storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE, payload.vehicleInfo); - expect(commit).toBeCalledWith(storeMutations.UPDATE_REGISTRATION, payload.registrationInfo); - }); - - it("saveRegistrationLicensePlateLookup, should call mutation if LP is different", () => { - // Arrange - const context = state; - - context.state = { - order: { - vehicle: { - registration: { - licensePlate: "ABC123" - } - } - } - }; - - const commit = jest.fn(); - const dispatch = jest.fn(); - - context.commit = commit; - context.dispatch = dispatch; - - // Act - const payload = { isCarIdDifferent: true, isSelectedGlassAvailableForVehicle: false, vehicleInfo: { carId: 'C010101', vin: "XXXXX" }, registrationInfo: { zipCode: "80020", licensePlate: "ALQX35" }, serviceLocationInfo: { state: "CO" }, customerEmail: "test@safelite.com" }; - - actions.saveRegistrationLicensePlateLookup(context, payload); - - // Assert - expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - expect(dispatch).toHaveBeenNthCalledWith(2, storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE, payload.vehicleInfo); - expect(commit).toBeCalledWith(storeMutations.UPDATE_REGISTRATION, payload.registrationInfo); - }); - - it("saveRegistrationAddressLookup, should call mutation when address is different", () => { - // Arrange - const context = state; - - context.state = { - order: { - vehicle: { - registration: { - address: "123 Main St" - } - } - } - }; - - const commit = jest.fn(); - const dispatch = jest.fn(); - - context.commit = commit; - context.dispatch = dispatch; - - // Act - const payload = { isCarIdDifferent: true, isSelectedGlassAvailableForVehicle: false, vehicleInfo: { carId: 'C010101', vin: "XXXXX" }, registrationInfo: { zipCode: "80020", address: "123 Marys Ave" }, serviceLocationInfo: { state: "CO" }, customerEmail: "test@safelite.com" }; - - actions.saveRegistrationAddressLookup(context, payload); - - // Assert - expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - expect(dispatch).toHaveBeenNthCalledWith(2, storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE, payload.vehicleInfo); - expect(commit).toBeCalledWith(storeMutations.UPDATE_REGISTRATION, payload.registrationInfo); - }); - - it("saveVehicleYear, should wipe out vehicle info if year changes", () => { - // Arrange - const context = state; - - context.state = { - order: { - vehicle: { - year: "2015" - } - } - }; - - const commit = jest.fn(); - const dispatch = jest.fn(); - - context.commit = commit; - context.dispatch = dispatch; - - // Act - actions.saveVehicleYear(context, "2016"); - - // Assert - expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - expect(dispatch).toHaveBeenNthCalledWith(2, storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - - expect(commit).toBeCalledWith(storeMutations.UPDATE_MAKE, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_MODEL, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_STYLE, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); - }); - - it("saveVehicleMake, should wipe out vehicle info if make changes", () => { - // Arrange - const context = state; - - context.state = { - order: { - vehicle: { - make: "Honda" - } - } - }; - - const commit = jest.fn(); - const dispatch = jest.fn(); - - context.commit = commit; - context.dispatch = dispatch; - - // Act - actions.saveVehicleMake(context, "Toyota"); - - // Assert - expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - expect(dispatch).toHaveBeenNthCalledWith(2, storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - - expect(commit).toBeCalledWith(storeMutations.UPDATE_MODEL, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_STYLE, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); - - }); - - it("saveVehicle model, should wipe out vehicle info if model changes", () => { - // Arrange - const context = state; - - context.state = { - order: { - vehicle: { - model: "Civic" - } - } - }; - - const commit = jest.fn(); - const dispatch = jest.fn(); - - context.commit = commit; - context.dispatch = dispatch; - - // Act - actions.saveVehicleModel(context, "Accord"); - - // Assert - expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - expect(dispatch).toHaveBeenNthCalledWith(2, storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - - expect(commit).toBeCalledWith(storeMutations.UPDATE_STYLE, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); - }); - - it("saveVehicleStyle, should wipe out vehicle info if style changes", () => { - // Arrange - const context = state; - - context.state = { - order: { - vehicle: { - style: "Sedan" - } - } - }; - - const commit = jest.fn(); - const dispatch = jest.fn(); - - context.commit = commit; - context.dispatch = dispatch; - - // Act - actions.saveVehicleStyle(context, "SUV"); - - // Assert - expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - expect(dispatch).toHaveBeenNthCalledWith(2, storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); - - expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); - }); - - it("saveVehicleDamage, should wipe out damage if different", () => { - - // Arrange - const context = state; - - context.state = { - order: { - damage: { - glassToReplace: [{ glassName: 'Single', glassLocation: 'Windshield' }] - } - } - }; - - const commit = jest.fn(); - const dispatch = jest.fn(); - - context.commit = commit; - context.dispatch = dispatch; - - // Act - const payload = { isWindshieldRepair: false, selectedGlassToReplace: [{ glassName: 'Rear', glassLocation: 'quarter' }], selectedWindshieldChipCount: 0 }; - actions.saveVehicleDamage(context, payload); - - // Assert - expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); - expect(commit).toBeCalledWith(storeMutations.UPDATE_IS_REPAIR, payload.isWindshieldRepair); - expect(commit).toBeCalledWith(storeMutations.UPDATE_NUMBER_OF_CHIPS, payload.isWindshieldRepair ? parseInt(payload.selectedWindshieldChipCount) : null); - expect(commit).toBeCalledWith(storeMutations.UPDATE_GLASS_TO_REPLACE, payload.selectedGlassToReplace); - - }); - - describe("runExperimentsForTrigger", () => { - beforeEach(() => { - mutations.resetState(state); - globalMethods.callHttpClient = jest.fn().mockReturnValue({ - data: { - experiments: [ - { - mockProperty: "mockValue" - } - ] - } - }); - }) - - test("triggerEvent is SiteEntry => set triggeredSiteEntry to true in store", async () => { - // Arrange - const context = state; - context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value)); - context.getters = { - ...getters, - applicationUser: getters.applicationUser(context) - }; - - // Act - await actions.runExperimentsForTrigger(context, { - triggerEvent: experimentTriggers.SITE_ENTRY, - }); - - // Assert - expect(context.commit).toHaveBeenNthCalledWith(1, storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, true); - expect(context.getters.applicationUser.triggeredSiteEntry).toBe(true); - expect(globalMethods.callHttpClient).toHaveBeenCalled(); - expect(context.commit).toHaveBeenNthCalledWith(2, storeMutations.UPDATE_EXPERIMENTS, [ - { - mockProperty: "mockValue" - } - ]); - }); - - test("triggerEvent is not SiteEntry => triggeredSiteEntry is false in store", async () => { - // Arrange - const context = state; - context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value)); - context.getters = { - ...getters, - applicationUser: getters.applicationUser(context) - }; - expect(context.commit).toHaveBeenCalledTimes(0); - - // Act - await actions.runExperimentsForTrigger(context, { - triggerEvent: "NotSiteEntry", - }); - - // Assert - expect(context.commit).not.toHaveBeenCalledWith(storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, expect.any); - expect(context.getters.applicationUser.triggeredSiteEntry).toBe(false); - - expect(globalMethods.callHttpClient).toHaveBeenCalledTimes(1); - expect(context.commit).toHaveBeenNthCalledWith(1, storeMutations.UPDATE_EXPERIMENTS, [ - { - mockProperty: "mockValue" - } - ]); - }); - }) - - describe("savePartQuestionAnswers", () => { - let context; - beforeEach(() => { - jest.clearAllMocks(); - mutations.resetState(state); - context = state; - context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value)); - context.getters = { - ...getters, - damage: getters.damage(context) - }; - }) - - function testPartQuestionAnswerDependenciesHaveBeenReset(context, shouldPartQuestionAnswersBeReset) { - if (shouldPartQuestionAnswersBeReset) { - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.VEHICLE_PARTS, data: null }); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null }); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null }); - } - else { - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.VEHICLE_PARTS, data: null }); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null }); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null }); - } - - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PART_QUESTION_ANSWERS, expect.anything()); - } - - test("there are no previous answers => resets necessary fields", async () => { - // Arrange - const previousPartQuestionAnswers = []; - const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }, { result: "I'M A PART2!" }]; - - actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); - jest.clearAllMocks(); - - // Act - actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); - - // Assert - testPartQuestionAnswerDependenciesHaveBeenReset(context, true); - }) - - test("previous answers does not match current answers => resets necessary fields", () => { - // Arrange - const previousPartQuestionAnswers = [{ result: "I'M A PART2!" }, { result: "I'M A PART4!" }, { result: "I'M A PART3!" }]; - const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }, { result: "I'M A PART2!" }]; - - actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); - jest.clearAllMocks(); - - // Act - actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); - - // Assert - testPartQuestionAnswerDependenciesHaveBeenReset(context, true); - }) - - test("previous answers match current answers => does not reset fields", () => { - // Arrange - const previousPartQuestionAnswers = [{ result: "I'M A PART2!" }, { result: "I'M A PART!" }, { result: "I'M A PART3!" }]; - const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }, { result: "I'M A PART2!" }]; - - actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); - jest.clearAllMocks(); - - // Act - actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); - - // Assert - testPartQuestionAnswerDependenciesHaveBeenReset(context, false); - }) - test("previous answers have more questions/answers than current => resets fields", () => { - // Arrange - const previousPartQuestionAnswers = [{ result: "I'M A PART2!" }, { result: "I'M A PART!" }, { result: "I'M A PART3!" }]; - const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }]; - - actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); - jest.clearAllMocks(); - - // Act - actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); - - // Assert - testPartQuestionAnswerDependenciesHaveBeenReset(context, true); - }) - - test("current answers have more questions/answers than previous => resets fields", () => { - // Arrange - const previousPartQuestionAnswers = [{ result: "I'M A PART2!" }, { result: "I'M A PART3!" }]; - const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }, { result: "I'M A PART2!" }]; - - actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); - jest.clearAllMocks(); - - // Act - actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); - - // Assert - testPartQuestionAnswerDependenciesHaveBeenReset(context, true); - }) - }) - - describe("resetMoldingAndCapabilityQuestionAnswersIfNeeded", () => { - let context; - beforeEach(() => { - mutations.resetState(state); - context = state; - context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value)); - context.getters = { - ...getters, - pageData: getters.pageData(context) - }; - }) - - function testVehiclePartDependenciesHaveBeenReset(context, shouldAnswersBeReset) { - if (shouldAnswersBeReset) { - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null }); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null }); - } - else { - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null }); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null }); - } - } - - test("there are no saved parts from molding or capability question pages => resets necessary fields", () => { - // Arrange - const previouslySelectedParts = {}; - - const currentlySelectedParts = [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }] - } - ]; - - mutations.updatePageData(context, { - page: fmgPageValues.MOLDING_QUESTIONS, - data: previouslySelectedParts - }); - - mutations.updatePageData(context, { - page: fmgPageValues.CAPABILITY_QUESTIONS, - data: previouslySelectedParts - }); - - // Act - actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts); - - // Assert - testVehiclePartDependenciesHaveBeenReset(context, true); - }) - - describe("previously saved parts from molding-questions match selected parts => does not reset fields", () => { - test("single glass location", () => { + it("getVehicleYears action, should return years array", async () => { // Arrange - const previouslySelectedParts = { - partsOrQuestions: [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART1" }, { partNumber: "PART2" }, { partNumber: "PART3" }] - } - ] - }; + const context = state; - const currentlySelectedParts = [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }] - } - ]; + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: [2023, 2022, 2021] }); + }); - mutations.updatePageData(context, { - page: fmgPageValues.MOLDING_QUESTIONS, - data: previouslySelectedParts + // Assert + const response = await actions.getVehicleYears(context); + + expect(response.data).toEqual([2023, 2022, 2021]); + }); + + it("lookupVehicleByYmms action, should return car data", async () => { + // Arrange + const context = state; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { carId: "C00000001" } }); + }); + + // Assert + const response = await actions.lookupVehicleByYmms( + context, + "2019", + "Acura", + "ILX", + "4 DOOR SEDAN" + ); + + expect(response.data).toEqual({ carId: "C00000001" }); + }); + + it("lookupVehicleByVin action, should return car data", async () => { + // Arrange + const context = state; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { carId: "C00000001" } }); + }); + + // Assert + const response = await actions.lookupVehicleByVin(context, "12345678901234567"); + + expect(response.data).toEqual({ carId: "C00000001" }); + }); + + it("lookupVinByPlate action, should return car data", async () => { + // Arrange + const context = state; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { carId: "C00000001" } }); + }); + + // Assert + const response = await actions.lookupVinByPlate(context, "12345678901234567"); + + expect(response.data).toEqual({ carId: "C00000001" }); + }); + + it("getVehicleMakes action, should return makes list", async () => { + // Arrange + const context = state; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: ["Acura", "Honda"] }); + }); + + // Assert + const response = await actions.getVehicleMakes(context, "2019"); + + expect(response.data).toEqual(["Acura", "Honda"]); + }); + + it("getVehicleModels action, should return models list", async () => { + // Arrange + const context = state; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: ["ILX", "RDX"] }); + }); + + // Assert + const response = await actions.getVehicleModels(context, "2019", "Acura"); + + expect(response.data).toEqual(["ILX", "RDX"]); + }); + + it("getVehicleStyles action, should return models list", async () => { + // Arrange + const context = state; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { style: "4 DOOR SEDAN" } }); + }); + + // Assert + const response = await actions.getVehicleStyles(context, "2019", "Acura", "ILX"); + + expect(response.data).toEqual({ style: "4 DOOR SEDAN" }); + }); + + it("setVehicle action, should get vehicle data and set carId and vehicle category", async () => { + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { carId: "C00000000", category: "CAR" } }); + }); + + // Assert + const response = await actions.setVehicle(context, "C00000000"); + + expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, "C00000000"); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, "CAR"); + expect(response.data).toEqual({ carId: "C00000000", category: "CAR" }); + }); + + it("getDamageOptions action", async () => { + // Arrange + const context = state; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: ["Windshield", "DriversFrontDoor"] }); + }); + + const response = await actions.getDamageOptions(context, "C00000000"); + + // Assert + expect(response.data).toEqual(["Windshield", "DriversFrontDoor"]); + }); + + it("validateZip action", async () => { + // Arrange + const context = state; + + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: "43201" }); + }); + + const response = await actions.validateZip(context, "C00000000"); + + // Assert + expect(response.data).toEqual("43201"); + }); + + it("resetVehicleAndDependencies action", async () => { + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + + // Act + await actions.resetVehicleAndDependencies(context); + + expect(commit).toBeCalledWith(storeMutations.RESET_VEHICLE_STATE); + expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_STATE); + expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_STATE); + }); + + it("resetDamageAndDependencies action", async () => { + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + + // Act + await actions.resetDamageAndDependencies(context); + + expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_STATE); + expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE); + }); + + it("resetRegistrationAndDependencies action", async () => { + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + + // Act + await actions.resetRegistrationAndDependencies(context); + + expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_STATE); + expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE); + }); + + it("resetPartsAndDependencies action", async () => { + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + + // Act + await actions.resetPartsAndDependencies(context); + + expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE); + }); + + it("resetState action", async () => { + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + + // Act + await actions.resetState(context); + + expect(commit).toBeCalledWith(storeMutations.RESET_STATE); + }); + + it("getRouteInfo action, returns route info", async () => { + // Arrange + const context = state; + + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { Widget: "Data" } }); }); // Act - actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts); + const response = await actions.getRouteInfo(context, "vehicle-year"); - // Assert - testVehiclePartDependenciesHaveBeenReset(context, false); - }) + expect(response.data).toEqual({ Widget: "Data" }); + }); - test("multiple glass locations", () => { + it("getHomepageName action, returns homepage name", async () => { // Arrange - const previouslySelectedParts = { - partsOrQuestions: [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART1" }, { partNumber: "PART2" }, { partNumber: "PART3" }] + const context = state; + + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { Name: "vehicle-year" } }); + }); + + // Act + const response = await actions.getHomepageName(context); + + expect(response.data).toEqual({ Name: "vehicle-year" }); + }); + + it("getPageData action, returns page data", async () => { + // Arrange + const context = state; + + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { Results: [{ Widget: "Data" }] } }); + }); + + // Act + const response = await actions.getPageData(context, "vehicle-year"); + + expect(response.data).toEqual({ Results: [{ Widget: "Data" }] }); + }); + + it("getEvoxImage action, returns image url", async () => { + // Arrange + const context = state; + + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { imageUrl: "https://test.com" } }); + }); + + // Act + const response = await actions.getEvoxImage(context, { + relativeUrl: "https://relativeurl.com", + }); + + expect(response.data).toEqual({ imageUrl: "https://test.com" }); + }); + + it("saveSession action, returns order information", async () => { + // Arrange + const context = state; + + context.getters = { + vehicle: { + registration: {}, }, - { - glassLocation: "Driver", - glassName: "Front", - parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }] - } - ] - }; - - const currentlySelectedParts = [ - { - glassLocation: "Driver", - glassName: "Front", - parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }] - }, - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }] - } - ]; - - mutations.updatePageData(context, { - page: fmgPageValues.MOLDING_QUESTIONS, - data: previouslySelectedParts - }); - - // Act - actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts); - - // Assert - testVehiclePartDependenciesHaveBeenReset(context, false); - }) - }) - - describe("previously saved parts from capability-questions match selected parts and there are none from molding-questions => does not reset fields", () => { - test("Single glass location", () => { - // Arrange - const previouslySelectedParts = { - partsOrQuestions: [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART1" }, { partNumber: "PART2" }, { partNumber: "PART3" }] - } - ] - }; - - const currentlySelectedParts = [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }] - } - ]; - - mutations.updatePageData(context, { - page: fmgPageValues.CAPABILITY_QUESTIONS, - data: previouslySelectedParts - }); - - // Act - actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts); - - // Assert - testVehiclePartDependenciesHaveBeenReset(context, false); - }) - - test("multiple glass locations", () => { - // Arrange - const previouslySelectedParts = { - partsOrQuestions: [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART1" }, { partNumber: "PART2" }, { partNumber: "PART3" }] + damage: {}, + applicationUser: { + lastPageVisited: "test-page", + crmCustomerId: "xxx-xxx-xxx", + savedSessionId: "xxx-xxx-xxx", }, - { - glassLocation: "Driver", - glassName: "Front", - parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }] - } - ] }; - - const currentlySelectedParts = [ - { - glassLocation: "Driver", - glassName: "Front", - parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }] - }, - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }] - } - ]; - - mutations.updatePageData(context, { - page: fmgPageValues.CAPABILITY_QUESTIONS, - data: previouslySelectedParts - }); - - // Act - actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts); - - // Assert - testVehiclePartDependenciesHaveBeenReset(context, false); - }) - }) - - describe("previously saved parts from molding-questions do not match selected parts => resets necessary fields", () => { - test("single glass location", () => { - // Arrange - const previouslySelectedParts = { - partsOrQuestions: [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART1" }, { partNumber: "PART6" }, { partNumber: "PART3" }] - } - ] - }; - - const currentlySelectedParts = [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }] - } - ]; - - mutations.updatePageData(context, { - page: fmgPageValues.MOLDING_QUESTIONS, - data: previouslySelectedParts - }); - - // Act - actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts); - - // Assert - testVehiclePartDependenciesHaveBeenReset(context, true); - }) - - test("multiple glass locations", () => { - // Arrange - const previouslySelectedParts = { - partsOrQuestions: [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART1" }, { partNumber: "PART6" }, { partNumber: "PART3" }] + context.state = { + order: { + serviceLocation: {}, + customer: {}, + lineItems: {}, }, - { - glassLocation: "Driver", - glassName: "Front", - parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }] - } - ] }; - const currentlySelectedParts = [ - { - glassLocation: "Driver", - glassName: "Front", - parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }] - }, - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }] - } - ]; - - mutations.updatePageData(context, { - page: fmgPageValues.MOLDING_QUESTIONS, - data: previouslySelectedParts + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { referralNumber: 123 } }); }); // Act - actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts); + const response = await actions.saveSession(context); // Assert - testVehiclePartDependenciesHaveBeenReset(context, true); - }) - }) + expect(response.data).toEqual({ referralNumber: 123 }); + }); - describe("previously saved parts from capability-questions do not match selected parts => resets necessary fields", () => { - test("single glass location", () => { + it("loadSession action, returns order information, calls mutation", async () => { // Arrange - const previouslySelectedParts = { - partsOrQuestions: [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART1" }, { partNumber: "PART6" }, { partNumber: "PART3" }] - } - ] - }; + const context = state; - const currentlySelectedParts = [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }] - } - ]; - - mutations.updatePageData(context, { - page: fmgPageValues.MOLDING_QUESTIONS, - data: previouslySelectedParts + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { referralNumber: 123 } }); }); + const commit = jest.fn(); + + context.commit = commit; + // Act - actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts); + const response = await actions.loadSession(context, { + referralNumber: "123", + referralDate: new Date().toUTCString(), + referralCorrelationId: "xxx-xxx-xxx", + }); // Assert - testVehiclePartDependenciesHaveBeenReset(context, true); - }) + expect(response.data).toEqual({ referralNumber: 123 }); + expect(commit).toBeCalledWith(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, { + referralNumber: 123, + }); + }); - test("multiple glass locations", () => { + it("loadSession: state doesn't have EON => do not reset state", async () => { // Arrange - const previouslySelectedParts = { - partsOrQuestions: [ - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART1" }, { partNumber: "PART6" }, { partNumber: "PART3" }] + const context = state; + + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { eon: "123" } }); + }); + + context.commit = jest.fn(); + context.state = { + order: {}, + }; + + // Act + const response = await actions.loadSession(context, { + referralNumber: "123", + referralDate: new Date().toUTCString(), + referralCorrelationId: "xxx-xxx-xxx", + }); + + // Assert + expect(response.data.eon).toEqual("123"); + expect(context.commit).not.toBeCalledWith(storeMutations.RESET_STATE); + }); + + it("loadSession eon doesn't match eon in state => reset state", async () => { + // Arrange + const context = state; + + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({ data: { eon: "123" } }); + }); + + context.commit = jest.fn(); + context.state = { + order: { + eon: "456", }, - { - glassLocation: "Driver", - glassName: "Front", - parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }] - } - ] }; - const currentlySelectedParts = [ - { - glassLocation: "Driver", - glassName: "Front", - parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }] - }, - { - glassLocation: "Windshield", - glassName: "Single", - parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }] - } - ]; - - mutations.updatePageData(context, { - page: fmgPageValues.CAPABILITY_QUESTIONS, - data: previouslySelectedParts + // Act + const response = await actions.loadSession(context, { + referralNumber: "123", + referralDate: new Date().toUTCString(), + referralCorrelationId: "xxx-xxx-xxx", }); + // Assert + expect(response.data.eon).toEqual("123"); + expect(context.commit).toBeCalledWith(storeMutations.RESET_STATE); + }); + + it("updateStoreWithSaveSessionResponse, should call commit six times", () => { + // Arrange + const context = state; + const commit = jest.fn(); + + context.commit = commit; + // Act - actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts); + actions.updateStoreWithSaveSessionResponse(context, { + referralNumber: "123", + referralDate: new Date().toUTCString(), + referralCorrelationId: "xxx-xxx-xxx", + accountNumber: "167132", + savedSessionId: "xxx-xxx-xxx", + crmCustomerId: "xxx-xxx-xxx", + }); // Assert - testVehiclePartDependenciesHaveBeenReset(context, true); - }) - }) - }) + expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_NUMBER, "123"); + expect(commit).toBeCalledWith( + storeMutations.UPDATE_REFERRAL_DATE, + new Date().toUTCString() + ); + expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, "xxx-xxx-xxx"); + expect(commit).toBeCalledWith(storeMutations.UPDATE_PARENT_ACCT_NUMBER, "167132"); + expect(commit).toBeCalledWith(storeMutations.UPDATE_SAVED_SESSION_ID, "xxx-xxx-xxx"); + expect(commit).toBeCalledWith(storeMutations.UPDATE_CRM_CUSTOMER_ID, "xxx-xxx-xxx"); + }); - describe("saveMoldingQuestionAnswers", () => { - let context; - beforeEach(() => { - jest.clearAllMocks(); - mutations.resetState(state); - context = state; - context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value)); - context.getters = { - ...getters, - damage: getters.damage(context) - }; - }) + it("logPageView action, should return nothing", async () => { + // Arrange + const context = state; + var pageEvent = { + action: "", + event: "ENTRY", + }; - function testMoldingQuestionAnswerDependenciesHaveBeenReset(context, shouldAnswersBeReset) { - if (shouldAnswersBeReset) { - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null }); - } - else { - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null); - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null }); - } - } + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({}); + }); - test("there are no previous answers => resets necessary fields", () => { - // Arrange - const previousMoldingQuestionAnswers = []; - const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART2!" }]; + // Assert + const response = await actions.logPageView(context, { + userId: "userId", + sessionKey: "sessionKey", + pageName: "pageName", + sessionId: "sessionId", + pageEvent: pageEvent, + shouldUseSessionId: false, + }); + expect(response).toEqual({}); + }); - actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); - jest.clearAllMocks(); + it("logCustomEvent action, should return nothing", async () => { + // Arrange + const context = state; + var customEvent = { + category: "tstCat", + action: "click", + label: "damage", + value: "psych", + }; - // Act - actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({}); + }); - // Assert - testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true); - }) + // Assert + const response = await actions.logCustomEvent(context, { + userId: "userId", + sessionKey: "sessionKey", + pageName: "pageName", + sessionId: "sessionId", + customEvent: customEvent, + shouldUseSessionId: false, + }); + expect(response).toEqual({}); + }); - test("previous answers match current answers => does not reset fields", () => { - // Arrange - const previousMoldingQuestionAnswers = [{ partNum: "I'M A PART2!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART!" }];; - const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART2!" }]; + it("initializeSession action, should return nothing", async () => { + // Arrange + const context = state; - actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); - jest.clearAllMocks(); + // Act + globalMethods.callHttpClient.mockImplementation(() => { + return Promise.resolve({}); + }); - // Act - actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + // Assert + const response = await actions.initializeSession(context, { + userId: "userId", + sessionId: "", + userAgent: "", + referrer: "", + shouldUseSessionId: false, + }); + expect(response).toEqual({}); + }); - // Assert - testMoldingQuestionAnswerDependenciesHaveBeenReset(context, false); - }) + it("saveVin, should call mutation when CarId is different and selectedGlass is not available for vehicle", () => { + // Arrange + const context = state; - test("previous answers do not match current answers => resets necessary fields", () => { - // Arrange - const previousMoldingQuestionAnswers = [{ partNum: "I'M A PART2!" }, { partNum: "I'M A PART4!" }, { partNum: "I'M A PART!" }];; - const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART2!" }]; + context.state = { + order: { + vehicle: { + vin: "YYYYY", + }, + }, + }; - actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); - jest.clearAllMocks(); + const commit = jest.fn(); + const dispatch = jest.fn(); - // Act - actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + context.commit = commit; + context.dispatch = dispatch; - // Assert - testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true); - }) + // Act + actions.saveVin(context, { + isCarIdDifferent: true, + isSelectedGlassAvailableForVehicle: false, + vehicleInfo: { carId: "C010101", vin: "XXXXX" }, + }); - test("previous answers have more questions/answers than current => resets fields", () => { - // Arrange - const previousMoldingQuestionAnswers = [{ partNum: "I'M A PART2!" }, { partNum: "I'M A PART4!" }, { partNum: "I'M A PART!" }];; - const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }]; + // Assert + expect(dispatch).toBeCalledWith(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE, { + carId: "C010101", + vin: "XXXXX", + }); + }); - actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); - jest.clearAllMocks(); + it("saveEmail, should call mutation", () => { + // Arrange + const context = state; + const commit = jest.fn(); - // Act - actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + context.commit = commit; - // Assert - testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true); - }) + // Act + actions.saveEmail(context, "test@safelite.com"); - test("current answers have more questions/answers than previous => resets fields", () => { - // Arrange - const previousMoldingQuestionAnswers = [{ partNum: "I'M A PART2!" }, { partNum: "I'M A PART!" }];; - const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART2!" }]; + // Assert + expect(commit).toBeCalledWith( + storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, + "test@safelite.com" + ); + }); - actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); - jest.clearAllMocks(); + it("saveServiceLocation, should call mutation", () => { + // Arrange + const context = state; + const commit = jest.fn(); - // Act - actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + context.commit = commit; - // Assert - testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true); - }) - }) + // Act + actions.saveServiceLocation(context, { zipCode: "80020" }); - describe("saveCapabilityQuestionAnswers", () => { - let context; - beforeEach(() => { - jest.clearAllMocks(); - mutations.resetState(state); - context = state; - context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value)); - context.getters = { - ...getters, - damage: getters.damage(context) - }; - }) + // Assert + expect(commit).toBeCalledWith(storeMutations.UPDATE_SERVICE_LOCATION, { zipCode: "80020" }); + }); - function testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, shouldAnswersBeReset) { - if (shouldAnswersBeReset) { - expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); - } - else { - expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); - } - } + it("saveGlassParts, should call mutation", () => { + // Arrange + const context = state; + const commit = jest.fn(); - test("there are no previous answers => resets necessary fields", () => { - // Arrange - const previousCapabilityQuestionAnswers = []; - const currentCapabilityQuestionAnswers = [{ result: "DYNAMIC" }, { result: "STATIC" }, { result: "UNKNOWN" }]; + context.commit = commit; - actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); - jest.clearAllMocks(); + // Act + actions.saveGlassParts(context, { glassParts: {} }); - // Act - actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + // Assert + expect(commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, { glassParts: {} }); + }); - // Assert - testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true); - }) + it("clearVin, should call mutation", () => { + // Arrange + const context = state; + const commit = jest.fn(); - test("previous answers match current answers => does not reset fields", () => { - // Arrange - const previousCapabilityQuestionAnswers = [{ result: "STATIC" }, { result: "DYNAMIC" }, { result: "UNKNOWN" }]; - const currentCapabilityQuestionAnswers = [{ result: "DYNAMIC" }, { result: "STATIC" }, { result: "UNKNOWN" }]; + context.commit = commit; - actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); - jest.clearAllMocks(); + // Act + actions.clearVin(context); - // Act - actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + // Assert + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); + }); - // Assert - testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, false); - }) + it("saveVinLookup, should call mutation if vin is different", () => { + // Arrange + const context = state; + const commit = jest.fn(); + const dispatch = jest.fn(); - test("previous answers do not match current answers => resets necessary fields", () => { - // Arrange - const previousCapabilityQuestionAnswers = [{ result: "DYNAMIC" }, { result: "DYNAMIC" }, { result: "STATIC" }]; - const currentCapabilityQuestionAnswers = [{ result: "STATIC" }, { result: "STATIC" }, { result: "DYNAMIC" }]; + context.commit = commit; + context.dispatch = dispatch; - actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); - jest.clearAllMocks(); + // Act + const payload = { + isCarIdDifferent: true, + isSelectedGlassAvailableForVehicle: false, + vehicleInfo: { + carId: "C010101", + vin: "XXXXX", + }, + registrationInfo: { + zipCode: "80020", + }, + serviceLocationInfo: { + state: "CO", + }, + customerEmail: "test@safleite.com", + }; - // Act - actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + actions.saveVinLookup(context, payload); - // Assert - testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true); - }) + // Assert + expect(dispatch).toHaveBeenNthCalledWith( + 1, + storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES + ); + expect(dispatch).toHaveBeenNthCalledWith( + 2, + storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES + ); + expect(dispatch).toHaveBeenNthCalledWith( + 3, + storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES + ); - test("previous answers have more questions/answers than current => resets fields", () => { - // Arrange - const previousCapabilityQuestionAnswers = [{ result: "STATIC" }, { result: "DYNAMIC" }, { result: "UNKNOWN" }]; - const currentCapabilityQuestionAnswers = [{ result: "DYNAMIC" }]; + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE, payload.vehicleInfo); + expect(commit).toBeCalledWith(storeMutations.UPDATE_REGISTRATION, payload.registrationInfo); + }); - actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); - jest.clearAllMocks(); + it("saveRegistrationLicensePlateLookup, should call mutation if LP is different", () => { + // Arrange + const context = state; - // Act - actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + context.state = { + order: { + vehicle: { + registration: { + licensePlate: "ABC123", + }, + }, + }, + }; - // Assert - testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true); - }) + const commit = jest.fn(); + const dispatch = jest.fn(); - test("current answers have more questions/answers than previous => resets fields", () => { - // Arrange - const previousCapabilityQuestionAnswers = [{ result: "STATIC" }, { result: "UNKNOWN" }]; - const currentCapabilityQuestionAnswers = [{ result: "DYNAMIC" }, { result: "STATIC" }, { result: "UNKNOWN" }]; - actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); - jest.clearAllMocks(); + context.commit = commit; + context.dispatch = dispatch; - // Act - actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + // Act + const payload = { + isCarIdDifferent: true, + isSelectedGlassAvailableForVehicle: false, + vehicleInfo: { carId: "C010101", vin: "XXXXX" }, + registrationInfo: { zipCode: "80020", licensePlate: "ALQX35" }, + serviceLocationInfo: { state: "CO" }, + customerEmail: "test@safelite.com", + }; - // Assert - testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true); - }) - }) + actions.saveRegistrationLicensePlateLookup(context, payload); + + // Assert + expect(dispatch).toHaveBeenNthCalledWith( + 1, + storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES + ); + expect(dispatch).toHaveBeenNthCalledWith( + 2, + storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES + ); + + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE, payload.vehicleInfo); + expect(commit).toBeCalledWith(storeMutations.UPDATE_REGISTRATION, payload.registrationInfo); + }); + + it("saveRegistrationAddressLookup, should call mutation when address is different", () => { + // Arrange + const context = state; + + context.state = { + order: { + vehicle: { + registration: { + address: "123 Main St", + }, + }, + }, + }; + + const commit = jest.fn(); + const dispatch = jest.fn(); + + context.commit = commit; + context.dispatch = dispatch; + + // Act + const payload = { + isCarIdDifferent: true, + isSelectedGlassAvailableForVehicle: false, + vehicleInfo: { carId: "C010101", vin: "XXXXX" }, + registrationInfo: { zipCode: "80020", address: "123 Marys Ave" }, + serviceLocationInfo: { state: "CO" }, + customerEmail: "test@safelite.com", + }; + + actions.saveRegistrationAddressLookup(context, payload); + + // Assert + expect(dispatch).toHaveBeenNthCalledWith( + 1, + storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES + ); + expect(dispatch).toHaveBeenNthCalledWith( + 2, + storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES + ); + + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE, payload.vehicleInfo); + expect(commit).toBeCalledWith(storeMutations.UPDATE_REGISTRATION, payload.registrationInfo); + }); + + it("saveVehicleYear, should wipe out vehicle info if year changes", () => { + // Arrange + const context = state; + + context.state = { + order: { + vehicle: { + year: "2015", + }, + }, + }; + + const commit = jest.fn(); + const dispatch = jest.fn(); + + context.commit = commit; + context.dispatch = dispatch; + + // Act + actions.saveVehicleYear(context, "2016"); + + // Assert + expect(dispatch).toHaveBeenNthCalledWith( + 1, + storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES + ); + expect(dispatch).toHaveBeenNthCalledWith( + 2, + storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES + ); + + expect(commit).toBeCalledWith(storeMutations.UPDATE_MAKE, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_MODEL, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_STYLE, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + }); + + it("saveVehicleMake, should wipe out vehicle info if make changes", () => { + // Arrange + const context = state; + + context.state = { + order: { + vehicle: { + make: "Honda", + }, + }, + }; + + const commit = jest.fn(); + const dispatch = jest.fn(); + + context.commit = commit; + context.dispatch = dispatch; + + // Act + actions.saveVehicleMake(context, "Toyota"); + + // Assert + expect(dispatch).toHaveBeenNthCalledWith( + 1, + storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES + ); + expect(dispatch).toHaveBeenNthCalledWith( + 2, + storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES + ); + + expect(commit).toBeCalledWith(storeMutations.UPDATE_MODEL, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_STYLE, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + }); + + it("saveVehicle model, should wipe out vehicle info if model changes", () => { + // Arrange + const context = state; + + context.state = { + order: { + vehicle: { + model: "Civic", + }, + }, + }; + + const commit = jest.fn(); + const dispatch = jest.fn(); + + context.commit = commit; + context.dispatch = dispatch; + + // Act + actions.saveVehicleModel(context, "Accord"); + + // Assert + expect(dispatch).toHaveBeenNthCalledWith( + 1, + storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES + ); + expect(dispatch).toHaveBeenNthCalledWith( + 2, + storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES + ); + + expect(commit).toBeCalledWith(storeMutations.UPDATE_STYLE, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + }); + + it("saveVehicleStyle, should wipe out vehicle info if style changes", () => { + // Arrange + const context = state; + + context.state = { + order: { + vehicle: { + style: "Sedan", + }, + }, + }; + + const commit = jest.fn(); + const dispatch = jest.fn(); + + context.commit = commit; + context.dispatch = dispatch; + + // Act + actions.saveVehicleStyle(context, "SUV"); + + // Assert + expect(dispatch).toHaveBeenNthCalledWith( + 1, + storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES + ); + expect(dispatch).toHaveBeenNthCalledWith( + 2, + storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES + ); + + expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_VIN, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null); + expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null); + }); + + it("saveVehicleDamage, should wipe out damage if different", () => { + // Arrange + const context = state; + + context.state = { + order: { + damage: { + glassToReplace: [{ glassName: "Single", glassLocation: "Windshield" }], + }, + }, + }; + + const commit = jest.fn(); + const dispatch = jest.fn(); + + context.commit = commit; + context.dispatch = dispatch; + + // Act + const payload = { + isWindshieldRepair: false, + selectedGlassToReplace: [{ glassName: "Rear", glassLocation: "quarter" }], + selectedWindshieldChipCount: 0, + }; + actions.saveVehicleDamage(context, payload); + + // Assert + expect(dispatch).toHaveBeenNthCalledWith( + 1, + storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES + ); + expect(commit).toBeCalledWith(storeMutations.UPDATE_IS_REPAIR, payload.isWindshieldRepair); + expect(commit).toBeCalledWith( + storeMutations.UPDATE_NUMBER_OF_CHIPS, + payload.isWindshieldRepair ? parseInt(payload.selectedWindshieldChipCount) : null + ); + expect(commit).toBeCalledWith( + storeMutations.UPDATE_GLASS_TO_REPLACE, + payload.selectedGlassToReplace + ); + }); + + describe("runExperimentsForTrigger", () => { + beforeEach(() => { + mutations.resetState(state); + globalMethods.callHttpClient = jest.fn().mockReturnValue({ + data: { + experiments: [ + { + mockProperty: "mockValue", + }, + ], + }, + }); + }); + + test("triggerEvent is SiteEntry => set triggeredSiteEntry to true in store", async () => { + // Arrange + const context = state; + context.commit = jest + .fn() + .mockImplementation((storeMutation, value) => + mutations[storeMutation](context, value) + ); + context.getters = { + ...getters, + applicationUser: getters.applicationUser(context), + }; + + // Act + await actions.runExperimentsForTrigger(context, { + triggerEvent: experimentTriggers.SITE_ENTRY, + }); + + // Assert + expect(context.commit).toHaveBeenNthCalledWith( + 1, + storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, + true + ); + expect(context.getters.applicationUser.triggeredSiteEntry).toBe(true); + expect(globalMethods.callHttpClient).toHaveBeenCalled(); + expect(context.commit).toHaveBeenNthCalledWith(2, storeMutations.UPDATE_EXPERIMENTS, [ + { + mockProperty: "mockValue", + }, + ]); + }); + + test("triggerEvent is not SiteEntry => triggeredSiteEntry is false in store", async () => { + // Arrange + const context = state; + context.commit = jest + .fn() + .mockImplementation((storeMutation, value) => + mutations[storeMutation](context, value) + ); + context.getters = { + ...getters, + applicationUser: getters.applicationUser(context), + }; + expect(context.commit).toHaveBeenCalledTimes(0); + + // Act + await actions.runExperimentsForTrigger(context, { + triggerEvent: "NotSiteEntry", + }); + + // Assert + expect(context.commit).not.toHaveBeenCalledWith( + storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, + expect.any + ); + expect(context.getters.applicationUser.triggeredSiteEntry).toBe(false); + + expect(globalMethods.callHttpClient).toHaveBeenCalledTimes(1); + expect(context.commit).toHaveBeenNthCalledWith(1, storeMutations.UPDATE_EXPERIMENTS, [ + { + mockProperty: "mockValue", + }, + ]); + }); + }); + + describe("savePartQuestionAnswers", () => { + let context; + beforeEach(() => { + jest.clearAllMocks(); + mutations.resetState(state); + context = state; + context.commit = jest + .fn() + .mockImplementation((storeMutation, value) => + mutations[storeMutation](context, value) + ); + context.getters = { + ...getters, + damage: getters.damage(context), + }; + }); + + function testPartQuestionAnswerDependenciesHaveBeenReset( + context, + shouldPartQuestionAnswersBeReset + ) { + if (shouldPartQuestionAnswersBeReset) { + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); + expect(context.commit).toBeCalledWith( + storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, + null + ); + expect(context.commit).toBeCalledWith( + storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, + null + ); + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.VEHICLE_PARTS, + data: null, + }); + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: null, + }); + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: null, + }); + } else { + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); + expect(context.commit).not.toBeCalledWith( + storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, + null + ); + expect(context.commit).not.toBeCalledWith( + storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, + null + ); + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.VEHICLE_PARTS, + data: null, + }); + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: null, + }); + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: null, + }); + } + + expect(context.commit).toBeCalledWith( + storeMutations.UPDATE_PART_QUESTION_ANSWERS, + expect.anything() + ); + } + + test("there are no previous answers => resets necessary fields", async () => { + // Arrange + const previousPartQuestionAnswers = []; + const currentPartQuestionAnswers = [ + { result: "I'M A PART!" }, + { result: "I'M A PART3!" }, + { result: "I'M A PART2!" }, + ]; + + actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); + + // Assert + testPartQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + + test("previous answers does not match current answers => resets necessary fields", () => { + // Arrange + const previousPartQuestionAnswers = [ + { result: "I'M A PART2!" }, + { result: "I'M A PART4!" }, + { result: "I'M A PART3!" }, + ]; + const currentPartQuestionAnswers = [ + { result: "I'M A PART!" }, + { result: "I'M A PART3!" }, + { result: "I'M A PART2!" }, + ]; + + actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); + + // Assert + testPartQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + + test("previous answers match current answers => does not reset fields", () => { + // Arrange + const previousPartQuestionAnswers = [ + { result: "I'M A PART2!" }, + { result: "I'M A PART!" }, + { result: "I'M A PART3!" }, + ]; + const currentPartQuestionAnswers = [ + { result: "I'M A PART!" }, + { result: "I'M A PART3!" }, + { result: "I'M A PART2!" }, + ]; + + actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); + + // Assert + testPartQuestionAnswerDependenciesHaveBeenReset(context, false); + }); + test("previous answers have more questions/answers than current => resets fields", () => { + // Arrange + const previousPartQuestionAnswers = [ + { result: "I'M A PART2!" }, + { result: "I'M A PART!" }, + { result: "I'M A PART3!" }, + ]; + const currentPartQuestionAnswers = [ + { result: "I'M A PART!" }, + { result: "I'M A PART3!" }, + ]; + + actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); + + // Assert + testPartQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + + test("current answers have more questions/answers than previous => resets fields", () => { + // Arrange + const previousPartQuestionAnswers = [ + { result: "I'M A PART2!" }, + { result: "I'M A PART3!" }, + ]; + const currentPartQuestionAnswers = [ + { result: "I'M A PART!" }, + { result: "I'M A PART3!" }, + { result: "I'M A PART2!" }, + ]; + + actions.savePartQuestionAnswers(context, previousPartQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.savePartQuestionAnswers(context, currentPartQuestionAnswers); + + // Assert + testPartQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + }); + + describe("resetMoldingAndCapabilityQuestionAnswersIfNeeded", () => { + let context; + beforeEach(() => { + mutations.resetState(state); + context = state; + context.commit = jest + .fn() + .mockImplementation((storeMutation, value) => + mutations[storeMutation](context, value) + ); + context.getters = { + ...getters, + pageData: getters.pageData(context), + }; + }); + + function testVehiclePartDependenciesHaveBeenReset(context, shouldAnswersBeReset) { + if (shouldAnswersBeReset) { + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); + expect(context.commit).toBeCalledWith( + storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, + null + ); + expect(context.commit).toBeCalledWith( + storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, + null + ); + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: null, + }); + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: null, + }); + } else { + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); + expect(context.commit).not.toBeCalledWith( + storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, + null + ); + expect(context.commit).not.toBeCalledWith( + storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, + null + ); + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: null, + }); + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: null, + }); + } + } + + test("there are no saved parts from molding or capability question pages => resets necessary fields", () => { + // Arrange + const previouslySelectedParts = {}; + + const currentlySelectedParts = [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART3" }, + { partNumber: "PART1" }, + { partNumber: "PART2" }, + ], + }, + ]; + + mutations.updatePageData(context, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: previouslySelectedParts, + }); + + mutations.updatePageData(context, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: previouslySelectedParts, + }); + + // Act + actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded( + context, + currentlySelectedParts + ); + + // Assert + testVehiclePartDependenciesHaveBeenReset(context, true); + }); + + describe("previously saved parts from molding-questions match selected parts => does not reset fields", () => { + test("single glass location", () => { + // Arrange + const previouslySelectedParts = { + partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART1" }, + { partNumber: "PART2" }, + { partNumber: "PART3" }, + ], + }, + ], + }; + + const currentlySelectedParts = [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART3" }, + { partNumber: "PART1" }, + { partNumber: "PART2" }, + ], + }, + ]; + + mutations.updatePageData(context, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: previouslySelectedParts, + }); + + // Act + actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded( + context, + currentlySelectedParts + ); + + // Assert + testVehiclePartDependenciesHaveBeenReset(context, false); + }); + + test("multiple glass locations", () => { + // Arrange + const previouslySelectedParts = { + partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART1" }, + { partNumber: "PART2" }, + { partNumber: "PART3" }, + ], + }, + { + glassLocation: "Driver", + glassName: "Front", + parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }], + }, + ], + }; + + const currentlySelectedParts = [ + { + glassLocation: "Driver", + glassName: "Front", + parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }], + }, + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART3" }, + { partNumber: "PART1" }, + { partNumber: "PART2" }, + ], + }, + ]; + + mutations.updatePageData(context, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: previouslySelectedParts, + }); + + // Act + actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded( + context, + currentlySelectedParts + ); + + // Assert + testVehiclePartDependenciesHaveBeenReset(context, false); + }); + }); + + describe("previously saved parts from capability-questions match selected parts and there are none from molding-questions => does not reset fields", () => { + test("Single glass location", () => { + // Arrange + const previouslySelectedParts = { + partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART1" }, + { partNumber: "PART2" }, + { partNumber: "PART3" }, + ], + }, + ], + }; + + const currentlySelectedParts = [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART3" }, + { partNumber: "PART1" }, + { partNumber: "PART2" }, + ], + }, + ]; + + mutations.updatePageData(context, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: previouslySelectedParts, + }); + + // Act + actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded( + context, + currentlySelectedParts + ); + + // Assert + testVehiclePartDependenciesHaveBeenReset(context, false); + }); + + test("multiple glass locations", () => { + // Arrange + const previouslySelectedParts = { + partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART1" }, + { partNumber: "PART2" }, + { partNumber: "PART3" }, + ], + }, + { + glassLocation: "Driver", + glassName: "Front", + parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }], + }, + ], + }; + + const currentlySelectedParts = [ + { + glassLocation: "Driver", + glassName: "Front", + parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }], + }, + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART3" }, + { partNumber: "PART1" }, + { partNumber: "PART2" }, + ], + }, + ]; + + mutations.updatePageData(context, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: previouslySelectedParts, + }); + + // Act + actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded( + context, + currentlySelectedParts + ); + + // Assert + testVehiclePartDependenciesHaveBeenReset(context, false); + }); + }); + + describe("previously saved parts from molding-questions do not match selected parts => resets necessary fields", () => { + test("single glass location", () => { + // Arrange + const previouslySelectedParts = { + partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART1" }, + { partNumber: "PART6" }, + { partNumber: "PART3" }, + ], + }, + ], + }; + + const currentlySelectedParts = [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART3" }, + { partNumber: "PART1" }, + { partNumber: "PART2" }, + ], + }, + ]; + + mutations.updatePageData(context, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: previouslySelectedParts, + }); + + // Act + actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded( + context, + currentlySelectedParts + ); + + // Assert + testVehiclePartDependenciesHaveBeenReset(context, true); + }); + + test("multiple glass locations", () => { + // Arrange + const previouslySelectedParts = { + partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART1" }, + { partNumber: "PART6" }, + { partNumber: "PART3" }, + ], + }, + { + glassLocation: "Driver", + glassName: "Front", + parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }], + }, + ], + }; + + const currentlySelectedParts = [ + { + glassLocation: "Driver", + glassName: "Front", + parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }], + }, + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART3" }, + { partNumber: "PART1" }, + { partNumber: "PART2" }, + ], + }, + ]; + + mutations.updatePageData(context, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: previouslySelectedParts, + }); + + // Act + actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded( + context, + currentlySelectedParts + ); + + // Assert + testVehiclePartDependenciesHaveBeenReset(context, true); + }); + }); + + describe("previously saved parts from capability-questions do not match selected parts => resets necessary fields", () => { + test("single glass location", () => { + // Arrange + const previouslySelectedParts = { + partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART1" }, + { partNumber: "PART6" }, + { partNumber: "PART3" }, + ], + }, + ], + }; + + const currentlySelectedParts = [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART3" }, + { partNumber: "PART1" }, + { partNumber: "PART2" }, + ], + }, + ]; + + mutations.updatePageData(context, { + page: fmgPageValues.MOLDING_QUESTIONS, + data: previouslySelectedParts, + }); + + // Act + actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded( + context, + currentlySelectedParts + ); + + // Assert + testVehiclePartDependenciesHaveBeenReset(context, true); + }); + + test("multiple glass locations", () => { + // Arrange + const previouslySelectedParts = { + partsOrQuestions: [ + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART1" }, + { partNumber: "PART6" }, + { partNumber: "PART3" }, + ], + }, + { + glassLocation: "Driver", + glassName: "Front", + parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }], + }, + ], + }; + + const currentlySelectedParts = [ + { + glassLocation: "Driver", + glassName: "Front", + parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }], + }, + { + glassLocation: "Windshield", + glassName: "Single", + parts: [ + { partNumber: "PART3" }, + { partNumber: "PART1" }, + { partNumber: "PART2" }, + ], + }, + ]; + + mutations.updatePageData(context, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: previouslySelectedParts, + }); + + // Act + actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded( + context, + currentlySelectedParts + ); + + // Assert + testVehiclePartDependenciesHaveBeenReset(context, true); + }); + }); + }); + + describe("saveMoldingQuestionAnswers", () => { + let context; + beforeEach(() => { + jest.clearAllMocks(); + mutations.resetState(state); + context = state; + context.commit = jest + .fn() + .mockImplementation((storeMutation, value) => + mutations[storeMutation](context, value) + ); + context.getters = { + ...getters, + damage: getters.damage(context), + }; + }); + + function testMoldingQuestionAnswerDependenciesHaveBeenReset(context, shouldAnswersBeReset) { + if (shouldAnswersBeReset) { + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); + expect(context.commit).toBeCalledWith( + storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, + null + ); + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: null, + }); + } else { + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); + expect(context.commit).not.toBeCalledWith( + storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, + null + ); + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { + page: fmgPageValues.CAPABILITY_QUESTIONS, + data: null, + }); + } + } + + test("there are no previous answers => resets necessary fields", () => { + // Arrange + const previousMoldingQuestionAnswers = []; + const currentMoldingQuestionAnswers = [ + { partNum: "I'M A PART!" }, + { partNum: "I'M A PART3!" }, + { partNum: "I'M A PART2!" }, + ]; + + actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + + // Assert + testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + + test("previous answers match current answers => does not reset fields", () => { + // Arrange + const previousMoldingQuestionAnswers = [ + { partNum: "I'M A PART2!" }, + { partNum: "I'M A PART3!" }, + { partNum: "I'M A PART!" }, + ]; + const currentMoldingQuestionAnswers = [ + { partNum: "I'M A PART!" }, + { partNum: "I'M A PART3!" }, + { partNum: "I'M A PART2!" }, + ]; + + actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + + // Assert + testMoldingQuestionAnswerDependenciesHaveBeenReset(context, false); + }); + + test("previous answers do not match current answers => resets necessary fields", () => { + // Arrange + const previousMoldingQuestionAnswers = [ + { partNum: "I'M A PART2!" }, + { partNum: "I'M A PART4!" }, + { partNum: "I'M A PART!" }, + ]; + const currentMoldingQuestionAnswers = [ + { partNum: "I'M A PART!" }, + { partNum: "I'M A PART3!" }, + { partNum: "I'M A PART2!" }, + ]; + + actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + + // Assert + testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + + test("previous answers have more questions/answers than current => resets fields", () => { + // Arrange + const previousMoldingQuestionAnswers = [ + { partNum: "I'M A PART2!" }, + { partNum: "I'M A PART4!" }, + { partNum: "I'M A PART!" }, + ]; + const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }]; + + actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + + // Assert + testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + + test("current answers have more questions/answers than previous => resets fields", () => { + // Arrange + const previousMoldingQuestionAnswers = [ + { partNum: "I'M A PART2!" }, + { partNum: "I'M A PART!" }, + ]; + const currentMoldingQuestionAnswers = [ + { partNum: "I'M A PART!" }, + { partNum: "I'M A PART3!" }, + { partNum: "I'M A PART2!" }, + ]; + + actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers); + + // Assert + testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + }); + + describe("saveCapabilityQuestionAnswers", () => { + let context; + beforeEach(() => { + jest.clearAllMocks(); + mutations.resetState(state); + context = state; + context.commit = jest + .fn() + .mockImplementation((storeMutation, value) => + mutations[storeMutation](context, value) + ); + context.getters = { + ...getters, + damage: getters.damage(context), + }; + }); + + function testCapabilityQuestionAnswerDependenciesHaveBeenReset( + context, + shouldAnswersBeReset + ) { + if (shouldAnswersBeReset) { + expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); + } else { + expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null); + } + } + + test("there are no previous answers => resets necessary fields", () => { + // Arrange + const previousCapabilityQuestionAnswers = []; + const currentCapabilityQuestionAnswers = [ + { result: "DYNAMIC" }, + { result: "STATIC" }, + { result: "UNKNOWN" }, + ]; + + actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + + // Assert + testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + + test("previous answers match current answers => does not reset fields", () => { + // Arrange + const previousCapabilityQuestionAnswers = [ + { result: "STATIC" }, + { result: "DYNAMIC" }, + { result: "UNKNOWN" }, + ]; + const currentCapabilityQuestionAnswers = [ + { result: "DYNAMIC" }, + { result: "STATIC" }, + { result: "UNKNOWN" }, + ]; + + actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + + // Assert + testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, false); + }); + + test("previous answers do not match current answers => resets necessary fields", () => { + // Arrange + const previousCapabilityQuestionAnswers = [ + { result: "DYNAMIC" }, + { result: "DYNAMIC" }, + { result: "STATIC" }, + ]; + const currentCapabilityQuestionAnswers = [ + { result: "STATIC" }, + { result: "STATIC" }, + { result: "DYNAMIC" }, + ]; + + actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + + // Assert + testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + + test("previous answers have more questions/answers than current => resets fields", () => { + // Arrange + const previousCapabilityQuestionAnswers = [ + { result: "STATIC" }, + { result: "DYNAMIC" }, + { result: "UNKNOWN" }, + ]; + const currentCapabilityQuestionAnswers = [{ result: "DYNAMIC" }]; + + actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + + // Assert + testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + + test("current answers have more questions/answers than previous => resets fields", () => { + // Arrange + const previousCapabilityQuestionAnswers = [{ result: "STATIC" }, { result: "UNKNOWN" }]; + const currentCapabilityQuestionAnswers = [ + { result: "DYNAMIC" }, + { result: "STATIC" }, + { result: "UNKNOWN" }, + ]; + actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers); + jest.clearAllMocks(); + + // Act + actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers); + + // Assert + testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true); + }); + }); }); describe("Getters", () => { - it("Vehicle getter, should return vehicle data", () => { - // Arrange - const storeState = state; + it("Vehicle getter, should return vehicle data", () => { + // Arrange + const storeState = state; - // Act - mutations.updateYear(storeState, "2019"); - mutations.updateMake(storeState, "Acura"); - mutations.updateModel(storeState, "ILX"); + // Act + mutations.updateYear(storeState, "2019"); + mutations.updateMake(storeState, "Acura"); + mutations.updateModel(storeState, "ILX"); - // Assert - expect(getters.vehicle(storeState).year).toEqual("2019"); - expect(getters.vehicle(storeState).make).toEqual("Acura"); - expect(getters.vehicle(storeState).model).toEqual("ILX"); - - }); - - it("Get event bus item by event category and eventSubCategory", () => { - // Arrange - const storeState = state; - const event = { category: "CategoryOne", subCategory: "SubCategoryOne", eventValue: "EventValueOne" }; - - // Act - mutations.addEventToBus(storeState, event); - - // Assert - //expect(storeState.applicationUser.eventBus).toEqual([event]); - expect(getters.eventBusItem(storeState)(event.category, event.subCategory)).toEqual(event.eventValue); - - }); - - it("Get event bus", () => { - // Arrange - const storeState = state; - storeState.applicationUser.eventBus = []; - - const event = { category: "CategoryOne", subCategory: "SubCategoryOne", eventValue: "EventValueOne" }; - - // Act - mutations.addEventToBus(storeState, event); - - // Assert - expect(getters.eventBus(storeState)).toEqual([event]); - - }); - - 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.updateGlassParts(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({}); - - }); - - it("Payment getter, should return payment data", () => { - // Arrange - const storeState = state; - - //Act - mutations.updateInsuranceVerifiedStatus(storeState, true); - - //Assert - expect(getters.payment(storeState).insuranceCoverage.isVerified).toEqual(true); - }); - - describe("experimentOrder", () => { - test("glassToReplace, glassParts, and otherParts are null > return correct experimentOrder values", () => { - // Arrange - const storeState = state; - const mockStateValues = { - funnelVehicleYear: 1000, - funnelVehicleMake: "CarMake", - funnelVehicleModel: "CarModel", - funnelVehicleStyle: "SuperCoolStyle", - funnelIsRepair: true, - funnelNumberOfChips: 9999999, - funnelCarId: "Gibberish", - funnelServiceCity: "Columbus", - funnelServiceState: "OH-IO", - funnelServiceZipCode: 43215, - funnelParentAccountNumber: "999999", - funnelIsCoverageVerified: true, - funnelGlassParts: null, - funnelOtherParts: null, - funnelGlassToReplace: null - } - - //Act - mutations.updateYear(storeState, mockStateValues.funnelVehicleYear); - mutations.updateMake(storeState, mockStateValues.funnelVehicleMake); - mutations.updateModel(storeState, mockStateValues.funnelVehicleModel); - mutations.updateStyle(storeState, mockStateValues.funnelVehicleStyle); - mutations.updateIsRepair(storeState, mockStateValues.funnelIsRepair); - mutations.updateNumberOfChips(storeState, mockStateValues.funnelNumberOfChips); - mutations.updateCarId(storeState, mockStateValues.funnelCarId); - mutations.updateServiceLocation(storeState, { - city: mockStateValues.funnelServiceCity, - state: mockStateValues.funnelServiceState, - zipCode: mockStateValues.funnelServiceZipCode - }); - mutations.updateParentAcctNumber(storeState, mockStateValues.funnelParentAccountNumber); - mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.funnelIsCoverageVerified); - mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts); - mutations.updateOtherParts(storeState, mockStateValues.funnelOtherParts); - mutations.updateGlassToReplace(storeState, mockStateValues.funnelGlassToReplace); - - //Assert - expect(getters.experimentOrder(storeState)).toEqual({ - funnelVehicleYear: mockStateValues.funnelVehicleYear, - funnelVehicleMake: mockStateValues.funnelVehicleMake, - funnelVehicleModel: mockStateValues.funnelVehicleModel, - funnelVehicleStyle: mockStateValues.funnelVehicleStyle, - funnelIsRepair: mockStateValues.funnelIsRepair, - funnelNumberOfChips: mockStateValues.funnelNumberOfChips, - funnelCarId: mockStateValues.funnelCarId, - funnelServiceCity: mockStateValues.funnelServiceCity, - funnelServiceState: mockStateValues.funnelServiceState, - funnelServiceZipCode: mockStateValues.funnelServiceZipCode, - funnelParentAccountNumber: mockStateValues.funnelParentAccountNumber, - funnelIsCoverageVerified: mockStateValues.funnelIsCoverageVerified, - funnelOrderPartNumbers: [], - funnelOrderPartTypes: [], - funnelHasRecalibrationPart: false, - funnelSelectedMultiGlass: false, - funnelSelectedWindshieldGlass: false, - funnelSelectedBackGlass: false, - funnelSelectedDriverSideGlass: false, - funnelSelectedPassengerSideGlass: false - }); + // Assert + expect(getters.vehicle(storeState).year).toEqual("2019"); + expect(getters.vehicle(storeState).make).toEqual("Acura"); + expect(getters.vehicle(storeState).model).toEqual("ILX"); }); - test("glassToReplace, glassParts, and otherParts are empty > return correct experimentOrder values", () => { - // Arrange - const storeState = state; - const mockStateValues = { - funnelVehicleYear: 1000, - funnelVehicleMake: "CarMake", - funnelVehicleModel: "CarModel", - funnelVehicleStyle: "SuperCoolStyle", - funnelIsRepair: true, - funnelNumberOfChips: 9999999, - funnelCarId: "Gibberish", - funnelServiceCity: "Columbus", - funnelServiceState: "OH-IO", - funnelServiceZipCode: 43215, - funnelParentAccountNumber: "999999", - funnelIsCoverageVerified: true, - funnelGlassParts: [], - funnelOtherParts: [], - funnelGlassToReplace: [] - } + it("Get event bus item by event category and eventSubCategory", () => { + // Arrange + const storeState = state; + const event = { + category: "CategoryOne", + subCategory: "SubCategoryOne", + eventValue: "EventValueOne", + }; - //Act - mutations.updateYear(storeState, mockStateValues.funnelVehicleYear); - mutations.updateMake(storeState, mockStateValues.funnelVehicleMake); - mutations.updateModel(storeState, mockStateValues.funnelVehicleModel); - mutations.updateStyle(storeState, mockStateValues.funnelVehicleStyle); - mutations.updateIsRepair(storeState, mockStateValues.funnelIsRepair); - mutations.updateNumberOfChips(storeState, mockStateValues.funnelNumberOfChips); - mutations.updateCarId(storeState, mockStateValues.funnelCarId); - mutations.updateServiceLocation(storeState, { - city: mockStateValues.funnelServiceCity, - state: mockStateValues.funnelServiceState, - zipCode: mockStateValues.funnelServiceZipCode - }); - mutations.updateParentAcctNumber(storeState, mockStateValues.funnelParentAccountNumber); - mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.funnelIsCoverageVerified); - mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts); - mutations.updateOtherParts(storeState, mockStateValues.funnelOtherParts); - mutations.updateGlassToReplace(storeState, mockStateValues.funnelGlassToReplace); + // Act + mutations.addEventToBus(storeState, event); - //Assert - expect(getters.experimentOrder(storeState)).toEqual({ - funnelVehicleYear: mockStateValues.funnelVehicleYear, - funnelVehicleMake: mockStateValues.funnelVehicleMake, - funnelVehicleModel: mockStateValues.funnelVehicleModel, - funnelVehicleStyle: mockStateValues.funnelVehicleStyle, - funnelIsRepair: mockStateValues.funnelIsRepair, - funnelNumberOfChips: mockStateValues.funnelNumberOfChips, - funnelCarId: mockStateValues.funnelCarId, - funnelServiceCity: mockStateValues.funnelServiceCity, - funnelServiceState: mockStateValues.funnelServiceState, - funnelServiceZipCode: mockStateValues.funnelServiceZipCode, - funnelParentAccountNumber: mockStateValues.funnelParentAccountNumber, - funnelIsCoverageVerified: mockStateValues.funnelIsCoverageVerified, - funnelOrderPartNumbers: [], - funnelOrderPartTypes: [], - funnelHasRecalibrationPart: false, - funnelSelectedMultiGlass: false, - funnelSelectedWindshieldGlass: false, - funnelSelectedBackGlass: false, - funnelSelectedDriverSideGlass: false, - funnelSelectedPassengerSideGlass: false - }); + // Assert + //expect(storeState.applicationUser.eventBus).toEqual([event]); + expect(getters.eventBusItem(storeState)(event.category, event.subCategory)).toEqual( + event.eventValue + ); }); - test("Single windshield requiring recalibration is selected > return correct experimentOrder values", () => { - // Arrange - const storeState = state; - const mockStateValues = { - vehicleYear: 1000, - vehicleMake: "CarMake", - vehicleModel: "CarModel", - vehicleStyle: "SuperCoolStyle", - isRepair: false, - numberOfChips: 0, - carId: "Gibberish", - serviceCity: "Columbus", - serviceState: "OH-IO", - serviceZipCode: 43215, - parentAccountNumber: "999999", - isCoverageVerified: false, - glassParts: [ - { - partNumber: "WINDSHIELDPARTNUMBER", - description: "This is a windshield", - recalibrationType: "ADAS, maybe", - requiresRecalibration: true, - requiresCapabilityQuestions: false - } - ], - otherParts: [ + it("Get event bus", () => { + // Arrange + const storeState = state; + storeState.applicationUser.eventBus = []; - ], - glassToReplace: [ - { - glassLocation: "Windshield", - glassName: "Single" - } - ] - } + const event = { + category: "CategoryOne", + subCategory: "SubCategoryOne", + eventValue: "EventValueOne", + }; - //Act - mutations.updateYear(storeState, mockStateValues.vehicleYear); - mutations.updateMake(storeState, mockStateValues.vehicleMake); - mutations.updateModel(storeState, mockStateValues.vehicleModel); - mutations.updateStyle(storeState, mockStateValues.vehicleStyle); - mutations.updateIsRepair(storeState, mockStateValues.isRepair); - mutations.updateNumberOfChips(storeState, mockStateValues.numberOfChips); - mutations.updateCarId(storeState, mockStateValues.carId); - mutations.updateServiceLocation(storeState, { - city: mockStateValues.serviceCity, - state: mockStateValues.serviceState, - zipCode: mockStateValues.serviceZipCode - }); - mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber); - mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified); - mutations.updateGlassParts(storeState, mockStateValues.glassParts); - mutations.updateOtherParts(storeState, mockStateValues.otherParts); - mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace); + // Act + mutations.addEventToBus(storeState, event); - //Assert - expect(getters.experimentOrder(storeState)).toEqual({ - funnelVehicleYear: mockStateValues.vehicleYear, - funnelVehicleMake: mockStateValues.vehicleMake, - funnelVehicleModel: mockStateValues.vehicleModel, - funnelVehicleStyle: mockStateValues.vehicleStyle, - funnelIsRepair: mockStateValues.isRepair, - funnelNumberOfChips: mockStateValues.numberOfChips, - funnelCarId: mockStateValues.carId, - funnelServiceCity: mockStateValues.serviceCity, - funnelServiceState: mockStateValues.serviceState, - funnelServiceZipCode: mockStateValues.serviceZipCode, - funnelParentAccountNumber: mockStateValues.parentAccountNumber, - funnelIsCoverageVerified: mockStateValues.isCoverageVerified, - funnelOrderPartNumbers: ["WINDSHIELDPARTNUMBER"], - funnelOrderPartTypes: ["ADAS, maybe"], - funnelHasRecalibrationPart: true, - funnelSelectedMultiGlass: false, - funnelSelectedWindshieldGlass: true, - funnelSelectedBackGlass: false, - funnelSelectedDriverSideGlass: false, - funnelSelectedPassengerSideGlass: false - }); + // Assert + expect(getters.eventBus(storeState)).toEqual([event]); }); - test("Select multiglass > return correct experimentOrder values", () => { - // Arrange - const storeState = state; - const mockStateValues = { - vehicleYear: 1000, - vehicleMake: "CarMake", - vehicleModel: "CarModel", - vehicleStyle: "SuperCoolStyle", - isRepair: false, - numberOfChips: 0, - carId: "Gibberish", - serviceCity: "Columbus", - serviceState: "OH-IO", - serviceZipCode: 43215, - parentAccountNumber: "999999", - isCoverageVerified: false, - glassParts: [ - { - partNumber: "BACKGLASS_PN", - description: "This is a back glass", - recalibrationType: null, - requiresRecalibration: false, - requiresCapabilityQuestions: false - }, - { - partNumber: "DRIVERGLASS_PN", - description: "This is a driver side glass", - recalibrationType: null, - requiresRecalibration: false, - requiresCapabilityQuestions: false - }, - { - partNumber: "PASSENGERGLASS_PN", - description: "This is a passenger side glass", - recalibrationType: null, - requiresRecalibration: false, - requiresCapabilityQuestions: false - } - ], - otherParts: [ + it("Damage getter, should return damage data", () => { + // Arrange + const storeState = state; - ], - glassToReplace: [ - { - glassLocation: "Rear", - glassName: "Stationary" - }, - { - glassLocation: "Driver", - glassName: "Front" - }, - { - glassLocation: "Passenger", - glassName: "Front" - }, - { - glassLocation: "Passenger", - glassName: "Quarter" - } - ] - } + // Act + mutations.updateGlassToReplace(storeState, ["Rear"]); - //Act - mutations.updateYear(storeState, mockStateValues.vehicleYear); - mutations.updateMake(storeState, mockStateValues.vehicleMake); - mutations.updateModel(storeState, mockStateValues.vehicleModel); - mutations.updateStyle(storeState, mockStateValues.vehicleStyle); - mutations.updateIsRepair(storeState, mockStateValues.isRepair); - mutations.updateNumberOfChips(storeState, mockStateValues.numberOfChips); - mutations.updateCarId(storeState, mockStateValues.carId); - mutations.updateServiceLocation(storeState, { - city: mockStateValues.serviceCity, - state: mockStateValues.serviceState, - zipCode: mockStateValues.serviceZipCode - }); - mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber); - mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified); - mutations.updateGlassParts(storeState, mockStateValues.glassParts); - mutations.updateOtherParts(storeState, mockStateValues.otherParts); - mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace); - - //Assert - expect(getters.experimentOrder(storeState)).toEqual({ - funnelVehicleYear: mockStateValues.vehicleYear, - funnelVehicleMake: mockStateValues.vehicleMake, - funnelVehicleModel: mockStateValues.vehicleModel, - funnelVehicleStyle: mockStateValues.vehicleStyle, - funnelIsRepair: mockStateValues.isRepair, - funnelNumberOfChips: mockStateValues.numberOfChips, - funnelCarId: mockStateValues.carId, - funnelServiceCity: mockStateValues.serviceCity, - funnelServiceState: mockStateValues.serviceState, - funnelServiceZipCode: mockStateValues.serviceZipCode, - funnelParentAccountNumber: mockStateValues.parentAccountNumber, - funnelIsCoverageVerified: mockStateValues.isCoverageVerified, - funnelOrderPartNumbers: ["BACKGLASS_PN", "DRIVERGLASS_PN", "PASSENGERGLASS_PN"], - funnelOrderPartTypes: [], - funnelHasRecalibrationPart: false, - funnelSelectedMultiGlass: true, - funnelSelectedWindshieldGlass: false, - funnelSelectedBackGlass: true, - funnelSelectedDriverSideGlass: true, - funnelSelectedPassengerSideGlass: true - }); + // Assert + expect(getters.damage(storeState).glassToReplace).toEqual(["Rear"]); }); - }) -}); \ No newline at end of file + + it("lineItems getter, should return lineItem data", () => { + // Arrange + const storeState = state; + + // Act + mutations.updateGlassParts(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({}); + }); + + it("Payment getter, should return payment data", () => { + // Arrange + const storeState = state; + + //Act + mutations.updateInsuranceVerifiedStatus(storeState, true); + + //Assert + expect(getters.payment(storeState).insuranceCoverage.isVerified).toEqual(true); + }); + + describe("experimentOrder", () => { + test("glassToReplace, glassParts, and otherParts are null > return correct experimentOrder values", () => { + // Arrange + const storeState = state; + const mockStateValues = { + funnelVehicleYear: 1000, + funnelVehicleMake: "CarMake", + funnelVehicleModel: "CarModel", + funnelVehicleStyle: "SuperCoolStyle", + funnelIsRepair: true, + funnelNumberOfChips: 9999999, + funnelCarId: "Gibberish", + funnelServiceCity: "Columbus", + funnelServiceState: "OH-IO", + funnelServiceZipCode: 43215, + funnelParentAccountNumber: "999999", + funnelIsCoverageVerified: true, + funnelGlassParts: null, + funnelOtherParts: null, + funnelGlassToReplace: null, + }; + + //Act + mutations.updateYear(storeState, mockStateValues.funnelVehicleYear); + mutations.updateMake(storeState, mockStateValues.funnelVehicleMake); + mutations.updateModel(storeState, mockStateValues.funnelVehicleModel); + mutations.updateStyle(storeState, mockStateValues.funnelVehicleStyle); + mutations.updateIsRepair(storeState, mockStateValues.funnelIsRepair); + mutations.updateNumberOfChips(storeState, mockStateValues.funnelNumberOfChips); + mutations.updateCarId(storeState, mockStateValues.funnelCarId); + mutations.updateServiceLocation(storeState, { + city: mockStateValues.funnelServiceCity, + state: mockStateValues.funnelServiceState, + zipCode: mockStateValues.funnelServiceZipCode, + }); + mutations.updateParentAcctNumber(storeState, mockStateValues.funnelParentAccountNumber); + mutations.updateInsuranceVerifiedStatus( + storeState, + mockStateValues.funnelIsCoverageVerified + ); + mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts); + mutations.updateOtherParts(storeState, mockStateValues.funnelOtherParts); + mutations.updateGlassToReplace(storeState, mockStateValues.funnelGlassToReplace); + + //Assert + expect(getters.experimentOrder(storeState)).toEqual({ + funnelVehicleYear: mockStateValues.funnelVehicleYear, + funnelVehicleMake: mockStateValues.funnelVehicleMake, + funnelVehicleModel: mockStateValues.funnelVehicleModel, + funnelVehicleStyle: mockStateValues.funnelVehicleStyle, + funnelIsRepair: mockStateValues.funnelIsRepair, + funnelNumberOfChips: mockStateValues.funnelNumberOfChips, + funnelCarId: mockStateValues.funnelCarId, + funnelServiceCity: mockStateValues.funnelServiceCity, + funnelServiceState: mockStateValues.funnelServiceState, + funnelServiceZipCode: mockStateValues.funnelServiceZipCode, + funnelParentAccountNumber: mockStateValues.funnelParentAccountNumber, + funnelIsCoverageVerified: mockStateValues.funnelIsCoverageVerified, + funnelOrderPartNumbers: [], + funnelOrderPartTypes: [], + funnelHasRecalibrationPart: false, + funnelSelectedMultiGlass: false, + funnelSelectedWindshieldGlass: false, + funnelSelectedBackGlass: false, + funnelSelectedDriverSideGlass: false, + funnelSelectedPassengerSideGlass: false, + }); + }); + + test("glassToReplace, glassParts, and otherParts are empty > return correct experimentOrder values", () => { + // Arrange + const storeState = state; + const mockStateValues = { + funnelVehicleYear: 1000, + funnelVehicleMake: "CarMake", + funnelVehicleModel: "CarModel", + funnelVehicleStyle: "SuperCoolStyle", + funnelIsRepair: true, + funnelNumberOfChips: 9999999, + funnelCarId: "Gibberish", + funnelServiceCity: "Columbus", + funnelServiceState: "OH-IO", + funnelServiceZipCode: 43215, + funnelParentAccountNumber: "999999", + funnelIsCoverageVerified: true, + funnelGlassParts: [], + funnelOtherParts: [], + funnelGlassToReplace: [], + }; + + //Act + mutations.updateYear(storeState, mockStateValues.funnelVehicleYear); + mutations.updateMake(storeState, mockStateValues.funnelVehicleMake); + mutations.updateModel(storeState, mockStateValues.funnelVehicleModel); + mutations.updateStyle(storeState, mockStateValues.funnelVehicleStyle); + mutations.updateIsRepair(storeState, mockStateValues.funnelIsRepair); + mutations.updateNumberOfChips(storeState, mockStateValues.funnelNumberOfChips); + mutations.updateCarId(storeState, mockStateValues.funnelCarId); + mutations.updateServiceLocation(storeState, { + city: mockStateValues.funnelServiceCity, + state: mockStateValues.funnelServiceState, + zipCode: mockStateValues.funnelServiceZipCode, + }); + mutations.updateParentAcctNumber(storeState, mockStateValues.funnelParentAccountNumber); + mutations.updateInsuranceVerifiedStatus( + storeState, + mockStateValues.funnelIsCoverageVerified + ); + mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts); + mutations.updateOtherParts(storeState, mockStateValues.funnelOtherParts); + mutations.updateGlassToReplace(storeState, mockStateValues.funnelGlassToReplace); + + //Assert + expect(getters.experimentOrder(storeState)).toEqual({ + funnelVehicleYear: mockStateValues.funnelVehicleYear, + funnelVehicleMake: mockStateValues.funnelVehicleMake, + funnelVehicleModel: mockStateValues.funnelVehicleModel, + funnelVehicleStyle: mockStateValues.funnelVehicleStyle, + funnelIsRepair: mockStateValues.funnelIsRepair, + funnelNumberOfChips: mockStateValues.funnelNumberOfChips, + funnelCarId: mockStateValues.funnelCarId, + funnelServiceCity: mockStateValues.funnelServiceCity, + funnelServiceState: mockStateValues.funnelServiceState, + funnelServiceZipCode: mockStateValues.funnelServiceZipCode, + funnelParentAccountNumber: mockStateValues.funnelParentAccountNumber, + funnelIsCoverageVerified: mockStateValues.funnelIsCoverageVerified, + funnelOrderPartNumbers: [], + funnelOrderPartTypes: [], + funnelHasRecalibrationPart: false, + funnelSelectedMultiGlass: false, + funnelSelectedWindshieldGlass: false, + funnelSelectedBackGlass: false, + funnelSelectedDriverSideGlass: false, + funnelSelectedPassengerSideGlass: false, + }); + }); + + test("Single windshield requiring recalibration is selected > return correct experimentOrder values", () => { + // Arrange + const storeState = state; + const mockStateValues = { + vehicleYear: 1000, + vehicleMake: "CarMake", + vehicleModel: "CarModel", + vehicleStyle: "SuperCoolStyle", + isRepair: false, + numberOfChips: 0, + carId: "Gibberish", + serviceCity: "Columbus", + serviceState: "OH-IO", + serviceZipCode: 43215, + parentAccountNumber: "999999", + isCoverageVerified: false, + glassParts: [ + { + partNumber: "WINDSHIELDPARTNUMBER", + description: "This is a windshield", + recalibrationType: "ADAS, maybe", + requiresRecalibration: true, + requiresCapabilityQuestions: false, + }, + ], + otherParts: [], + glassToReplace: [ + { + glassLocation: "Windshield", + glassName: "Single", + }, + ], + }; + + //Act + mutations.updateYear(storeState, mockStateValues.vehicleYear); + mutations.updateMake(storeState, mockStateValues.vehicleMake); + mutations.updateModel(storeState, mockStateValues.vehicleModel); + mutations.updateStyle(storeState, mockStateValues.vehicleStyle); + mutations.updateIsRepair(storeState, mockStateValues.isRepair); + mutations.updateNumberOfChips(storeState, mockStateValues.numberOfChips); + mutations.updateCarId(storeState, mockStateValues.carId); + mutations.updateServiceLocation(storeState, { + city: mockStateValues.serviceCity, + state: mockStateValues.serviceState, + zipCode: mockStateValues.serviceZipCode, + }); + mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber); + mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified); + mutations.updateGlassParts(storeState, mockStateValues.glassParts); + mutations.updateOtherParts(storeState, mockStateValues.otherParts); + mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace); + + //Assert + expect(getters.experimentOrder(storeState)).toEqual({ + funnelVehicleYear: mockStateValues.vehicleYear, + funnelVehicleMake: mockStateValues.vehicleMake, + funnelVehicleModel: mockStateValues.vehicleModel, + funnelVehicleStyle: mockStateValues.vehicleStyle, + funnelIsRepair: mockStateValues.isRepair, + funnelNumberOfChips: mockStateValues.numberOfChips, + funnelCarId: mockStateValues.carId, + funnelServiceCity: mockStateValues.serviceCity, + funnelServiceState: mockStateValues.serviceState, + funnelServiceZipCode: mockStateValues.serviceZipCode, + funnelParentAccountNumber: mockStateValues.parentAccountNumber, + funnelIsCoverageVerified: mockStateValues.isCoverageVerified, + funnelOrderPartNumbers: ["WINDSHIELDPARTNUMBER"], + funnelOrderPartTypes: ["ADAS, maybe"], + funnelHasRecalibrationPart: true, + funnelSelectedMultiGlass: false, + funnelSelectedWindshieldGlass: true, + funnelSelectedBackGlass: false, + funnelSelectedDriverSideGlass: false, + funnelSelectedPassengerSideGlass: false, + }); + }); + + test("Select multiglass > return correct experimentOrder values", () => { + // Arrange + const storeState = state; + const mockStateValues = { + vehicleYear: 1000, + vehicleMake: "CarMake", + vehicleModel: "CarModel", + vehicleStyle: "SuperCoolStyle", + isRepair: false, + numberOfChips: 0, + carId: "Gibberish", + serviceCity: "Columbus", + serviceState: "OH-IO", + serviceZipCode: 43215, + parentAccountNumber: "999999", + isCoverageVerified: false, + glassParts: [ + { + partNumber: "BACKGLASS_PN", + description: "This is a back glass", + recalibrationType: null, + requiresRecalibration: false, + requiresCapabilityQuestions: false, + }, + { + partNumber: "DRIVERGLASS_PN", + description: "This is a driver side glass", + recalibrationType: null, + requiresRecalibration: false, + requiresCapabilityQuestions: false, + }, + { + partNumber: "PASSENGERGLASS_PN", + description: "This is a passenger side glass", + recalibrationType: null, + requiresRecalibration: false, + requiresCapabilityQuestions: false, + }, + ], + otherParts: [], + glassToReplace: [ + { + glassLocation: "Rear", + glassName: "Stationary", + }, + { + glassLocation: "Driver", + glassName: "Front", + }, + { + glassLocation: "Passenger", + glassName: "Front", + }, + { + glassLocation: "Passenger", + glassName: "Quarter", + }, + ], + }; + + //Act + mutations.updateYear(storeState, mockStateValues.vehicleYear); + mutations.updateMake(storeState, mockStateValues.vehicleMake); + mutations.updateModel(storeState, mockStateValues.vehicleModel); + mutations.updateStyle(storeState, mockStateValues.vehicleStyle); + mutations.updateIsRepair(storeState, mockStateValues.isRepair); + mutations.updateNumberOfChips(storeState, mockStateValues.numberOfChips); + mutations.updateCarId(storeState, mockStateValues.carId); + mutations.updateServiceLocation(storeState, { + city: mockStateValues.serviceCity, + state: mockStateValues.serviceState, + zipCode: mockStateValues.serviceZipCode, + }); + mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber); + mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified); + mutations.updateGlassParts(storeState, mockStateValues.glassParts); + mutations.updateOtherParts(storeState, mockStateValues.otherParts); + mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace); + + //Assert + expect(getters.experimentOrder(storeState)).toEqual({ + funnelVehicleYear: mockStateValues.vehicleYear, + funnelVehicleMake: mockStateValues.vehicleMake, + funnelVehicleModel: mockStateValues.vehicleModel, + funnelVehicleStyle: mockStateValues.vehicleStyle, + funnelIsRepair: mockStateValues.isRepair, + funnelNumberOfChips: mockStateValues.numberOfChips, + funnelCarId: mockStateValues.carId, + funnelServiceCity: mockStateValues.serviceCity, + funnelServiceState: mockStateValues.serviceState, + funnelServiceZipCode: mockStateValues.serviceZipCode, + funnelParentAccountNumber: mockStateValues.parentAccountNumber, + funnelIsCoverageVerified: mockStateValues.isCoverageVerified, + funnelOrderPartNumbers: ["BACKGLASS_PN", "DRIVERGLASS_PN", "PASSENGERGLASS_PN"], + funnelOrderPartTypes: [], + funnelHasRecalibrationPart: false, + funnelSelectedMultiGlass: true, + funnelSelectedWindshieldGlass: false, + funnelSelectedBackGlass: true, + funnelSelectedDriverSideGlass: true, + funnelSelectedPassengerSideGlass: true, + }); + }); + }); +}); diff --git a/src/styles/common-animations.scss b/src/styles/common-animations.scss index c03c267ab..8b73d30d1 100644 --- a/src/styles/common-animations.scss +++ b/src/styles/common-animations.scss @@ -2,8 +2,8 @@ transition: opacity 0.8s ease; } -.fade-leave-active { - transition: opacity 0.3s ease; +.fade-leave-active { + transition: opacity 0.3s ease; } .fade-enter-from, @@ -16,7 +16,7 @@ } .route-fade-leave-active .fade-on-route-transition { - transition: opacity 0.2s ease-in; //This duration should be kept in sync with the app.vue element attribute + transition: opacity 0.2s ease-in; //This duration should be kept in sync with the app.vue element attribute } .route-fade-enter-from .fade-on-route-transition, @@ -27,4 +27,4 @@ has been fixed. */ opacity: 0.001; -} \ No newline at end of file +} diff --git a/src/styles/common-error-styles.scss b/src/styles/common-error-styles.scss index ea39df63c..f2ff5cf23 100644 --- a/src/styles/common-error-styles.scss +++ b/src/styles/common-error-styles.scss @@ -1,160 +1,160 @@ html { - .has-error { - &.list-button, - &.list-card, - &.list-card.list-button { - color: $red; - input[type=checkbox]:focus + label, - input[type=radio]:focus + label { - box-shadow: 0 0 0 2.5px $red; - } - input[type=checkbox]:checked + label { - box-shadow: 0 0 0 1px $red; - } - &:hover { - box-shadow: 0px 0px 0px 4px $red-200; - border-radius: .5rem; - } - label { - border: 1px solid $red; - border-radius: .5rem; - } - label:hover { - box-shadow: 0px 0px 0px 4px $red-200; - border-radius: 10px; - border: 1px solid $red; - } - } - &.list-button-horizontal { - color: $red; - label { - border: 1px solid $red; - &:hover { - box-shadow: 0px 0px 0px 4px $red-200; - } - } - input[type=checkbox]:focus + label, - input[type=radio]:focus + label { - box-shadow: 0 0 1px $red; - } - } - &.grid-item { - input[type="radio"] { - + label { - border: 1px solid $red; - &:hover { - background-color: $blue-100; - box-shadow: 0px 0px 0px 4px $red-200; - } - } - } - } - &.ui-radio, - &.ui-checkbox { - input[type=checkbox], - input[type=radio], - input[type=radio]+label:before, - input[type=checkbox]+label:before { - border: 1px solid $red; - background-color: initial; - } - input[type=checkbox]:checked + label:before { - border: 1px solid $blue; - } - } - &.textbox-question, - &.dropdown-question { - input:hover { - box-shadow: 0px 0px 0px 4px $red-200; - border-radius: .5rem; - border: 1px solid $red; - } - input:focus { - box-shadow: 0 0 0 2.5px $red; - } - p { - color: $red; - } - input, - select { - border: 1px solid transparent; - box-shadow: 0 0 0 1px $red; - &:focus { - border: 1px solid transparent; - } - } - select { - background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%23d4281c'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right 0.75rem center; - background-size: 16px 12px; - } - } - } - //Restore to default style if alert box is present - .alertError { .has-error { - &.list-button, - &.list-card { - border: 1px solid $gray-500; - input:not(:focus) { - + label { - border: none; - box-shadow: 0 0 0 1px $gray-500; - border-radius: .5rem; - } + &.list-button, + &.list-card, + &.list-card.list-button { + color: $red; + input[type="checkbox"]:focus + label, + input[type="radio"]:focus + label { + box-shadow: 0 0 0 2.5px $red; + } + input[type="checkbox"]:checked + label { + box-shadow: 0 0 0 1px $red; + } + &:hover { + box-shadow: 0px 0px 0px 4px $red-200; + border-radius: 0.5rem; + } + label { + border: 1px solid $red; + border-radius: 0.5rem; + } + label:hover { + box-shadow: 0px 0px 0px 4px $red-200; + border-radius: 10px; + border: 1px solid $red; + } } - input:checked:focus { - + label { - border: none; - box-shadow: 0 0 0 2.5px $blue; - border-radius: .5rem; - } + &.list-button-horizontal { + color: $red; + label { + border: 1px solid $red; + &:hover { + box-shadow: 0px 0px 0px 4px $red-200; + } + } + input[type="checkbox"]:focus + label, + input[type="radio"]:focus + label { + box-shadow: 0 0 1px $red; + } } - input:checked:not(:focus) { - + label { - box-shadow: 0 0 0 1px $blue; - border-radius: .5rem; - } + &.grid-item { + input[type="radio"] { + + label { + border: 1px solid $red; + &:hover { + background-color: $blue-100; + box-shadow: 0px 0px 0px 4px $red-200; + } + } + } } - input:focus { - border: 1px solid $gray-500; - + label { - box-shadow: 0 0 0 2.5px transparent; - } + &.ui-radio, + &.ui-checkbox { + input[type="checkbox"], + input[type="radio"], + input[type="radio"] + label:before, + input[type="checkbox"] + label:before { + border: 1px solid $red; + background-color: initial; + } + input[type="checkbox"]:checked + label:before { + border: 1px solid $blue; + } } - &:hover { - box-shadow: 0 0 0 4px $blue-300; - + label { - box-shadow: 0 0 0 2.5px transparent; - border: 1px solid $blue; - } + &.textbox-question, + &.dropdown-question { + input:hover { + box-shadow: 0px 0px 0px 4px $red-200; + border-radius: 0.5rem; + border: 1px solid $red; + } + input:focus { + box-shadow: 0 0 0 2.5px $red; + } + p { + color: $red; + } + input, + select { + border: 1px solid transparent; + box-shadow: 0 0 0 1px $red; + &:focus { + border: 1px solid transparent; + } + } + select { + background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%23d4281c'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right 0.75rem center; + background-size: 16px 12px; + } + } + } + //Restore to default style if alert box is present + .alertError { + .has-error { + &.list-button, + &.list-card { + border: 1px solid $gray-500; + input:not(:focus) { + + label { + border: none; + box-shadow: 0 0 0 1px $gray-500; + border-radius: 0.5rem; + } + } + input:checked:focus { + + label { + border: none; + box-shadow: 0 0 0 2.5px $blue; + border-radius: 0.5rem; + } + } + input:checked:not(:focus) { + + label { + box-shadow: 0 0 0 1px $blue; + border-radius: 0.5rem; + } + } + input:focus { + border: 1px solid $gray-500; + + label { + box-shadow: 0 0 0 2.5px transparent; + } + } + &:hover { + box-shadow: 0 0 0 4px $blue-300; + + label { + box-shadow: 0 0 0 2.5px transparent; + border: 1px solid $blue; + } + } + } } - } } - } - .form-test-error { - color: $red; - font-size: .875rem; - font-weight: 500; - } + .form-test-error { + color: $red; + font-size: 0.875rem; + font-weight: 500; + } - .form-test-invalid { - &.btn.btn-primary { - color: $gray-600; - background: $gray-200; - cursor: pointer; - pointer-events: all; - font-weight: $font-weight-normal; + .form-test-invalid { + &.btn.btn-primary { + color: $gray-600; + background: $gray-200; + cursor: pointer; + pointer-events: all; + font-weight: $font-weight-normal; + } + &.btn.btn-primary:hover { + background: $gray-200; + box-shadow: none; + } + &.btn.btn-primary:focus, + &.btn.btn-primary:focus-visible { + box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue-700; + } } - &.btn.btn-primary:hover { - background: $gray-200; - box-shadow: none; - } - &.btn.btn-primary:focus, - &.btn.btn-primary:focus-visible { - box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue-700; - } - } } diff --git a/src/styles/common-styles.scss b/src/styles/common-styles.scss index cac5f1f42..73845e5cc 100644 --- a/src/styles/common-styles.scss +++ b/src/styles/common-styles.scss @@ -1,59 +1,58 @@ // Common/Global Styles // Use this file for global styles that don't or won't have their own stylesheet body { - font-size: 16px; - background-color: #fff; - color: #4D5151; - .container-fluid { - max-width: 576px; //Remove once desktop app is complete - &.container-shadow { - box-shadow: 0px 0px 6px 0px rgba(0,0,0,0.15); //Use instead of Bootstrap's helper + font-size: 16px; + background-color: #fff; + color: #4d5151; + .container-fluid { + max-width: 576px; //Remove once desktop app is complete + &.container-shadow { + box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.15); //Use instead of Bootstrap's helper + } + &.make-tall { + height: 100vh; + display: flex; + flex-direction: column; + } + .prevent-squish { + overflow-x: unset; + } } - &.make-tall { - height: 100vh; - display: flex; - flex-direction: column; + .pointer { + cursor: pointer; } - .prevent-squish{ - overflow-x: unset; + .container, + .container-fluid { + overflow-x: hidden; } - } - .pointer { - cursor: pointer; - } - .container, - .container-fluid { - overflow-x: hidden; - } - .sub-container{ - &.make-tall { - height: 100%; - width: 100%; - display: flex; - flex-direction: column; + .sub-container { + &.make-tall { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + } } - } - .sr-only { - position: absolute; - left: -10000px; - top: auto; - width: 1px; - height: 1px; - overflow: hidden; - } + .sr-only { + position: absolute; + left: -10000px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; + } - .page-container-grouped-styles { - @extend .container-fluid, .shadow, .rounded-3, .p-2, .position-relative, .make-tall, .px-5; - } - - //Footer modal backdrop adjustments for positioning - .modal-backdrop { - left: 50%; - transform: translateX(-50%); - max-width: 576px; - height: calc(100% - 72px); - } + .page-container-grouped-styles { + @extend .container-fluid, .shadow, .rounded-3, .p-2, .position-relative, .make-tall, .px-5; + } + //Footer modal backdrop adjustments for positioning + .modal-backdrop { + left: 50%; + transform: translateX(-50%); + max-width: 576px; + height: calc(100% - 72px); + } } diff --git a/src/styles/common-typography-styles.scss b/src/styles/common-typography-styles.scss index 9ee9aa286..72b2e5875 100644 --- a/src/styles/common-typography-styles.scss +++ b/src/styles/common-typography-styles.scss @@ -1,67 +1,72 @@ //Typography p, body { - font-size: 1rem; - line-height: 1.625; - font-weight: 400; + font-size: 1rem; + line-height: 1.625; + font-weight: 400; } //Headings //add helper fw-bold to any element to get bold style (500) -h1,.h1 { - line-height: 1.325; - font-weight: 300; +h1, +.h1 { + line-height: 1.325; + font-weight: 300; } -h2,.h2 { - line-height: 1.325; - font-weight: 300; +h2, +.h2 { + line-height: 1.325; + font-weight: 300; } -h3,.h3 { - line-height: 1.375; - font-weight: 300; +h3, +.h3 { + line-height: 1.375; + font-weight: 300; } -h4,.h4 { - line-height: 1.6; - font-weight: 300; +h4, +.h4 { + line-height: 1.6; + font-weight: 300; } -h5,.h5 { - line-height: 1.325; - font-weight: 400; +h5, +.h5 { + line-height: 1.325; + font-weight: 400; } -h6,.h6 { - line-height: 1.7; - letter-spacing: .75px; - text-transform: uppercase; +h6, +.h6 { + line-height: 1.7; + letter-spacing: 0.75px; + text-transform: uppercase; } .small { - font-size: .875rem; - line-height: 1.7; - font-weight: 400; + font-size: 0.875rem; + line-height: 1.7; + font-weight: 400; } label, .label { - font-size: 1rem; - line-height: 1.5; - font-weight: 400; + font-size: 1rem; + line-height: 1.5; + font-weight: 400; } caption, .caption { - font-size: .75rem; - line-height: 1.7; - font-weight: 400; + font-size: 0.75rem; + line-height: 1.7; + font-weight: 400; } - // Font size .fs-5 { - line-height: 2; + line-height: 2; } .fs-6 { - font-size: 1rem !important; - line-height: 1.4; - font-weight: 500; -} \ No newline at end of file + font-size: 1rem !important; + line-height: 1.4; + font-weight: 500; +} diff --git a/src/styles/mixins/customMixins.scss b/src/styles/mixins/customMixins.scss index 3576a8ac9..8620968f0 100644 --- a/src/styles/mixins/customMixins.scss +++ b/src/styles/mixins/customMixins.scss @@ -2,5 +2,5 @@ //Blue gradient background mixin @mixin blue-gradient { - background: linear-gradient(270deg, $blue 0%, $blue-800 100%); + background: linear-gradient(270deg, $blue 0%, $blue-800 100%); } diff --git a/src/styles/ux-variables.scss b/src/styles/ux-variables.scss index 5944c8c42..d9b462af4 100644 --- a/src/styles/ux-variables.scss +++ b/src/styles/ux-variables.scss @@ -1,187 +1,189 @@ //Colors // White/black -$white: #FFFFFF; -$black: #000000; +$white: #ffffff; +$black: #000000; // Blues -$blue-100: #E4F1F7;// Used in theme -$blue-200: #C1DFEE; -$blue-300: #9FCEE6; -$blue-400: #69ADCF;// Used in theme -$blue-500: #3B8FB8; -$blue: #1574A1;// Default Blue -$blue-700: #06577C; -$blue-800: #003D58; -$blue-900: #002433;// Used in theme +$blue-100: #e4f1f7; // Used in theme +$blue-200: #c1dfee; +$blue-300: #9fcee6; +$blue-400: #69adcf; // Used in theme +$blue-500: #3b8fb8; +$blue: #1574a1; // Default Blue +$blue-700: #06577c; +$blue-800: #003d58; +$blue-900: #002433; // Used in theme // Reds -$red-100: #FFE6E4; -$red-200: #FCBFBB; -$red-300: #F89892; -$red-400: #E65C53; -$red: #D4281C;// Default Red -$red-600: #AC160B; -$red-700: #840900; -$red-800: #5B0600; -$red-900: #330300; +$red-100: #ffe6e4; +$red-200: #fcbfbb; +$red-300: #f89892; +$red-400: #e65c53; +$red: #d4281c; // Default Red +$red-600: #ac160b; +$red-700: #840900; +$red-800: #5b0600; +$red-900: #330300; // Greens -$green-100: #E3F2EA; -$green-200: #BCEDD4; -$green-300: #94D7B6; -$green-400: #5ABC8C;// Used in theme -$green-500: #2CA168; -$green: #0C7E47;// Default Green -$green-700: #006A36; -$green-800: #004F28; -$green-900: #00331A; +$green-100: #e3f2ea; +$green-200: #bcedd4; +$green-300: #94d7b6; +$green-400: #5abc8c; // Used in theme +$green-500: #2ca168; +$green: #0c7e47; // Default Green +$green-700: #006a36; +$green-800: #004f28; +$green-900: #00331a; // Yellows -$yellow-100: #FFF5EB; -$yellow-200: #FFE1C6; -$yellow-300: #FFCAA0; -$yellow-400: #F5975D; -$yellow: #E86421;// Default Yellow -$yellow-600: #BB4B06; -$yellow-700: #8E3C00; -$yellow-800: #602D00; -$yellow-900: #331A00; +$yellow-100: #fff5eb; +$yellow-200: #ffe1c6; +$yellow-300: #ffcaa0; +$yellow-400: #f5975d; +$yellow: #e86421; // Default Yellow +$yellow-600: #bb4b06; +$yellow-700: #8e3c00; +$yellow-800: #602d00; +$yellow-900: #331a00; // Grays -$gray-100: #F5F5F5;// Used in theme -$gray-200: #E3E4E4;// Used in theme -$gray-300: #D2D4D4; -$gray: #B0B3B3;// Default Gray -$gray-500: #8E9292;// Used in theme -$gray-550: #727676;// Used in theme -$gray-600: #4D5151;// Used in theme -$gray-700: #303333;// Used in theme -$gray-800: #222424;// Used in theme -$gray-900: #181A1A;// Used in theme +$gray-100: #f5f5f5; // Used in theme +$gray-200: #e3e4e4; // Used in theme +$gray-300: #d2d4d4; +$gray: #b0b3b3; // Default Gray +$gray-500: #8e9292; // Used in theme +$gray-550: #727676; // Used in theme +$gray-600: #4d5151; // Used in theme +$gray-700: #303333; // Used in theme +$gray-800: #222424; // Used in theme +$gray-900: #181a1a; // Used in theme // Miscellaneous Colors -$indigo: #6610f2; -$purple: #6f42c1; -$pink: #d63384; -$orange: #fd7e14; -$teal: #20c997; -$cyan: #0dcaf0; +$indigo: #6610f2; +$purple: #6f42c1; +$pink: #d63384; +$orange: #fd7e14; +$teal: #20c997; +$cyan: #0dcaf0; // scss-docs-start colors-map $colors: ( -"blue": $blue, -"indigo": $indigo, -"purple": $purple, -"pink": $pink, -"red": $red, -"orange": $orange, -"yellow": $yellow, -"green": $green, -"teal": $teal, -"cyan": $cyan, -"white": $white, -"gray": $gray, -"gray-dark": $gray-500 + "blue": $blue, + "indigo": $indigo, + "purple": $purple, + "pink": $pink, + "red": $red, + "orange": $orange, + "yellow": $yellow, + "green": $green, + "teal": $teal, + "cyan": $cyan, + "white": $white, + "gray": $gray, + "gray-dark": $gray-500, ); // scss-docs-start theme-color-variables -$primary: $blue; -$secondary: $red; -$success: $green; -$info: $cyan; -$warning: $yellow; -$danger: $red; -$light: $gray-100; -$dark: $gray-500; +$primary: $blue; +$secondary: $red; +$success: $green; +$info: $cyan; +$warning: $yellow; +$danger: $red; +$light: $gray-100; +$dark: $gray-500; // scss-docs-start theme-colors-map $theme-colors: ( -"primary": $primary, -"secondary": $secondary, -"success": $success, -"info": $info, -"warning": $warning, -"danger": $danger, -"light": $light, -"dark": $dark + "primary": $primary, + "secondary": $secondary, + "success": $success, + "info": $info, + "warning": $warning, + "danger": $danger, + "light": $light, + "dark": $dark, ); //Default font color -$body-color: $gray-600; +$body-color: $gray-600; //Fonts -$font-family-sans-serif: Roboto, Arial, Helvetica, sans-serif; -$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +$font-family-sans-serif: Roboto, Arial, Helvetica, sans-serif; +$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", + monospace; // stylelint-enable value-keyword-case -$font-family-base: $font-family-sans-serif; -$font-family-code: $font-family-monospace; -$font-size-base: 1rem; // Assumes the browser default, typically `16px` +$font-family-base: $font-family-sans-serif; +$font-family-code: $font-family-monospace; +$font-size-base: 1rem; // Assumes the browser default, typically `16px` //Custom Font size (extra small) -$font-size-xsm: $font-size-base * .75; +$font-size-xsm: $font-size-base * 0.75; $font-sizes: ( - 7: $font-size-xsm + 7: $font-size-xsm, ); //Font weight -$font-weight-lighter: lighter; -$font-weight-light: 300; -$font-weight-normal: 400; -$font-weight-bold: 500; -$font-weight-bolder: bolder; +$font-weight-lighter: lighter; +$font-weight-light: 300; +$font-weight-normal: 400; +$font-weight-bold: 500; +$font-weight-bolder: bolder; //Headings -$h1-font-size: $font-size-base * 3; -$h2-font-size: $font-size-base * 2.625; -$h3-font-size: $font-size-base * 2; -$h4-font-size: $font-size-base * 1.625; -$h5-font-size: $font-size-base * 1.25; -$h6-font-size: $font-size-base * .875; +$h1-font-size: $font-size-base * 3; +$h2-font-size: $font-size-base * 2.625; +$h3-font-size: $font-size-base * 2; +$h4-font-size: $font-size-base * 1.625; +$h5-font-size: $font-size-base * 1.25; +$h6-font-size: $font-size-base * 0.875; //Border Radius // Helper classes are rounded, rounded-1, rounded-2, rounded-3 -$border-radius: .25rem; -$border-radius-sm: .2rem; -$border-radius-lg: .5rem;//Used for buttons. Can be used for other things, of course. -$border-radius-pill: 50rem; +$border-radius: 0.25rem; +$border-radius-sm: 0.2rem; +$border-radius-lg: 0.5rem; //Used for buttons. Can be used for other things, of course. +$border-radius-pill: 50rem; //Spacing // 8 spacers available instead of the usual 5 $spacer: 1rem; $spacers: ( - 0: 0, - 1: $spacer * .25, /* 4px */ - 2: $spacer * .5, /* 8px */ - 3: $spacer * .75, /* 12px */ - 4: $spacer * 1, /* 16px */ - 5: $spacer * 1.5, /* 24px */ - 6: $spacer * 2, /* 32px */ - 7: $spacer * 2.5, /* 40px */ - 8: $spacer * 3, /* 48px */ + 0: 0, + 1: $spacer * 0.25, + /* 4px */ 2: $spacer * 0.5, + /* 8px */ 3: $spacer * 0.75, + /* 12px */ 4: $spacer * 1, + /* 16px */ 5: $spacer * 1.5, + /* 24px */ 6: $spacer * 2, + /* 32px */ 7: $spacer * 2.5, + /* 40px */ 8: $spacer * 3, + /* 48px */ ); //Grid breakpoints $grid-breakpoints: ( - xs: 0, - sm: 576px, - md: 838px, - lg: 1074px, - xl: 1416px + xs: 0, + sm: 576px, + md: 838px, + lg: 1074px, + xl: 1416px, ); //Shadow -$box-shadow: 0 .5rem 1rem rgba($black, .15); -$box-shadow-sm: 0 .125rem .25rem rgba($black, .075); -$box-shadow-lg: 0 1rem 3rem rgba($black, .25);//Safelite default -$box-shadow-inset: inset 0 1px 2px rgba($black, .075); +$box-shadow: 0 0.5rem 1rem rgba($black, 0.15); +$box-shadow-sm: 0 0.125rem 0.25rem rgba($black, 0.075); +$box-shadow-lg: 0 1rem 3rem rgba($black, 0.25); //Safelite default +$box-shadow-inset: inset 0 1px 2px rgba($black, 0.075); //Alerts -$alert-bg-scale: -90%; -$alert-border-scale: -100%; -$alert-color-scale: 40%; +$alert-bg-scale: -90%; +$alert-border-scale: -100%; +$alert-color-scale: 40%; //Modal animation // This affects all [Bootstrap] modals -$modal-fade-transform: translate(0, 100%); -$modal-backdrop-opacity: 0; +$modal-fade-transform: translate(0, 100%); +$modal-backdrop-opacity: 0; diff --git a/src/ux-components/alert/alert.spec.js b/src/ux-components/alert/alert.spec.js index 440362ef4..343323b9e 100644 --- a/src/ux-components/alert/alert.spec.js +++ b/src/ux-components/alert/alert.spec.js @@ -3,206 +3,220 @@ import { getMountOptions } from "@/helpers/unit-test-helper.js"; import alert from "./alert"; describe("alert.vue", () => { + it("Should add class 'alert-dismissible' if isDismissible is true", async () => { + // Arrange + const wrapper = shallowMount( + alert, + setupMocks({ + propsData: { + isDismissible: true, + manualHeadline: "testHeader", + manualCopy: "testCopy", + }, + }) + ); - it("Should add class 'alert-dismissible' if isDismissible is true", async () => { - // Arrange - const wrapper = shallowMount(alert, setupMocks({ - propsData: { - isDismissible: true, - manualHeadline: 'testHeader', - manualCopy: 'testCopy' - }, - })); + const wrapperDiv = wrapper.find("div"); - const wrapperDiv = wrapper.find('div'); - - // Assert - expect(wrapperDiv.classes()).toContain('alert-dismissible') - }); - - it("Should add specified alert class", async () => { - // Arrange - const wrapper = shallowMount(alert, setupMocks({ - propsData: { - alertClass: 'warning', - manualHeadline: 'testHeader', - manualCopy: 'testCopy' - }, - })); - - const wrapperDiv = wrapper.find('div'); - - // Assert - expect(wrapperDiv.classes()).toContain('warning') - }); - - it("Should update alert Headline to manualHeadline datam entered and alert copy to manualCopy datam entered when no cmsWidgetName entered", async () => { - // Arrange - const wrapper = shallowMount(alert, setupMocks({})); - // Assert - expect(wrapper.vm.alertHeadline).toBe("testHeader"); - expect(wrapper.vm.alertCopy).toBe("testCopy"); - }); - - it("Should container a tag if the manualCopy contains a {routerLink: testName, testLink} placeholder", () =>{ - // Arrange & Act - const wrapper = shallowMount(alert, setupMocks({ - propsData: { - manualHeadline: 'testHeader', - manualCopy: 'testCopy with a {routerLink: testName, testLink} inside of it' - }, - stubs: ['router-link'], - })); - // Assert - expect(wrapper.find('router-link').exists()).toBe(true); - }); - - it("Should contain 'n+1'

tags if the body copy has 'n'

tags", () =>{ - // Arrange & Act - const wrapper = shallowMount(alert, setupMocks({ - propsData: { - manualHeadline: 'testHeader', - manualCopy: '

testCopy with a {routerLink: testName, testLink} inside of it

and two paragraphs

' - }, - stubs: ['router-link'], - })); - // Assert - expect(wrapper.findAll('p').length === 3).toBe(true); - }); - - it("Should call scrollIntoView() when the clientBoundingRect is not entirely in the viewport (out of view top)", () => { - // Arrange - var viewPortHeight = 200; - setUpViewPort(viewPortHeight); - - Element.prototype.getBoundingClientRect = jest.fn(()=> { - return {top: -100, bottom: 200} + // Assert + expect(wrapperDiv.classes()).toContain("alert-dismissible"); }); - - var mockScrollIntoView = jest.fn(); - Element.prototype.scrollIntoView = mockScrollIntoView; - // Act - const wrapper = shallowMount(alert, setupMocks({})); + it("Should add specified alert class", async () => { + // Arrange + const wrapper = shallowMount( + alert, + setupMocks({ + propsData: { + alertClass: "warning", + manualHeadline: "testHeader", + manualCopy: "testCopy", + }, + }) + ); - // Assert - // This is an implementation detail - we just need to test that the final step of snapping - // the window to the alert is working. If using a different function to accomplish that - // just swap this out with the new function - expect(mockScrollIntoView).toHaveBeenCalled(); - }); + const wrapperDiv = wrapper.find("div"); - it("Should call scrollIntoView() when the clientBoundingRect is not entirely in the viewport (bottom is hidden behind footer)", () => { - // Arrange - var viewPortHeight = 240; - setUpViewPort(viewPortHeight); - - Element.prototype.getBoundingClientRect = jest.fn(()=> { - return {top: 100, bottom: 200} + // Assert + expect(wrapperDiv.classes()).toContain("warning"); }); - - var mockScrollIntoView = jest.fn(); - Element.prototype.scrollIntoView = mockScrollIntoView; - // Act - const wrapper = shallowMount(alert, setupMocks({})); - - // Assert - // This is an implementation detail - we just need to test that the final step of snapping - // the window to the alert is working. If using a different function to accomplish that - // just swap this out with the new function - expect(mockScrollIntoView).toHaveBeenCalled(); - }); - - it("Should not call scrollIntoView() when the clientBoundingRect is not entirely in the viewport but 'shouldScrollToOnMount' is false", () => { - // Arrange - var viewPortHeight = 200; - setUpViewPort(viewPortHeight); - - Element.prototype.getBoundingClientRect = jest.fn(()=> { - return {top: -100, bottom: 200} + it("Should update alert Headline to manualHeadline datam entered and alert copy to manualCopy datam entered when no cmsWidgetName entered", async () => { + // Arrange + const wrapper = shallowMount(alert, setupMocks({})); + // Assert + expect(wrapper.vm.alertHeadline).toBe("testHeader"); + expect(wrapper.vm.alertCopy).toBe("testCopy"); }); - - var mockScrollIntoView = jest.fn(); - Element.prototype.scrollIntoView = mockScrollIntoView; - // Act - const wrapper = shallowMount(alert, setupMocks({ - propsData: { - shouldScrollToOnMount: false, - manualHeadline: 'testHeader', - manualCopy: 'testCopy' - }, - })); - - // Assert - // This is an implementation detail - we just need to test that the final step of snapping - // the window to the alert is working. If using a different function to accomplish that - // just swap this out with the new function - expect(mockScrollIntoView).not.toHaveBeenCalled(); - - }); - - it("Should not call scrollIntoView() when the clientBoundingRect is entirely in the viewport", () => { - // Arrange - var viewPortHeight = 500; - setUpViewPort(viewPortHeight); - - Element.prototype.getBoundingClientRect = jest.fn(()=> { - return {top: 100, bottom: 200} + it("Should container a tag if the manualCopy contains a {routerLink: testName, testLink} placeholder", () => { + // Arrange & Act + const wrapper = shallowMount( + alert, + setupMocks({ + propsData: { + manualHeadline: "testHeader", + manualCopy: "testCopy with a {routerLink: testName, testLink} inside of it", + }, + stubs: ["router-link"], + }) + ); + // Assert + expect(wrapper.find("router-link").exists()).toBe(true); }); - - var mockScrollIntoView = jest.fn(); - Element.prototype.scrollIntoView = mockScrollIntoView; - // Act - const wrapper = shallowMount(alert, setupMocks({})); + it("Should contain 'n+1'

tags if the body copy has 'n'

tags", () => { + // Arrange & Act + const wrapper = shallowMount( + alert, + setupMocks({ + propsData: { + manualHeadline: "testHeader", + manualCopy: + "

testCopy with a {routerLink: testName, testLink} inside of it

and two paragraphs

", + }, + stubs: ["router-link"], + }) + ); + // Assert + expect(wrapper.findAll("p").length === 3).toBe(true); + }); - // Assert - // This is an implementation detail - we just need to test that the final step of snapping - // the window to the alert is working. If using a different function to accomplish that - // just swap this out with the new function - expect(mockScrollIntoView).not.toHaveBeenCalled(); - - }); + it("Should call scrollIntoView() when the clientBoundingRect is not entirely in the viewport (out of view top)", () => { + // Arrange + var viewPortHeight = 200; + setUpViewPort(viewPortHeight); + Element.prototype.getBoundingClientRect = jest.fn(() => { + return { top: -100, bottom: 200 }; + }); + + var mockScrollIntoView = jest.fn(); + Element.prototype.scrollIntoView = mockScrollIntoView; + + // Act + const wrapper = shallowMount(alert, setupMocks({})); + + // Assert + // This is an implementation detail - we just need to test that the final step of snapping + // the window to the alert is working. If using a different function to accomplish that + // just swap this out with the new function + expect(mockScrollIntoView).toHaveBeenCalled(); + }); + + it("Should call scrollIntoView() when the clientBoundingRect is not entirely in the viewport (bottom is hidden behind footer)", () => { + // Arrange + var viewPortHeight = 240; + setUpViewPort(viewPortHeight); + + Element.prototype.getBoundingClientRect = jest.fn(() => { + return { top: 100, bottom: 200 }; + }); + + var mockScrollIntoView = jest.fn(); + Element.prototype.scrollIntoView = mockScrollIntoView; + + // Act + const wrapper = shallowMount(alert, setupMocks({})); + + // Assert + // This is an implementation detail - we just need to test that the final step of snapping + // the window to the alert is working. If using a different function to accomplish that + // just swap this out with the new function + expect(mockScrollIntoView).toHaveBeenCalled(); + }); + + it("Should not call scrollIntoView() when the clientBoundingRect is not entirely in the viewport but 'shouldScrollToOnMount' is false", () => { + // Arrange + var viewPortHeight = 200; + setUpViewPort(viewPortHeight); + + Element.prototype.getBoundingClientRect = jest.fn(() => { + return { top: -100, bottom: 200 }; + }); + + var mockScrollIntoView = jest.fn(); + Element.prototype.scrollIntoView = mockScrollIntoView; + + // Act + const wrapper = shallowMount( + alert, + setupMocks({ + propsData: { + shouldScrollToOnMount: false, + manualHeadline: "testHeader", + manualCopy: "testCopy", + }, + }) + ); + + // Assert + // This is an implementation detail - we just need to test that the final step of snapping + // the window to the alert is working. If using a different function to accomplish that + // just swap this out with the new function + expect(mockScrollIntoView).not.toHaveBeenCalled(); + }); + + it("Should not call scrollIntoView() when the clientBoundingRect is entirely in the viewport", () => { + // Arrange + var viewPortHeight = 500; + setUpViewPort(viewPortHeight); + + Element.prototype.getBoundingClientRect = jest.fn(() => { + return { top: 100, bottom: 200 }; + }); + + var mockScrollIntoView = jest.fn(); + Element.prototype.scrollIntoView = mockScrollIntoView; + + // Act + const wrapper = shallowMount(alert, setupMocks({})); + + // Assert + // This is an implementation detail - we just need to test that the final step of snapping + // the window to the alert is working. If using a different function to accomplish that + // just swap this out with the new function + expect(mockScrollIntoView).not.toHaveBeenCalled(); + }); }); const mockMixin = { - methods: { - getCmsContent: jest.fn(), - getFooterInfoBoxHeight: jest.fn(()=> 50), - }, - computed: { - dynamicStrings: jest.fn(()=> { - return {ROUTER_LINK: 'routerLink:'} - }) - } -} + methods: { + getCmsContent: jest.fn(), + getFooterInfoBoxHeight: jest.fn(() => 50), + }, + computed: { + dynamicStrings: jest.fn(() => { + return { ROUTER_LINK: "routerLink:" }; + }), + }, +}; function setUpViewPort(height) { - Object.defineProperty(global.window, 'innerHeight', { - writable: true, - configurable: true, - value: height, - }); + Object.defineProperty(global.window, "innerHeight", { + writable: true, + configurable: true, + value: height, + }); - Object.defineProperty(window.document.documentElement, 'clientHeight', { - writable: true, - configurable: true, - value: height - }); + Object.defineProperty(window.document.documentElement, "clientHeight", { + writable: true, + configurable: true, + value: height, + }); } function setupMocks(mountOptionsMockData = {}) { - const defaultMountOptions = { - propsData: { - manualHeadline: 'testHeader', - manualCopy: 'testCopy' - }, - mixins: [mockMixin] - }; - const baseMountOptions = getMountOptions(Object.assign(defaultMountOptions, mountOptionsMockData)); - const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions); - return allMountOptions; -} \ No newline at end of file + const defaultMountOptions = { + propsData: { + manualHeadline: "testHeader", + manualCopy: "testCopy", + }, + mixins: [mockMixin], + }; + const baseMountOptions = getMountOptions( + Object.assign(defaultMountOptions, mountOptionsMockData) + ); + const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions); + return allMountOptions; +} diff --git a/src/ux-components/alert/alert.vue b/src/ux-components/alert/alert.vue index 2b6e8cda8..ca5ea7f45 100644 --- a/src/ux-components/alert/alert.vue +++ b/src/ux-components/alert/alert.vue @@ -1,179 +1,189 @@ diff --git a/src/ux-components/text-link/text-link.spec.js b/src/ux-components/text-link/text-link.spec.js index de414fd05..8b48ad5eb 100644 --- a/src/ux-components/text-link/text-link.spec.js +++ b/src/ux-components/text-link/text-link.spec.js @@ -3,50 +3,48 @@ import textLink from "./text-link"; import { nextTick } from "vue"; describe("text-link.vue", () => { + it("Should return class navigation-link", async () => { + // Act + const wrapper = shallowMount(textLink, { + propsData: { + linkType: "navigation", + }, + }); - it("Should return class navigation-link", async () => { - // Act - const wrapper = shallowMount(textLink, { - propsData: { - linkType: "navigation", - }, + // Assert + const paragraph = wrapper.find("a"); + + // Expect + expect(paragraph.attributes("class")).toContain("navigation-link"); }); - // Assert - const paragraph = wrapper.find("a"); + it("Should return class footer", async () => { + // Act + const wrapper = shallowMount(textLink, { + propsData: { + linkType: "footer", + }, + }); - // Expect - expect(paragraph.attributes("class")).toContain("navigation-link"); - }); + // Assert + const paragraph = wrapper.find("a"); - it("Should return class footer", async () => { - // Act - const wrapper = shallowMount(textLink, { - propsData: { - linkType: "footer", - }, + // Expect + expect(paragraph.attributes("class")).toContain("footer"); }); - // Assert - const paragraph = wrapper.find("a"); + it("Should return class text-small", async () => { + // Act + const wrapper = shallowMount(textLink, { + propsData: { + linkType: "textSmall", + }, + }); - // Expect - expect(paragraph.attributes("class")).toContain("footer"); - }); + // Assert + const paragraph = wrapper.find("a"); - it("Should return class text-small", async () => { - // Act - const wrapper = shallowMount(textLink, { - propsData: { - linkType: "textSmall", - }, + // Expect + expect(paragraph.attributes("class")).toContain("small"); }); - - // Assert - const paragraph = wrapper.find("a"); - - // Expect - expect(paragraph.attributes("class")).toContain("small"); - }); - }); diff --git a/src/ux-components/text-link/text-link.vue b/src/ux-components/text-link/text-link.vue index d338b9d44..57f061971 100644 --- a/src/ux-components/text-link/text-link.vue +++ b/src/ux-components/text-link/text-link.vue @@ -1,62 +1,78 @@ From 341bb7a7e44330788d38cf1265493c325b4cb05c Mon Sep 17 00:00:00 2001 From: Katie Date: Thu, 3 Nov 2022 08:35:25 -0400 Subject: [PATCH 22/40] CSR-917 Don't allow enter key for multiselects --- .../base-input-button/base-input-button.spec.js | 13 ++++++------- .../base-input-button/base-input-button.vue | 1 - 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/common-components/base-input-button/base-input-button.spec.js b/src/common-components/base-input-button/base-input-button.spec.js index 87e8af6d2..8a3ff6356 100644 --- a/src/common-components/base-input-button/base-input-button.spec.js +++ b/src/common-components/base-input-button/base-input-button.spec.js @@ -228,7 +228,7 @@ describe("baseInputButton.vue", () => { expect(wrapper.emitted()).not.toHaveProperty("update:modelValue"); }); - test("focus and click enter on a checkbox => update:modelValue is emitted with correct value", async () => { + test("focus and click enter on a checkbox => nothing should happen", async () => { // Arrange const { wrapper } = setupMocks({ mockData: { @@ -243,9 +243,10 @@ describe("baseInputButton.vue", () => { // Act await input.trigger("keypress", { key: "enter" }); + console.log(wrapper.emitted()["update:modelValue"]); // Assert - expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual(["Hi"]); + expect(wrapper.emitted()["update:modelValue"]).toBe(undefined); }); }); @@ -319,7 +320,7 @@ describe("baseInputButton.vue", () => { expect(wrapper.vm.handlePushClickEventToGACheck).not.toHaveBeenCalled(); }); - test("eventType === eventTypes.ENTER => call handleClick and handlePushClickEventToGACheck", () => { + test("eventType === eventTypes.ENTER => do nothing", () => { // Arrange const { wrapper } = setupMocks({ mockData: { @@ -336,11 +337,9 @@ describe("baseInputButton.vue", () => { wrapper.vm.handleEventAction("enter", { myEvent: "test" }); // Assert - expect(wrapper.vm.handleClick).toHaveBeenCalledWith({ - myEvent: "test", - }); - expect(wrapper.vm.handlePushClickEventToGACheck).toHaveBeenCalledWith("click"); + expect(wrapper.vm.handleClick).not.toHaveBeenCalled(); expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled(); + expect(wrapper.vm.handlePushClickEventToGACheck).not.toHaveBeenCalled(); }); test("eventType === eventTypes.CHANGE => call handleClick and handlePushClickEventToGACheck", () => { diff --git a/src/common-components/base-input-button/base-input-button.vue b/src/common-components/base-input-button/base-input-button.vue index b71f4a63c..006323a18 100644 --- a/src/common-components/base-input-button/base-input-button.vue +++ b/src/common-components/base-input-button/base-input-button.vue @@ -53,7 +53,6 @@ export default { handleEventAction(eventType, e) { if (this.isMultiSelect) { switch (eventType) { - case this.eventTypes.ENTER: case this.eventTypes.CHANGE: this.handleClick(e); this.handlePushClickEventToGACheck(this.eventTypes.CLICK); From cd192f0ad23424bf51f64c4ffe12c709bbc82841 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 3 Nov 2022 08:45:33 -0400 Subject: [PATCH 23/40] CSR-731 | Merge issues --- src/store/index.js | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index 622c97d38..34ec8a8a9 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -405,49 +405,6 @@ export const getters = { applicationUser: (state) => state.applicationUser, order: (state) => state.order, payment: (state) => state.order.payment, - experimentOrder: (state) => { - return { - funnelVehicleYear: state.order.vehicle.year, - funnelVehicleMake: state.order.vehicle.make, - funnelVehicleModel: state.order.vehicle.model, - funnelVehicleStyle: state.order.vehicle.style, - funnelIsRepair: state.order.damage.isRepair, - funnelNumberOfChips: state.order.damage.numberOfChips, - funnelCarId: state.order.vehicle.carId, - funnelServiceCity: state.order.serviceLocation.city, - funnelServiceState: state.order.serviceLocation.state, - funnelServiceZipCode: state.order.serviceLocation.zipCode, - funnelParentAccountNumber: state.order.accountNumber, - funnelIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified, - funnelHasRecalibrationPart: getHasRecalibrationPart(state), - funnelSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1, - funnelSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.damage.glassToReplace, - "glassLocation" - ).includes(damageLocationsSelected.WINDSHIELD), - funnelSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.damage.glassToReplace, - "glassLocation" - ).includes(damageLocationsSelected.REAR), - funnelSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.damage.glassToReplace, - "glassLocation" - ).includes(damageLocationsSelected.DRIVER), - funnelSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.damage.glassToReplace, - "glassLocation" - ).includes(damageLocationsSelected.PASSENGER), - 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]; - }, - applicationUser: (state) => state.applicationUser, - order: (state) => state.order, - payment: (state) => state.order.payment, experimentOrder: (state) => { return { funnelVehicleYear: state.order.vehicle.year, From 19dc1e993744051bcc2f0a3509ecdbe3893376bf Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 3 Nov 2022 08:50:28 -0400 Subject: [PATCH 24/40] CSR-731 | Update glassLocation variable name after dev merge --- .../quote/service-package-question/service-package-question.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 395d7867e..d92a9bb2e 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -232,7 +232,7 @@ export default { }, glassToReplaceContainsGlassLocation(glassLocation) { const glassLocationMatches = this.$store.getters.order.damage.glassToReplace.filter( - (glassToReplace) => glassToReplace.location === glassLocation + (glassToReplace) => glassToReplace.glassLocation === glassLocation ); return !!glassLocationMatches.length; }, From 409770d92a39bc6577c40d95cef89585842324dc Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Thu, 3 Nov 2022 08:58:57 -0400 Subject: [PATCH 25/40] CSR-731 | Fixing unit test --- .../list-button-horizontal/list-button-horizontal.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js b/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js index 09a63b349..52ce67e22 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js @@ -52,12 +52,12 @@ describe("list-button-horizontal.vue", () => { expect(input.attributes()["aria-required"]).toEqual("true"); }); - it("is cash or insurance button => has 'radio-fancy' class", () => { + it("is cash or insurance button => has 'strong' class", () => { // Arrange/Act const { wrapper } = setupMocks({ mockData: { propsData: { - isCashOrInsurance: true, + additionalButtonStyling: 'listButtonHorizontalStrong', }, }, }); @@ -65,7 +65,7 @@ describe("list-button-horizontal.vue", () => { // Assert const label = wrapper.find("label"); - expect(label.classes()).toContain("radio-fancy"); + expect(label.classes()).toContain("strong"); }); test("has buttonLabel => displays buttonLabel", () => { From af80ae6f22a35cd6de29402f8a1b0c0189e1df84 Mon Sep 17 00:00:00 2001 From: Katie Date: Thu, 3 Nov 2022 09:33:37 -0400 Subject: [PATCH 26/40] CSR-918 Edit hover styling --- src/App.vue | 1 + src/router/index.js | 5 +++++ src/styles/common-error-styles.scss | 13 ++++++++----- src/styles/mixins/customMixins.scss | 4 ++++ src/styles/shared-input-button-styles.scss | 7 +++++++ .../list-button-horizontal.vue | 14 +------------- src/ux-components/list-button/list-button.vue | 12 +----------- src/ux-components/list-card/list-card.vue | 13 +------------ src/ux-components/radio/radio.vue | 2 +- 9 files changed, 29 insertions(+), 42 deletions(-) create mode 100644 src/styles/shared-input-button-styles.scss diff --git a/src/App.vue b/src/App.vue index 75e80355f..aef77fcb0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -23,4 +23,5 @@ export default { @import "@/styles/common-typography-styles.scss"; @import "@/styles/common-error-styles.scss"; @import "@/styles/common-animations.scss"; +@import "@/styles/shared-input-button-styles.scss"; diff --git a/src/router/index.js b/src/router/index.js index 6350edf5d..02e0def4c 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -289,6 +289,11 @@ function navigateToUrl(url, optionalQuery = {}) { externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]); } + externalUrl.searchParams.append( + "experiments", + "ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_TEST=true" + ); + window.location.assign(externalUrl); } diff --git a/src/styles/common-error-styles.scss b/src/styles/common-error-styles.scss index d497ae6ce..9befb1eb0 100644 --- a/src/styles/common-error-styles.scss +++ b/src/styles/common-error-styles.scss @@ -37,11 +37,14 @@ html { } &.list-button-horizontal { color: $red; - label { - border: 1px solid $red; - &:hover { - box-shadow: 0px 0px 0px 4px $red-200; - } + // label { + // border: 1px solid $red; + // &:hover { + // @include box-shadow-lg-hover($red-200); + // } + // } + &:hover { + @include box-shadow-lg-hover($red-200); } input[type="checkbox"]:focus + label, input[type="radio"]:focus + label { diff --git a/src/styles/mixins/customMixins.scss b/src/styles/mixins/customMixins.scss index 8620968f0..e9954b2a2 100644 --- a/src/styles/mixins/customMixins.scss +++ b/src/styles/mixins/customMixins.scss @@ -4,3 +4,7 @@ @mixin blue-gradient { background: linear-gradient(270deg, $blue 0%, $blue-800 100%); } + +@mixin box-shadow-lg-hover($color) { + box-shadow: 0 0 0 4px $color; +} diff --git a/src/styles/shared-input-button-styles.scss b/src/styles/shared-input-button-styles.scss new file mode 100644 index 000000000..e64faee71 --- /dev/null +++ b/src/styles/shared-input-button-styles.scss @@ -0,0 +1,7 @@ +.base-input-button { + &:hover { + cursor: pointer; + @include box-shadow-lg-hover($blue-300); + border: 1px solid transparent; + } +} diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue index 1b4232c4c..4537ce5bb 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.vue +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.vue @@ -2,7 +2,7 @@ @@ -86,18 +86,6 @@ export default { width: 100%; color: $gray-600; - &:hover { - @include media-breakpoint-up(sm) { - box-shadow: 0 0 0 4px $blue-300; - cursor: pointer; - z-index: 4 !important; - } - } - - + p { - display: none; - } - span { font-size: 0.875rem; } diff --git a/src/ux-components/list-button/list-button.vue b/src/ux-components/list-button/list-button.vue index 4f7d34ae9..031805509 100644 --- a/src/ux-components/list-button/list-button.vue +++ b/src/ux-components/list-button/list-button.vue @@ -1,7 +1,7 @@