Merge pull request #380 from Safelite/refactor/linting5

Baseline linting of helpers & mixins
This commit is contained in:
DavidAtSafelite 2023-07-26 12:17:18 -04:00 committed by GitHub
commit 5fc43ab548
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 577 additions and 717 deletions

View file

@ -23,6 +23,7 @@ module.exports = {
'comma-dangle': ['error', 'never'],
indent: ['error', 4],
'max-len': ['error', { code: 140 }],
'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
'vue/html-indent': 'off',
'vue/html-closing-bracket-newline': ['error', {
singleline: 'never',

View file

@ -1,5 +1,5 @@
const applicationConfig = {
CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod"
CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod"
CONSUMER_CF_DISTRO: process.env.VUE_APP_CONSUMER_CF_DISTRO,
ANALYTICS_SESSION_TIMEOUT_MINUTES: 30,
SAVED_SESSION_TIMEOUT_DAYS: 45,
@ -8,7 +8,7 @@ const applicationConfig = {
SITE_ENTRY_TRIGGER_VALUE: 'SelfService',
APPLICATION_ABBREVIATION: 'iss',
PAGE_QUERYSTRING: 'issPage',
CLIENTTAG_QUERYSTRING: 'CientTag',
CLIENTTAG_QUERYSTRING: 'ClientTag',
GOOGLE_PLACES_API_KEY: process.env.VUE_APP_GOOGLE_PLACES_API_KEY,
ISS_DEV_CMS_DOMAIN: 'https://digitalisscms.dev.safelite.io',
CASH_PARENT_ACCOUNT_NUMBER: 167132

View file

@ -81,7 +81,7 @@ import listButtonHorizontal from '@/ux-components/list-button-horizontal/list-bu
import listCard from '@/ux-components/list-card/list-card';
import radio from '@/ux-components/radio/radio';
import { ErrorMessage } from 'vee-validate';
import providerPrefRadio from '@/layouts/provider-preference/provider-pref-radio/provider-pref-radio.vue';
import providerPrefRadio from '@/layouts/provider-preference/provider-pref-radio/provider-pref-radio';
export default {
name: 'button-question',

View file

@ -7,7 +7,7 @@ const mockMixin = {
methods: {
getCmsContent: jest.fn().mockImplementation(() => questionText)
}
}
};
// TODO: Remove the following from dropdown-question.vue -> :class="(errors && errors.length) || hasError ? 'has-error' : ''"
// It is not being used.

View file

@ -58,6 +58,7 @@ export default {
hasError: Boolean,
placeHolderText: String
},
emits: ['update:modelValue'],
setup(props) {
const propsClone = { ...props };
const { modelValue } = propsClone;

View file

@ -50,7 +50,7 @@ export default {
// NOTE: needs to have async/await here; tested and won't work without it
await useValidateForm();
this.questionData.map((q, i) => {
this.questionData.map((q) => {
const question = {
questionText: q.questionText,
questionSequence: q.questionSequence,
@ -78,6 +78,7 @@ export default {
if (!q.suppressThisQuestion) {
this.questions.push(question);
}
return q;
});
if (!this.modelValue?.length > 0 && this.questions.length > 0) {

View file

@ -1,44 +1,9 @@
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],
});
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']));
});
});
///////////////
// Constants //
///////////////
const mockCmsContent = {
Text: 'Sample text here.'
};
const mockMixin = {
methods: {
@ -52,6 +17,37 @@ const mockProps = {
justifyText: 'mockJustifyText'
};
const mockCmsContent = {
Text: 'Sample text here.'
};
describe('modal.vue', () => {
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));
});
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));
});
});

View file

@ -1,5 +1,7 @@
<template>
<div class="textbox-question" :class="(errors && errors.length) || hasError ? 'has-error' : ''">
<div
class="textbox-question"
:class="(errors && errors.length) || hasError ? 'has-error' : ''">
<label
v-if="displayQuestionText"
:for="inputId"
@ -43,7 +45,10 @@
@focus="$emit('focus', $event.target.value)"
@paste="trimOnPaste"
@drop="trimOnPaste" />
<button v-if="includeSearchIcon" type="submit" aria-label="Search button" />
<button
v-if="includeSearchIcon"
type="submit"
aria-label="Search button" />
<button
v-if="includeSelectIcon"
type="submit"
@ -51,8 +56,12 @@
:data-bs-target="'#' + cmsWidgetName"
aria-label="Select button" />
</div>
<div v-show="errorMessage" class="row my-1 form-test-error">
<span class="d-inline-flex mt-0" role="alert">{{ errorMessage }}</span>
<div
v-show="errorMessage"
class="row my-1 form-test-error">
<span
class="d-inline-flex mt-0"
role="alert">{{ errorMessage }}</span>
</div>
</div>
</template>
@ -107,12 +116,12 @@ export default {
let initialValue;
switch (typeof modelValue) {
case 'number':
initialValue = modelValue;
break;
default:
initialValue = modelValue && modelValue.length > 0 ? modelValue : '';
break;
case 'number':
initialValue = modelValue;
break;
default:
initialValue = modelValue && modelValue.length > 0 ? modelValue : '';
break;
}
const fieldOptions = {
@ -122,11 +131,9 @@ export default {
};
// eslint-disable-next-line no-shadow
const { errorMessage, handleBlur, handleChange, meta, validate, errors } = useField(
props.inputId,
const { errorMessage, handleBlur, handleChange, meta, validate, errors } = useField(props.inputId,
props.validationRules,
fieldOptions
);
fieldOptions);
return {
errorMessage,

View file

@ -8,6 +8,12 @@
let lastFocusedInputGroupName = '';
let onButtonQuestionLostFocusCallback = null;
const invokeButtonQuestionLostFocusCallback = () => {
if (onButtonQuestionLostFocusCallback) {
onButtonQuestionLostFocusCallback();
}
};
const handleAnyComponentFocus = (e) => {
const targetType = e.target.type;
if (targetType !== 'radio' && targetType !== 'checkbox') {
@ -28,10 +34,4 @@ const handleInputComponentBlur = (e) => {
}
};
const invokeButtonQuestionLostFocusCallback = () => {
if (onButtonQuestionLostFocusCallback) {
onButtonQuestionLostFocusCallback();
}
};
export { handleAnyComponentFocus, handleButtonComponentFocus, handleInputComponentBlur };

View file

@ -1,13 +1,12 @@
import { useMainStore } from '@/store';
export function validateISSClientTag(clientTag) {
const store = useMainStore()
const store = useMainStore();
return store.validateClientTag(clientTag)
.then((response) =>
// Success
response
,
response,
(error) =>
// Error
null);

View file

@ -3,34 +3,31 @@ import { useMainStore } from '@/store';
export function fetchCmsContentForPage(issPage) {
const store = useMainStore();
const clientName = store.issConfig.clientName;
const accountNumber = store.issConfig.accountNumber;
const { clientName } = store.issConfig;
const { accountNumber } = store.issConfig;
const clientOverride = (clientName.length > 0 && accountNumber > 0);
return store.getPageData(issPage)
.then(
// Get the base/default page first.
(baseResponse) => {
if (!clientOverride) {
// Return the base page if there are no client override.
return processPageData(baseResponse, null);
} else {
// Else get the client override page.
const pageName = issPage + '_' + clientName.toLowerCase().replace(/ /g, '');
// Get the base/default page first.
.then((baseResponse) => {
if (!clientOverride) {
// Return the base page if there are no client override.
return processPageData(baseResponse, null);
}
// Else get the client override page.
const pageName = `${issPage}_${clientName.toLowerCase().replace(/ /g, '')}`;
return store.getPageData(pageName)
.then((clientResponse) => {
// Process the client override if it exists.
return processPageData(baseResponse, clientResponse);
},
(error) => {
console.error(error);
// Process the just the base if no client override exists.
return processPageData(baseResponse, null);
});
}
});
};
return store.getPageData(pageName)
.then((clientResponse) =>
// Process the client override if it exists.
processPageData(baseResponse, clientResponse),
(error) => {
console.error(error);
// Process the just the base if no client override exists.
return processPageData(baseResponse, null);
});
});
}
// Support method for processing the page data from the CMS call.
// widgets = current widget collection used by page.
@ -41,8 +38,8 @@ function processPageData(baseResponse, clientResponse) {
let widgets = [];
if (!baseResponse?.data?.Result) {
console.error('No result data found'); // Something has gone terribly wrong.
return {}
console.error('No result data found'); // Something has gone terribly wrong.
return {};
}
if (!clientResponse?.data?.Result) {
widgets = baseResponse.data.Result;
@ -111,7 +108,7 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) {
};
Object.keys(widgetModel).forEach((key) => {
let modelWithReplacements = processWidgetItemForReplacement(widgetModel,
const modelWithReplacements = processWidgetItemForReplacement(widgetModel,
key);
objWithReplacements.Model[key] = modelWithReplacements;
@ -144,8 +141,8 @@ function processWidgetItemForReplacement(widgetModel, key) {
}
// If we have an object. array, etc
if (typeof widgetModel[key] === 'object' &&
Object.keys(widgetModel[key]).length) {
if (typeof widgetModel[key] === 'object'
&& Object.keys(widgetModel[key]).length) {
Object.keys(widgetModel[key]).forEach((item) => {
processWidgetItemForReplacement(widgetModel[key], item);
});
@ -158,14 +155,14 @@ function processWidgetItemForReplacement(widgetModel, key) {
}
function mapStringToModal(str) {
const startIndex = str.indexOf('{' + dynamicStrings.MODAL_LINK);
const startIndex = str.indexOf(`{${dynamicStrings.MODAL_LINK}`);
let linkToReplace = str.substring(startIndex, str.length);
linkToReplace = linkToReplace.substring(0, linkToReplace.indexOf('}') + 1);
const params = linkToReplace.substring((dynamicStrings.MODAL_LINK).length + 2, linkToReplace.length - 1);
const splitParams = params.split(',');
const bodyText = '<a modalTarget="' + splitParams[0] + '" class="modal-text" aria-label="Modal window">' + splitParams[1] + '</a>';
const bodyText = `<a modalTarget="${splitParams[0]}" class="modal-text" aria-label="Modal window">${splitParams[1]}</a>`;
let returnVal = str.replace(linkToReplace, bodyText);
@ -176,14 +173,14 @@ function mapStringToModal(str) {
}
function mapStringToLink(str) {
const startIndex = str.indexOf('{' + dynamicStrings.EXTERNAL_LINK);
const startIndex = str.indexOf(`{${dynamicStrings.EXTERNAL_LINK}`);
let linkToReplace = str.substring(startIndex, str.length);
linkToReplace = linkToReplace.substring(0, linkToReplace.indexOf('}') + 1);
const params = linkToReplace.substring((dynamicStrings.EXTERNAL_LINK).length + 2, linkToReplace.length - 1);
const splitParams = params.split(',');
const bodyText = '<a href="' + splitParams[0] + '" class="external-text" target="_blank">' + splitParams[1] + '</a>';
const bodyText = `<a href="${splitParams[0]}" class="external-text" target="_blank">${splitParams[1]}</a>`;
let returnVal = str.replace(linkToReplace, bodyText);
@ -196,21 +193,19 @@ function mapStringToLink(str) {
// 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 = /{([^{}]*?):([^{}]*?)}/g;
const regexMatches = [...str.matchAll(regexExp)];
const globalStateMatches = regexMatches.filter(match => {
return match[1] === dynamicStrings.GLOBAL_STATE;
});
const globalStateMatches = regexMatches.filter((match) => match[1] === dynamicStrings.GLOBAL_STATE);
// Our final string value that will be built from the matches.
let stringBuilder = '';
const stringBuilder = '';
for (const match of globalStateMatches) {
// Reset store state for each match.
const valueFromStore = getStoreValueFromString(match[2]);
const valueFromStore = getStoreValueFromString(match[2]);
if (!valueFromStore) {
console.warning('Unable to resolve global state data.');
return '' // if we can't map our string to state data, return an empty string.
return ''; // if we can't map our string to state data, return an empty string.
}
const stringWithReplacement = str.replace(match[0], valueFromStore);
@ -231,7 +226,7 @@ function getStoreValueFromString(str) {
let storeOrStateObject = useMainStore();
for (const s of str.split('.')) {
if (s === 'getters') continue; //For backward compatibility
if (s === 'getters') continue; // For backward compatibility
if (storeOrStateObject[s] != undefined) {
storeOrStateObject = storeOrStateObject[s];
} else {
@ -241,9 +236,9 @@ 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
@ -253,23 +248,22 @@ function getStoreValueFromString(str) {
* @returns The processed string
*/
export function processIfStatements(str, ifConditionKeyword, replacePlaceholderCallback) {
const containsRelevantIfStatement = new RegExp('{if:' + ifConditionKeyword + ':.+?}', 'g').test(str);
const containsRelevantIfStatement = new RegExp(`{if:${ifConditionKeyword}:.+?}`, 'g').test(str);
const hasEmbeddedCrLf = /\r?\n|\r/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 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) {
@ -342,20 +336,18 @@ function executeIfStatementAndSetProcessedStrings(ifStatementArray, replacePlace
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 {
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];
}
entry.groups.processedString = entry.isFlaggedForProcessing
? entry.groups.endTrailingString
: entry[0];
}
}
@ -364,43 +356,43 @@ function getIfStatementRegexExpression() {
// {if:...} or {else} or {end}
const anyLogicOperatorNonCapture = '(?:{(?:end|else|if:.*?)})';
// NOTE: ?<variableName> syntax stores the captured match like so: match.groups.variableName
const matchStartOfString =
'(?<processedString>^.+?)' + // Match and Capture all characters (lazy), cannot be empty
'(?=(?:{if))'; // Looks ahead but does not capture {if
const matchIfOperator =
'(?<isIfStatement>{if:)' + // Match & Capture {if:
'(?<ifConditionType>.*?):' + // Match all chars up to and including next ':' - Capture all chars up to ':'
'(?<ifCondition>.*?)}' + // Match all chars up to and including next '}' - Capture all chars up to '}'
'(?<ifTrailingString>.*?)' + // Match and Capture all characters (lazy), can be empty
'(?=' +
anyLogicOperatorNonCapture +
')'; // Looks ahead but does not capture the next logic operator
const matchElseOperator =
'(?<isElseStatement>{else})' + // Match & Capture {else}
'(?<elseTrailingString>.*?)' + // Match & Capture all characters (lazy), can be empty
'(?=' +
anyLogicOperatorNonCapture +
')'; // Looks ahead but does not capture the next logic operator
const matchEndOperator =
'(?<isEndStatement>{end})' + // Match & Capture {end}
'(?<endTrailingString>.*?)' + // Match & Capture all chracters (lazy), can be empty
'(?=' +
anyLogicOperatorNonCapture +
'|$)'; // Looks ahead but does not capture the next logic operator
const matchStartOfString
= '(?<processedString>^.+?)' // Match and Capture all characters (lazy), cannot be empty
+ '(?=(?:{if))'; // Looks ahead but does not capture {if
const matchIfOperator
= '(?<isIfStatement>{if:)' // Match & Capture {if:
+ '(?<ifConditionType>.*?):' // Match all chars up to and including next ':' - Capture all chars up to ':'
+ '(?<ifCondition>.*?)}' // Match all chars up to and including next '}' - Capture all chars up to '}'
+ '(?<ifTrailingString>.*?)' // Match and Capture all characters (lazy), can be empty
+ `(?=${
anyLogicOperatorNonCapture
})`; // Looks ahead but does not capture the next logic operator
const matchElseOperator
= '(?<isElseStatement>{else})' // Match & Capture {else}
+ '(?<elseTrailingString>.*?)' // Match & Capture all characters (lazy), can be empty
+ `(?=${
anyLogicOperatorNonCapture
})`; // Looks ahead but does not capture the next logic operator
const matchEndOperator
= '(?<isEndStatement>{end})' // Match & Capture {end}
+ '(?<endTrailingString>.*?)' // Match & Capture all characters (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,
return new RegExp(`${matchStartOfString
}|${
matchIfOperator
}|${
matchElseOperator
}|${
matchEndOperator}`,
'g');
}
//////////////////////////////////////////
/// ///////////////////////////////////////
// End of If Statement Processing Logic //
//////////////////////////////////////////
/// ///////////////////////////////////////
export function doesCopyContainTextLink(copy) {
return copy.includes(dynamicStrings.TEXT_LINK);
@ -409,12 +401,12 @@ export function doesCopyContainTextLink(copy) {
export function setupModalLinks(context) {
context.$nextTick(() => {
const elements = document.getElementsByClassName('modal-text');
for (let element of elements){
for (const element of elements) {
const target = element.getAttribute('modalTarget');
if (target) {
element.addEventListener('click', () => context.$refs[target].openModal() );
element.addEventListener('click', () => context.$refs[target].openModal());
}
};
}
});
}

View file

@ -106,11 +106,9 @@ export function updateSessionIdCookie() {
createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), { maxAge: 60 * 30 });
}
export function setCookieProperties(
properties,
{ useDefaultISSCookieAttributes = true, maxAge, isSecure }
) {
if (typeof properties == 'object') {
export function setCookieProperties(properties,
{ useDefaultISSCookieAttributes = true, maxAge, isSecure }) {
if (typeof properties === 'object') {
Object.keys(properties).forEach((key) => {
createOrUpdateCookie(key, properties[key], {
useDefaultISSCookieAttributes,
@ -132,8 +130,8 @@ export function setCookieProperties(
Takes an object with properties to set. Will overwrite existing properties.
*/
function setISSCookieProperties(properties) {
if (typeof properties == 'object') {
let cookie = getISSCookie();
if (typeof properties === 'object') {
const cookie = getISSCookie();
if (cookie !== null) {
Object.keys(properties).forEach((key) => {
@ -150,11 +148,8 @@ function setISSCookieProperties(properties) {
Used to create a cookie.
`useDefaultISSCookieAttributes` will set the path and domain to our defaults
*/
function createOrUpdateCookie(
key,
value = '',
{ useDefaultISSCookieAttributes = true, maxAge, isSecure = true }
) {
function createOrUpdateCookie(key, value = '',
{ useDefaultISSCookieAttributes = true, maxAge, isSecure = true }) {
let cookieToAdd = `${key}=${value}; `;
if (useDefaultISSCookieAttributes) {
@ -163,7 +158,7 @@ function createOrUpdateCookie(
if (isSecure && !isLocalhost()) {
cookieToAdd += 'secure; ';
}
if (!isNaN(maxAge)) {
if (!Number.isNaN(maxAge)) {
cookieToAdd += `max-age=${maxAge};`;
}
@ -174,7 +169,7 @@ function createOrUpdateCookie(
Gets current domain without the subdomain for cookie.
*/
function getDomainWithoutSubdomain() {
let url = location.hostname;
const url = location.hostname;
if (isLocalhost()) {
return 'localhost';
}
@ -191,8 +186,8 @@ function getDomainWithoutSubdomain() {
Gets cookie value by name, returns empty string if not found.
*/
function getCookieValueByName(name) {
const value = '; ' + document.cookie;
const parts = value.split('; ' + name + '=');
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) {
return parts.pop().split(';').shift();

View file

@ -1,16 +1,5 @@
import { randomUUID } from 'crypto';
export function getRandomString(minLength = 1, maxLength = 100) {
const length = getRandomInt(minLength, maxLength + 1);
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
export function getRandomInt(min = 0, max = 1000) {
min = Math.ceil(min);
max = Math.floor(max);
@ -26,3 +15,14 @@ export function getRandomBoolean() {
const index = getRandomInt(0, 2);
return bools[index];
}
export function getRandomString(minLength = 1, maxLength = 100) {
const length = getRandomInt(minLength, maxLength + 1);
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}

View file

@ -4,9 +4,9 @@ export default {
// Adds event to the bus given its category, subcategory, and eventValue;
addEventToBus(category, subCategory, eventValue) {
useMainStore().addEventToBus({
category: category,
subCategory: subCategory,
eventValue: eventValue,
category,
subCategory,
eventValue
});
},
@ -16,8 +16,8 @@ export default {
if (event) {
useMainStore().removeEventFromBus({
category: category,
subCategory: subCategory,
category,
subCategory
});
}

View file

@ -11,7 +11,7 @@ useMainStore().removeEventFromBus = jest.fn();
useMainStore().eventBusItem = jest.fn();
describe('event-bus.js', () => {
let event = {
const event = {
isDismissible: true,
messageCopy: 'You can get a quote by starting on this page.',
messageHeadline: "We're sorry, something went wrong.",
@ -20,15 +20,13 @@ describe('event-bus.js', () => {
afterEach(() => {
jest.resetAllMocks();
})
});
it('removes items when readandpop is called', () => {
useMainStore().eventBusItem.mockReturnValueOnce(event);
const eventValue = eventBus.readAndPopEventFromBus(
globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND
);
const eventValue = eventBus.readAndPopEventFromBus(globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND);
expect(useMainStore().eventBusItem).toBeCalledTimes(1);
expect(useMainStore().removeEventFromBus).toBeCalledTimes(1);
@ -37,10 +35,8 @@ describe('event-bus.js', () => {
it("doesn't try to remove items when readandpop is called and item doesn't exist", () => {
useMainStore().eventBusItem.mockReturnValueOnce(undefined);
const eventValue = eventBus.readAndPopEventFromBus(
globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND
);
const eventValue = eventBus.readAndPopEventFromBus(globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND);
expect(useMainStore().eventBusItem).toBeCalledTimes(1);
expect(useMainStore().removeEventFromBus).toBeCalledTimes(0);
@ -49,21 +45,17 @@ describe('event-bus.js', () => {
it('returns event from bus', () => {
useMainStore().eventBusItem.mockReturnValueOnce(event);
const eventValue = eventBus.readEventFromBus(
globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND
);
const eventValue = eventBus.readEventFromBus(globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND);
expect(eventValue).toBe(event);
});
it('Reads event from bus, should have event value.', () => {
// Arrange / Act
eventBus.addEventToBus(
globalEvents.Categories.GLOBAL_ALERT,
eventBus.addEventToBus(globalEvents.Categories.GLOBAL_ALERT,
globalEvents.SubCategories.PAGE_NOT_FOUND,
event
);
event);
expect(useMainStore().addEventToBus).toHaveBeenCalled();
});

View file

@ -2,11 +2,7 @@ export function settleAllPromises(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) => {
return Promise.allSettled(promiseNames.map((e) => e[1]).map((n) => n.promise)).then((results) => {
const resultMap = {};
// Build a map of the results

View file

@ -1,20 +1,16 @@
import { useMainStore } from '@/store';
export async function getServiceabilityDetails(serviceZipCode, lineItems) {
const serviceabilityDetails = await useMainStore().getServiceabilityDetails(
{
serviceZipCode: serviceZipCode,
lineItems: lineItems,
},
false
);
const serviceabilityDetails = await useMainStore().getServiceabilityDetails({
serviceZipCode,
lineItems
},
false);
return Promise.resolve(serviceabilityDetails);
}
export async function getZipCodeData(zipCode) {
const serviceZipValidationResponse = await useMainStore().validateZip(
{ zip: zipCode }
);
const serviceZipValidationResponse = await useMainStore().validateZip({ zip: zipCode });
return {
containsMilitaryBase: serviceZipValidationResponse.data.containsMilitaryBase,

View file

@ -10,8 +10,7 @@ export function isAnalyticsSessionStillActive() {
const lastTouchedValue = getISSCookie().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;
@ -19,6 +18,7 @@ export function isAnalyticsSessionStillActive() {
return true;
}
return false;
}
/*
@ -35,6 +35,7 @@ export function isSavedSessionStillActive() {
return !isSavedSessionTimedOut;
}
return false;
}
/*

View file

@ -9,15 +9,7 @@ import {
getCookieDomainValue,
setCookieProperties
} from '@/helpers/cookie-helper';
import {
analyticsPageEvents,
GaCategories,
GaActions,
GaLabels,
GaEvents,
ValueToLogTypes
} from '@/constants/analytics';
import { routerParams } from '@/router/router-constants/router-params';
import { GaActions } from '@/constants/analytics';
import { queryStrings } from '@/constants/query-strings';
import { useMainStore } from '@/store';
import { mapStores } from 'pinia';
@ -53,7 +45,7 @@ export function getMountOptions(mockData) {
mocks.prependActionToMethod = jest.fn();
const global = {
mocks: mocks,
mocks,
mixins: [mockMixin],
plugins: [pinia],
stubs: {
@ -78,10 +70,10 @@ export const cookies = {
export function setupCookies({ ISSCookieValue = '', includeHeritageCookie = true }) {
Object.keys(cookies).forEach((key) => {
const cookieValue =
key == cookieNames.ISS_SESSION_INFO ? ISSCookieValue : cookies[key];
if (includeHeritageCookie || key != cookieNames.ISS_SESSION_INFO)
const cookieValue = key === cookieNames.ISS_SESSION_INFO ? ISSCookieValue : cookies[key];
if (includeHeritageCookie || key !== cookieNames.ISS_SESSION_INFO) {
setCookieProperties({ [key]: cookieValue }, { isSecure: false });
}
});
}

View file

@ -20,5 +20,5 @@ export function regex(expression, errorMessage) {
}
return true;
}
};
}

View file

@ -161,18 +161,19 @@ describe('address-questions.vue', () => {
autocompleteElement.addEventListener = jest
.fn()
.mockImplementation((eventName, callbackFunction) => {
if (eventName == 'change') {
if (eventName === 'change') {
changeEventCallbackFunction = callbackFunction;
}
});
const { wrapper } = setupMocks({
querySelectorFunction(query) {
if (query == '.pac-container .pac-item') {
if (query === '.pac-container .pac-item') {
const element = document.createElement('div');
element.textContent = '123 Test Street';
return element;
}
return null;
},
geocoderResult: {
address_components: [
@ -276,7 +277,7 @@ describe('address-questions.vue', () => {
autocompleteElement.addEventListener = jest
.fn()
.mockImplementation((eventName, callbackFunction) => {
if (eventName == 'change') {
if (eventName === 'change') {
changeEventCallbackFunction = callbackFunction;
}
});
@ -306,18 +307,19 @@ describe('address-questions.vue', () => {
autocompleteElement.addEventListener = jest
.fn()
.mockImplementation((eventName, callbackFunction) => {
if (eventName == 'change') {
if (eventName === 'change') {
changeEventCallbackFunction = callbackFunction;
}
});
const { wrapper } = setupMocks({
querySelectorFunction(query) {
if (query == '.pac-container .pac-item') {
if (query === '.pac-container .pac-item') {
const element = document.createElement('div');
element.textContent = '123 Test Street';
return element;
}
return null;
}
});
@ -474,7 +476,6 @@ function setupMocks({
const resultingMountOptions = getMountOptions({
...mountOptions,
router: {
navigate: jest.fn(),
navigate: jest.fn()
},
loadScript: jest.fn().mockResolvedValue()
@ -522,7 +523,7 @@ function setupMocks({
: mount(addressQuestions, resultingMountOptions);
document.querySelector = jest.fn().mockImplementation((query) => {
let result = null;
if (query == '.pac-container') result = document.createElement('div');
if (query === '.pac-container') result = document.createElement('div');
else if (querySelectorFunction) {
result = querySelectorFunction(query);
}

View file

@ -1,40 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import ButtonQuestionModal from './button-question-modal';
describe('button-question-modal.vue', () => {
it('Should display header text when QuestionText is defined in the CMS', async () => {
// Act
const wrapper = shallowMount(ButtonQuestionModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: 'test'
},
attachTo: document.body
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['QuestionText']));
});
it('Should display subheader text when ButtonText is defined in the CMS', async () => {
// Act
const wrapper = shallowMount(ButtonQuestionModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: 'test'
},
attachTo: document.body
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['ButtonText']));
});
});
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return mockCmsContent[cmsFieldName];
})
}
};
const mockCmsContent = {
QuestionText: 'What caused damage.',
ButtonText: 'Select an option.',
@ -49,3 +15,35 @@ const mockCmsContent = {
}
]
};
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => mockCmsContent[cmsFieldName])
}
};
describe('button-question-modal.vue', () => {
it('Should display header text when QuestionText is defined in the CMS', async () => {
// Act
const wrapper = shallowMount(ButtonQuestionModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: 'test'
},
attachTo: document.body
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent.QuestionText));
});
it('Should display subheader text when ButtonText is defined in the CMS', async () => {
// Act
const wrapper = shallowMount(ButtonQuestionModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: 'test'
},
attachTo: document.body
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent.ButtonText));
});
});

View file

@ -1,9 +1,23 @@
import { mount } from '@vue/test-utils';
import contentGroupModal from './content-group-modal';
import crypto from 'crypto';
import contentGroupModal from './content-group-modal';
global.crypto = crypto;
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.'
};
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => mockCmsContent[cmsFieldName])
}
};
describe('content-group-modal.vue', () => {
it('Should display header text when HeaderText is defined in the CMS', async () => {
const wrapper = mount(contentGroupModal, {
@ -13,7 +27,7 @@ describe('content-group-modal.vue', () => {
},
attachTo: document.body
});
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 () => {
@ -24,7 +38,7 @@ describe('content-group-modal.vue', () => {
},
attachTo: document.body
});
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 () => {
@ -35,7 +49,7 @@ describe('content-group-modal.vue', () => {
},
attachTo: document.body
});
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 () => {
@ -46,22 +60,6 @@ describe('content-group-modal.vue', () => {
},
attachTo: document.body
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['BodyText']));
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent.BodyText));
});
});
const mockMixin = {
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.'
};

View file

@ -5,6 +5,14 @@ import { getMountOptions } from '@/helpers/unit-test-helper.js';
jest.mock('@/assets/img/loader.gif', () => 'loader.gif');
jest.mock('@/assets/img/windshield.png', () => 'windshield.png');
function setupMocks() {
const mountOptions = getMountOptions({
});
const wrapper = shallowMount(loadingModal, mountOptions);
return { wrapper };
}
describe('loadingModal', () => {
test('showModal sets modal visible', async () => {
// Arrange
@ -19,11 +27,3 @@ describe('loadingModal', () => {
wrapper.unmount();
});
});
function setupMocks() {
const mountOptions = getMountOptions({
});
const wrapper = shallowMount(loadingModal, mountOptions);
return { wrapper };
}

View file

@ -1,6 +1,5 @@
import navButton from './nav-button'
import { shallowMount } from '@vue/test-utils';
import navButton from './nav-button';
describe('NavButton', () => {
it('should display input when type is button', () => {

View file

@ -4,8 +4,8 @@ import questionsPageLayout from '@/iss-components/questions-page-layout/question
// Supporting Files
import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper.js';
import baseMixin from '../../mixins/base-mixin';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import baseMixin from '@/mixins/base-mixin';
// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => ({
@ -182,63 +182,59 @@ describe('questionsPageLayout.vue', () => {
});
function setupMocks() {
const baseStoreGettersPageData = () => {
return {
partsOrQuestions: [
{
parts: null,
partQuestions: [
{
questionSequence: 1,
questionText:
const baseStoreGettersPageData = () => ({
partsOrQuestions: [
{
parts: null,
partQuestions: [
{
questionSequence: 1,
questionText:
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
answers: [
{
answerResult: '',
answerText: 'Yes',
nextQuestionSequence: 2
},
{
answerResult: '',
answerText: 'No',
nextQuestionSequence: 3
}
]
}
],
glassLocation: 'Windshield',
glassName: 'Single',
answerKey: 'Windshield-Single',
answerData: null
}
]
};
};
const baseStoreGettersDamage = () => {
return {
partsQuestionAnswers: [
{
glassLocation: 'Windshield',
glassName: 'Single',
result: 'FW04848',
answeredQuestions: [
{
questionText:
answers: [
{
answerResult: '',
answerText: 'Yes',
nextQuestionSequence: 2
},
{
answerResult: '',
answerText: 'No',
nextQuestionSequence: 3
}
]
}
],
glassLocation: 'Windshield',
glassName: 'Single',
answerKey: 'Windshield-Single',
answerData: null
}
]
});
const baseStoreGettersDamage = () => ({
partsQuestionAnswers: [
{
glassLocation: 'Windshield',
glassName: 'Single',
result: 'FW04848',
answeredQuestions: [
{
questionText:
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
selectedAnswerText: 'Yes',
questionNum: 1
},
{
questionText:
selectedAnswerText: 'Yes',
questionNum: 1
},
{
questionText:
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
selectedAnswerText: 'Yes',
questionNum: 2
}
]
}
]
};
};
selectedAnswerText: 'Yes',
questionNum: 2
}
]
}
]
});
const mountOptions = getMountOptions({
mixins: [baseMixin, vehicleQuestionsMixin]
});

View file

@ -57,7 +57,7 @@ import alert from '@/ux-components/alert/alert';
import questionChain from '@/digital-components/question-chain/question-chain';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
import siteFooter from '@/iss-components/site-footer/site-footer';
import loadingModal from '@/iss-components/loading-modal/loading-modal.vue';
import loadingModal from '@/iss-components/loading-modal/loading-modal';
export default {
name: 'questions-page',

View file

@ -1,39 +1,6 @@
import { mount } from '@vue/test-utils';
import siteFooter from './site-footer';
describe('site-footer.vue', () => {
it('Should emit ForwardClicked on button click', async () => {
// Act
const wrapper = mount(siteFooter, {
mixins: [mockMixin],
});
wrapper.vm.buttonClick();
// Assert
expect(wrapper.emitted()['forwardClicked'][0]).toHaveBeenCalled;
});
it('Should emit BackClicked on link click', async () => {
// Act
const wrapper = mount(siteFooter, {
mixins: [mockMixin],
});
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(siteFooter, {
mixins: [mockMixin],
});
wrapper.vm.updateButtonText('newText');
// Assert
expect(wrapper.componentVM.customButtontext).toBe('newText');
});
});
const mockMixin = {
methods: {
getCmsContent: jest.fn(),
@ -41,3 +8,36 @@ const mockMixin = {
getFooterInfoBoxHeight: jest.fn(() => 80)
}
};
describe('site-footer.vue', () => {
it('Should emit ForwardClicked on button click', async () => {
// Act
const wrapper = mount(siteFooter, {
mixins: [mockMixin]
});
wrapper.vm.buttonClick();
// Assert
expect(wrapper.emitted().forwardClicked[0]).toHaveBeenCalled;
});
it('Should emit BackClicked on link click', async () => {
// Act
const wrapper = mount(siteFooter, {
mixins: [mockMixin]
});
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(siteFooter, {
mixins: [mockMixin]
});
wrapper.vm.updateButtonText('newText');
// Assert
expect(wrapper.componentVM.customButtontext).toBe('newText');
});
});

View file

@ -2,15 +2,6 @@ import siteHeader from '@/iss-components/site-header/site-header';
import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper.js';
describe('site-header', () => {
test('renders the logo image', () => {
const wrapper = setupMocks({mountOptionsMockData: {}});
expect(wrapper.find('img')).toBeTruthy();
wrapper.unmount();
});
});
function setupMocks({
mountOptionsMockData = {}
}) {
@ -19,3 +10,12 @@ function setupMocks({
return wrapper;
}
describe('site-header', () => {
test('renders the logo image', () => {
const wrapper = setupMocks({mountOptionsMockData: {} });
expect(wrapper.find('img')).toBeTruthy();
wrapper.unmount();
});
});

View file

@ -5,9 +5,7 @@ describe('site sub header', () => {
const subHeaderText = "let's fix your glass";
const mockMixin = {
methods: {
getCmsContent: jest.fn().mockImplementation(() => {
return subHeaderText;
})
getCmsContent: jest.fn().mockImplementation(() => subHeaderText)
}
};

View file

@ -4,6 +4,17 @@ import { createPinia } from 'pinia';
import { createApp } from 'vue';
import steeringTextModal from './steering-text';
const mockCmsContent = {
BodyText: 'MASteeringText'
};
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => mockCmsContent[cmsFieldName])
}
};
describe('steering-text.vue', () => {
test('Should display steering text from CMS for state MA', async () => {
// Act
@ -15,26 +26,10 @@ describe('steering-text.vue', () => {
},
attachTo: document.body
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['BodyText']));
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent.BodyText));
});
});
const vueApp = createApp(App);
const pinia = createPinia();
vueApp.use(pinia);
///////////////
// Constants //
///////////////
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return mockCmsContent[cmsFieldName];
})
}
};
const mockCmsContent = {
BodyText: 'MASteeringText'
};

View file

@ -24,13 +24,12 @@ export default {
if (params.has(queryStrings.ISS_PAGE)) {
return params.get(queryStrings.ISS_PAGE);
} else {
return '';
}
return '';
},
logPageView(pageEvent) {
const currentPageName = this.getPageNameByQueryString();
var payload = {
const payload = {
userId: getDeviceIdValue(),
sessionKey: getSessionKeyValue(),
pageName: currentPageName,
@ -47,15 +46,15 @@ export default {
logCustomEvent(category, action, label, value) {
const currentPageName = this.getPageNameByQueryString();
var payload = {
const payload = {
userId: getDeviceIdValue(),
sessionKey: getSessionKeyValue(),
pageName: currentPageName,
sessionId: getSessionIdValue(),
category: category,
action: action,
label: label,
value: value,
category,
action,
label,
value,
shouldUseSessionId: false,
experimentsForUser: useMainStore().applicationUser.experiments
};
@ -69,8 +68,8 @@ export default {
const eventToBePushed = {
event: GaEvents.GENERIC_EVENT,
category: category,
action: action,
category,
action,
label: labelToLog,
value: undefined,
path: `/iss/?${queryStrings.ISS_PAGE}=${currentPageName}`
@ -97,14 +96,14 @@ export default {
},
pushExperimentsToDataLayer() {
const experiments = useMainStore().applicationUser.experiments;
const { experiments } = useMainStore().applicationUser;
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];
googleDimensionIndex
= exp.settings[experimentSettings.GOOGLE_CUSTOM_DIMENSION_INDEX];
}
// Create object with dimension index and value.
@ -113,7 +112,7 @@ export default {
[`variationId_${googleDimensionIndex}`]: exp.variationId,
[`experimentName_${googleDimensionIndex}`]: exp.universeName,
[`variationName_${googleDimensionIndex}`]: exp.variationName,
[`customDimension_${googleDimensionIndex}`]: `${exp.universeId}_${exp.variationId}_${exp.universeName}_${exp.variationName}`,
[`customDimension_${googleDimensionIndex}`]: `${exp.universeId}_${exp.variationId}_${exp.universeName}_${exp.variationName}`
};
// Push to the data layer with the Google Custom Dimension Index.
@ -135,7 +134,7 @@ export default {
async initSession() {
const sid = getSessionIdValue();
const skey = getSessionKeyValue();
var payload = {
const payload = {
userId: getDeviceIdValue(),
sessionId: sid,
userAgent: navigator.userAgent,
@ -146,28 +145,24 @@ export default {
if (response?.data) {
if (response?.data.sessionKey && skey === 0) {
setCookieProperties(
{ [cookieNames.SESSION_KEY]: response?.data.sessionKey },
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 },
setCookieProperties({ [cookieNames.SESSION_ID]: response?.data.sessionId },
{
maxAge: 60 * 30 // 30 minutes
}
);
});
}
}
},
noSession() {
return (
getSessionKeyValue() === 0 ||
getSessionIdValue() === '00000000-0000-0000-0000-000000000000'
getSessionKeyValue() === 0
|| getSessionIdValue() === '00000000-0000-0000-0000-000000000000'
);
}
},

View file

@ -5,20 +5,14 @@ import {
GaCategories,
GaActions,
GaLabels,
GaEvents,
ValueToLogTypes
} from '@/constants/analytics';
import { useMainStore } from '@/store';
describe('analyticsMixin.js', () => {
test('logPageView: calls dispatch with type and payload', () => {
const type = '';
const payload = {};
const mockData = {
};
const mocks = setupMocksForJsFiles(mockData);
const testCookieValue = {
sid: '10000000-0000-0000-0000-000000000001'
};
@ -31,9 +25,6 @@ describe('analyticsMixin.js', () => {
});
test('logCustomEvent: calls dispatch with type and payload', () => {
const mockData = {};
const mocks = setupMocksForJsFiles(mockData);
useMainStore().logCustomEvent('someCat', 'someAction', 'someLabel', 'someVal');
expect(useMainStore().logCustomEvent).toBeCalled();
@ -42,9 +33,7 @@ describe('analyticsMixin.js', () => {
test('pushEventToGA, should call dataLayer push and logCustomEvent too', () => {
// Arrange
window.dataLayer = [];
const mockData = {};
const mocks = setupMocksForJsFiles(mockData);
var mockDataLayer = [];
const mockDataLayer = [];
mockDataLayer.push({
event: 'event',
category: 'category',
@ -64,7 +53,7 @@ describe('analyticsMixin.js', () => {
test('pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 only logs last 5 of label', () => {
// Arrange
window.dataLayer = [];
var expectedDataLayer = [];
const expectedDataLayer = [];
expectedDataLayer.push({
event: 'event',
category: 'category',
@ -75,13 +64,11 @@ describe('analyticsMixin.js', () => {
});
// Act
analyticsMixin.methods.pushEventToGA(
'category',
analyticsMixin.methods.pushEventToGA('category',
'action',
'1111122222333333',
false,
ValueToLogTypes.LAST_5
);
ValueToLogTypes.LAST_5);
// Assert
expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer));
@ -90,7 +77,7 @@ describe('analyticsMixin.js', () => {
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 = [];
const expectedDataLayer = [];
expectedDataLayer.push({
event: 'event',
category: 'category',
@ -101,13 +88,11 @@ describe('analyticsMixin.js', () => {
});
// Act
analyticsMixin.methods.pushEventToGA(
'category',
analyticsMixin.methods.pushEventToGA('category',
'action',
'111',
false,
ValueToLogTypes.LAST_5
);
ValueToLogTypes.LAST_5);
// Assert
expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer));

View file

@ -24,11 +24,11 @@ export default {
return footerInfoBox ? footerInfoBox.offsetHeight : 0;
},
savePageDataToStore(page, data) {
useMainStore().updatePageData({ page: page, data: data});
useMainStore().updatePageData({ page, data });
}
},
computed: {
// store will be accessible globally as its id + 'Store'
// store will be accessible globally as its id + 'Store'
...mapStores(useMainStore),
navigationScenarios() {
@ -44,10 +44,10 @@ export default {
return dynamicStrings;
},
cssClassNameForCmsWidget() {
return 'widget-name-' + this.cmsWidgetName;
return `widget-name-${this.cmsWidgetName}`;
},
routerParams() {
return routerParams;
}
}
}
};

View file

@ -1,5 +1,5 @@
import baseMixin from '@/mixins/base-mixin';
import { shallowMount, mount } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
describe('base-mixin', () => {
it('should set and get cms content', () => {

View file

@ -3,7 +3,7 @@ import { useMainStore } from '@/store';
export default {
methods: {
hasSettingEqualTo(settingName, settingValue) {
return useMainStore().experimentSettings[settingName] == settingValue;
return useMainStore().experimentSettings[settingName] === settingValue;
},
hasSetting(settingName) {
return Object.hasOwn(useMainStore().experimentSettings, settingName);

View file

@ -11,14 +11,10 @@ export default {
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) => 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) => pq.parts?.some((part) => part.requiresCapabilityQuestions === true));
},
// method to only include keys listed for lineItems.glassParts in
@ -54,8 +50,8 @@ export default {
];
return (
orderedVehicleQuestionPages.indexOf(currentPage) -
orderedVehicleQuestionPages.indexOf(issPage)
orderedVehicleQuestionPages.indexOf(currentPage)
- orderedVehicleQuestionPages.indexOf(issPage)
);
},
currentPageComesBeforePage(currentPage = this.$route.query.issPage, issPage) {
@ -75,18 +71,18 @@ export default {
alreadyAnsweredQuestions?.forEach((answeredGlass) => {
// if answeredGlass lacks any of these properties then exit
if (
!answeredGlass.glassLocation ||
!answeredGlass.glassName ||
!answeredGlass.answeredQuestions ||
(!answeredGlass.result && !answeredGlass.partNum)
!answeredGlass.glassLocation
|| !answeredGlass.glassName
|| !answeredGlass.answeredQuestions
|| (!answeredGlass.result && !answeredGlass.partNum)
) {
return;
}
// test if glass parts match
if (
glass.glassLocation === answeredGlass.glassLocation &&
glass.glassName === answeredGlass.glassName
glass.glassLocation === answeredGlass.glassLocation
&& glass.glassName === answeredGlass.glassName
) {
let answerString = '';
@ -98,12 +94,10 @@ export default {
// determine which answer was previously chosen
const chosenAns = glass.questions[
answeredQuestion.questionNum - 1
].answers.find((a) => {
return (
a.answerText.toUpperCase() ===
answeredQuestion.selectedAnswerText.toUpperCase()
);
});
].answers.find((a) => (
a.answerText.toUpperCase()
=== answeredQuestion.selectedAnswerText.toUpperCase()
));
// set the answerString to use for answerSelected
if (chosenAns.nextQuestionSequence) {
answerString = `${answeredQuestion.questionNum}|nextQuestion|${chosenAns.nextQuestionSequence}|${chosenAns.answerText}`;
@ -112,8 +106,8 @@ export default {
}
// mark this question as answered (question-chain will read this)
glass.questions[answeredQuestion.questionNum - 1].answerSelected =
answerString;
glass.questions[answeredQuestion.questionNum - 1].answerSelected
= answerString;
// mark this question as suppressed if needed (question-chain uses this)
if (answeredQuestion.suppressThisQuestion) {
glass.questions[
@ -132,8 +126,8 @@ export default {
// add answerData to current glass
glass.answerData = {
answerResult: answerResult,
answeredQuestions: answeredGlass.answeredQuestions,
answerResult,
answeredQuestions: answeredGlass.answeredQuestions
};
}
});
@ -252,8 +246,8 @@ export default {
// if the duplicate is 1ST question in array, set indexToSuppressTo
if (questionIndex === 0) {
if (
!indexToSuppressTo ||
matchedAnswer.nextQuestionSequence < indexToSuppressTo
!indexToSuppressTo
|| matchedAnswer.nextQuestionSequence < indexToSuppressTo
) {
indexToSuppressTo = matchedAnswer.nextQuestionSequence;
}
@ -265,8 +259,8 @@ export default {
q.answers.forEach((a) => {
// revert any previously set nextQuestion logic modifications
if (
a.originalNextQuestionSequence ===
question.questionSequence
a.originalNextQuestionSequence
=== question.questionSequence
) {
// restore original nextQuestionSequence
a.nextQuestionSequence = a.originalNextQuestionSequence;
@ -283,16 +277,16 @@ export default {
// update questions that lead to duplicated question
if (matchedAnswer.nextQuestionSequence) {
a.originalNextQuestionSequence =
a.nextQuestionSequence;
a.nextQuestionSequence =
matchedAnswer.nextQuestionSequence;
a.originalNextQuestionSequence
= a.nextQuestionSequence;
a.nextQuestionSequence
= matchedAnswer.nextQuestionSequence;
} else {
a.originalNextQuestionSequence =
a.nextQuestionSequence;
a.originalNextQuestionSequence
= a.nextQuestionSequence;
a.nextQuestionSequence = null;
a.originalAnswerResult =
a.originalAnswerResult || a.answerResult;
a.originalAnswerResult
= a.originalAnswerResult || a.answerResult;
a.answerResult = matchedAnswer.answerResult;
}
}
@ -303,9 +297,7 @@ export default {
question.suppressThisQuestion = true;
// are there any questions left that are not suppressed?
const remainingQuestions = glass.questions.filter((q) => {
return !q.suppressThisQuestion;
});
const remainingQuestions = glass.questions.filter((q) => !q.suppressThisQuestion);
if (remainingQuestions.length < 1) {
// this is the final answer for this glass piece
@ -315,14 +307,14 @@ export default {
questionText: question.questionText,
selectedAnswerText: matchedAnswer.answerText,
questionNum: question.questionSequence,
suppressThisQuestion: question.suppressThisQuestion,
suppressThisQuestion: question.suppressThisQuestion
};
// set the answerData (used as indicator that it has been already answered)
glass.answerData = {
answerResult: matchedAnswer.nextQuestionSequence
? matchedAnswer.nextQuestionSequence
: matchedAnswer.answerResult,
answeredQuestions: [answeredQuestionObj],
answeredQuestions: [answeredQuestionObj]
};
// suppress this glass piece because it has an answer
@ -332,8 +324,7 @@ export default {
});
// Update key to force re-render of glass piece with duplicate question in case user changes previous related answer in the chain
self.questionsData[glassIndex].key =
self.questionsData[glassIndex].key + Date.now().toString();
self.questionsData[glassIndex].key += Date.now().toString();
}
});
});
@ -341,7 +332,7 @@ export default {
// set final answer data for the current answered glass part
self.questionsData[answer.index].answerData = {
answerResult: answer.answerResult,
answeredQuestions: answer.answeredQuestions,
answeredQuestions: answer.answeredQuestions
};
// this part has been fully answered, so advance to next part's question chain
@ -360,74 +351,60 @@ export default {
const currentPage = self.$route.query.issPage;
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);
if (hasPartQuestions && this.currentPageComesBeforePage(currentPage, issPageValues.PART_QUESTIONS))
{
self.$router.navigate(self.navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS, self.$route,
{},
{},
{ partsOrQuestions: partsOrQuestions }
);
} else if (hasGlassLocationWithMultipleParts && this.currentPageComesBeforePage(currentPage, issPageValues.VEHICLE_PARTS))
{
if (hasPartQuestions && this.currentPageComesBeforePage(currentPage, issPageValues.PART_QUESTIONS)) {
self.$router.navigate(self.navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS, self.$route, {}, {}, { partsOrQuestions });
} else if (hasGlassLocationWithMultipleParts && this.currentPageComesBeforePage(currentPage, issPageValues.VEHICLE_PARTS)) {
// if multiple parts on any glass
// go to vehicle-parts page and pass the partsData
self.$router.navigate(
self.navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
self.$router.navigate(self.navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
self.$route,
{},
{},
{ partsOrQuestions: partsOrQuestions }
);
{ partsOrQuestions });
} else if (
hasChildPartQuestions &&
this.currentPageComesBeforePage(currentPage, issPageValues.MOLDING_QUESTIONS)
hasChildPartQuestions
&& this.currentPageComesBeforePage(currentPage, issPageValues.MOLDING_QUESTIONS)
) {
// if any childpart questions
// go to molding-questions page and pass the partsData
self.$router.navigate(
self.navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS,
self.$router.navigate(self.navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS,
self.$route,
{},
{},
{ partsOrQuestions: partsOrQuestions }
);
{ partsOrQuestions });
} else if (
hasCapabilityQuestions &&
this.currentPageComesBeforePage(currentPage, issPageValues.CAPABILITY_QUESTIONS)
hasCapabilityQuestions
&& this.currentPageComesBeforePage(currentPage, issPageValues.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) {
for (const partOrQuestion of partsOrQuestions) {
if (this.hasCapabilityQuestions([partOrQuestion])) {
let capabilityQuestionsForGlassLocation = (await useMainStore().getCapabilityQuestions(useMainStore().vehicle.carId, partOrQuestion.parts[0].partNumber)).data;
const capabilityQuestionsForGlassLocation = (await useMainStore().getCapabilityQuestions(useMainStore().vehicle.carId, partOrQuestion.parts[0].partNumber)).data;
capabilityQuestionsForGlassLocation.forEach((question) => {
question.answers = question.answers.map((answer) => {
return {
...answer,
answerResult: answer.answerResult1
};
});
question.answers = question.answers.map((answer) => ({
...answer,
answerResult: answer.answerResult1
}));
});
partOrQuestion.capabilityQuestions = capabilityQuestionsForGlassLocation;
}
}
self.$router.navigate(
self.navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS,
self.$router.navigate(self.navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS,
self.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
} else {
// if single parts only
const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions);
@ -435,28 +412,27 @@ export default {
// save to store lineItems.glassParts
useMainStore().updateGlassParts(collectedGlassParts);
self.$router.navigate(self.navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,self.$route);
self.$router.navigate(self.navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, self.$route);
}
},
navigateBack(vm) {
const self = vm ?? this;
const partsOrQuestions = (
self.mainStore.pageData(issPageValues.CAPABILITY_QUESTIONS) ??
self.mainStore.pageData(issPageValues.MOLDING_QUESTIONS) ??
self.mainStore.pageData(issPageValues.VEHICLE_PARTS) ??
self.mainStore.pageData(issPageValues.PART_QUESTIONS)
self.mainStore.pageData(issPageValues.CAPABILITY_QUESTIONS)
?? self.mainStore.pageData(issPageValues.MOLDING_QUESTIONS)
?? self.mainStore.pageData(issPageValues.VEHICLE_PARTS)
?? self.mainStore.pageData(issPageValues.PART_QUESTIONS)
)?.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 = '';
if (self.mainStore.order.damage.isRepair) {
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_REPAIR;
}
else {
} else {
backNavigationScenario = self.mainStore.vehicle.vin
? navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS
: navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS;
@ -464,24 +440,24 @@ export default {
const currentPage = self.$route.query.issPage;
if (
hasCapabilityQuestions &&
this.currentPageComesAfterPage(currentPage, issPageValues.CAPABILITY_QUESTIONS)
hasCapabilityQuestions
&& this.currentPageComesAfterPage(currentPage, issPageValues.CAPABILITY_QUESTIONS)
) {
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_CAPABILITY_QUESTIONS;
} else if (
hasChildPartQuestions &&
this.currentPageComesAfterPage(currentPage, issPageValues.MOLDING_QUESTIONS)
hasChildPartQuestions
&& this.currentPageComesAfterPage(currentPage, issPageValues.MOLDING_QUESTIONS)
) {
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS;
} else if (
hasGlassLocationWithMultipleParts &&
this.currentPageComesAfterPage(currentPage, issPageValues.VEHICLE_PARTS)
hasGlassLocationWithMultipleParts
&& this.currentPageComesAfterPage(currentPage, issPageValues.VEHICLE_PARTS)
) {
backNavigationScenario =
navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE;
backNavigationScenario
= navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE;
} else if (
hasPartQuestions &&
this.currentPageComesAfterPage(currentPage, issPageValues.PART_QUESTIONS)
hasPartQuestions
&& this.currentPageComesAfterPage(currentPage, issPageValues.PART_QUESTIONS)
) {
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS;
}

View file

@ -3,7 +3,7 @@ import { shallowMount } from '@vue/test-utils';
import { setupMocksForJsFiles, getMountOptions } from '@/helpers/unit-test-helper.js';
import { issPageValues } from '@/router/router-constants/issPage-values';
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios';
import {useMainStore} from '@/store';
import { useMainStore } from '@/store';
describe('vehicle-questions-mixin', () => {
afterEach(() => {
@ -290,8 +290,7 @@ describe('vehicle-questions-mixin', () => {
[issPageValues.PART_QUESTIONS, issPageValues.CAPABILITY_QUESTIONS, true],
[issPageValues.MOLDING_QUESTIONS, issPageValues.CAPABILITY_QUESTIONS, true]
];
test.each(testCases)(
'%s comes before %s is %s',
test.each(testCases)('%s comes before %s is %s',
(currentPage, nextPage, expectedResult) => {
// Arrange
const { wrapper } = setupMocks({});
@ -301,8 +300,7 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(result).toEqual(expectedResult);
}
);
});
});
describe('currentPageComesAfterPage', () => {
@ -390,11 +388,9 @@ describe('vehicle-questions-mixin', () => {
const { wrapper } = setupMocks({});
// Act
const returnedGlass = await wrapper.vm.setupInitialData(
glass,
const returnedGlass = await wrapper.vm.setupInitialData(glass,
i,
alreadyAnsweredQuestions
);
alreadyAnsweredQuestions);
// Assert
expect(returnedGlass.answerData).toMatchObject({ answerResult: 'WKT D1106 C' });
@ -963,22 +959,12 @@ describe('vehicle-questions-mixin', () => {
// Act
wrapper.vm.handleAnswerUpdates(answerNo, '', wrapper.vm);
const duplicatedQuestion = wrapper.vm.questionsData[1].questions[2];
const duplicatedQuestionAnswer = duplicatedQuestion.answers.filter((a) => {
return a.selected;
});
const answerToTest = wrapper.vm.questionsData[1].questions[0].answers.filter(
(a) => {
return a.originalNextQuestionSequence;
}
);
const duplicatedQuestionAnswer = duplicatedQuestion.answers.filter((a) => a.selected);
const answerToTest = wrapper.vm.questionsData[1].questions[0].answers.filter((a) => a.originalNextQuestionSequence);
// Assert
expect(answerToTest[0].originalNextQuestionSequence).toEqual(
duplicatedQuestion.questionSequence
);
expect(answerToTest[0].nextQuestionSequence).toEqual(
duplicatedQuestionAnswer[0].nextQuestionSequence
);
expect(answerToTest[0].originalNextQuestionSequence).toEqual(duplicatedQuestion.questionSequence);
expect(answerToTest[0].nextQuestionSequence).toEqual(duplicatedQuestionAnswer[0].nextQuestionSequence);
});
describe('and the duplicate was the first question for that part', () => {
@ -1080,15 +1066,9 @@ describe('vehicle-questions-mixin', () => {
wrapper.vm.handleAnswerUpdates(answerNo, '', wrapper.vm);
// Assert
expect(
wrapper.vm.questionsData[1].questions[0].suppressThisQuestion
).toBeTruthy();
expect(
wrapper.vm.questionsData[1].questions[1].suppressThisQuestion
).toBeTruthy();
expect(
wrapper.vm.questionsData[1].questions[2].suppressThisQuestion
).toBeFalsy();
expect(wrapper.vm.questionsData[1].questions[0].suppressThisQuestion).toBeTruthy();
expect(wrapper.vm.questionsData[1].questions[1].suppressThisQuestion).toBeTruthy();
expect(wrapper.vm.questionsData[1].questions[2].suppressThisQuestion).toBeFalsy();
});
});
});
@ -1194,19 +1174,13 @@ describe('vehicle-questions-mixin', () => {
const questionsToTest = wrapper.vm.questionsData[1].questions;
const answerLeadingToDuplicate = questionsToTest[0].answers[1];
const duplicateQuestion = questionsToTest[2];
const answerInDuplicateQuestion = duplicateQuestion.answers.filter((a) => {
return a.selected;
});
const answerInDuplicateQuestion = duplicateQuestion.answers.filter((a) => a.selected);
// Assert
expect(answerLeadingToDuplicate.answerResult).toEqual(
answerInDuplicateQuestion[0].answerResult
);
expect(answerLeadingToDuplicate.answerResult).toEqual(answerInDuplicateQuestion[0].answerResult);
expect(answerLeadingToDuplicate.originalAnswerResult).toBeFalsy();
expect(answerLeadingToDuplicate.nextQuestionSequence).toEqual(null);
expect(answerLeadingToDuplicate.originalNextQuestionSequence).toEqual(
duplicateQuestion.questionSequence
);
expect(answerLeadingToDuplicate.originalNextQuestionSequence).toEqual(duplicateQuestion.questionSequence);
});
});
});
@ -1244,7 +1218,7 @@ describe('vehicle-questions-mixin', () => {
];
const { wrapper } = setupMocks({
partsOrQuestions: partsOrQuestions
partsOrQuestions
});
// Act
@ -1252,13 +1226,11 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
wrapper.vm.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
});
test('multiple glass locations have part questions => go to parts-questions', async () => {
@ -1359,7 +1331,7 @@ describe('vehicle-questions-mixin', () => {
];
const { wrapper } = setupMocks({
partsOrQuestions: partsOrQuestions
partsOrQuestions
});
// Act
@ -1367,13 +1339,11 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
wrapper.vm.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
});
test('multiple glass locations selected, one has part question => go to parts-questions', async () => {
@ -1466,7 +1436,7 @@ describe('vehicle-questions-mixin', () => {
];
const { wrapper } = setupMocks({
partsOrQuestions: partsOrQuestions
partsOrQuestions
});
// Act
@ -1474,13 +1444,11 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
wrapper.vm.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
});
test('a selected glass location has part questions and multiple parts => go to parts-questions', async () => {
@ -1621,7 +1589,7 @@ describe('vehicle-questions-mixin', () => {
];
const { wrapper } = setupMocks({
partsOrQuestions: partsOrQuestions
partsOrQuestions
});
// Act
@ -1629,13 +1597,11 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
wrapper.vm.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
});
});
@ -1669,7 +1635,7 @@ describe('vehicle-questions-mixin', () => {
];
const { wrapper } = setupMocks({
partsOrQuestions: partsOrQuestions
partsOrQuestions
});
// Act
@ -1677,13 +1643,11 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
wrapper.vm.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
});
test('multiple glass locations selected, one of them has multiple parts => go to vehicle parts', async () => {
@ -1781,7 +1745,7 @@ describe('vehicle-questions-mixin', () => {
];
const { wrapper } = setupMocks({
partsOrQuestions: partsOrQuestions
partsOrQuestions
});
// Act
@ -1789,13 +1753,11 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
wrapper.vm.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
});
test('multiple glass locations selected, multiple have multiple parts => go to vehicle-parts', async () => {
@ -1959,7 +1921,7 @@ describe('vehicle-questions-mixin', () => {
];
const { wrapper } = setupMocks({
partsOrQuestions: partsOrQuestions
partsOrQuestions
});
// Act
@ -1967,13 +1929,11 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
wrapper.vm.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
});
});
@ -2018,7 +1978,7 @@ describe('vehicle-questions-mixin', () => {
];
const { wrapper } = setupMocks({
partsOrQuestions: partsOrQuestions
partsOrQuestions
});
// Act
@ -2026,13 +1986,11 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS,
wrapper.vm.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
});
});
@ -2060,7 +2018,7 @@ describe('vehicle-questions-mixin', () => {
];
const { wrapper } = setupMocks({
partsOrQuestions: partsOrQuestions
partsOrQuestions
});
// Act
@ -2068,13 +2026,11 @@ describe('vehicle-questions-mixin', () => {
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS,
wrapper.vm.$route,
{},
{},
{ partsOrQuestions }
);
{ partsOrQuestions });
});
});
});
@ -2089,10 +2045,8 @@ describe('vehicle-questions-mixin', () => {
wrapper.vm.navigateBack();
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_BACK_WITH_REPAIR,
{ query: { issPage: issPageValues.COVERAGE_STATEMENT } }
);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_REPAIR,
{ query: { issPage: issPageValues.COVERAGE_STATEMENT } });
});
test('current page is molding questions and there are part questions, multiple parts to choose, and capability questions => go to vehicle-parts', () => {
@ -2107,10 +2061,8 @@ describe('vehicle-questions-mixin', () => {
wrapper.vm.navigateBack();
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE,
{ query: { issPage: issPageValues.MOLDING_QUESTIONS } }
);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE,
{ query: { issPage: issPageValues.MOLDING_QUESTIONS } });
});
test('current page is molding questions and there are part questions and capability questions => go to part-questions', () => {
@ -2125,15 +2077,13 @@ describe('vehicle-questions-mixin', () => {
wrapper.vm.navigateBack();
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS,
{ query: { issPage: issPageValues.MOLDING_QUESTIONS } }
);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS,
{ query: { issPage: issPageValues.MOLDING_QUESTIONS } });
});
});
});
function setupMocks({ issPage = issPageValues.VIN_LOOKUP, hasVin, carId }) {
function setupMocks({ issPage = issPageValues.VIN_LOOKUP }) {
const baseMixin = setupMocksForJsFiles({
actionList: [
{
@ -2168,8 +2118,8 @@ function setupMocks({ issPage = issPageValues.VIN_LOOKUP, hasVin, carId }) {
};
const store = useMainStore();
let actionResult = [];
store.getCapabilityQuestions.mockImplementation(() => Promise.resolve({data: actionResult}));
const actionResult = [];
store.getCapabilityQuestions.mockImplementation(() => Promise.resolve({ data: actionResult }));
const wrapper = shallowMount(mockVehicleQuestionComponent, mocks);

View file

@ -5,7 +5,7 @@ export default {
methods: {
async navigateForwardWithSingleCarMatch() {
const result = await useMainStore().getPartsOrQuestions();
const partsOrQuestions = result.data.partsOrQuestions;
const { partsOrQuestions } = result.data;
vehicleQuestionsMixin.methods.navigateForward(partsOrQuestions, this);
}

View file

@ -4,27 +4,6 @@ import { getMountOptions } from '@/helpers/unit-test-helper.js';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import { useMainStore } from '@/store';
describe('vin-pages-mixin', () => {
afterEach(() => {
jest.clearAllMocks();
});
describe('navigateForwardWithSingleCarMatch', () => {
test('should navigateForward', async () => {
// Arrange
useMainStore().getPartsOrQuestions = () => { return { data: {partsOrQuestions: {}}} };
const { wrapper } = setupMocks({});
vehicleQuestionsMixin.methods.navigateForward = jest.fn();
// Act
await wrapper.vm.navigateForwardWithSingleCarMatch();
// Assert
expect(vehicleQuestionsMixin.methods.navigateForward).toHaveBeenCalled();
});
});
});
function setupMocks() {
const mocks = getMountOptions({
router: {
@ -40,3 +19,24 @@ function setupMocks() {
return { wrapper };
}
describe('vin-pages-mixin', () => {
afterEach(() => {
jest.clearAllMocks();
});
describe('navigateForwardWithSingleCarMatch', () => {
test('should navigateForward', async () => {
// Arrange
useMainStore().getPartsOrQuestions = () => ({ data: { partsOrQuestions: {} } });
const { wrapper } = setupMocks({});
vehicleQuestionsMixin.methods.navigateForward = jest.fn();
// Act
await wrapper.vm.navigateForwardWithSingleCarMatch();
// Assert
expect(vehicleQuestionsMixin.methods.navigateForward).toHaveBeenCalled();
});
});
});