Merge branch 'develop' into feature/kiener/INSR-7753

This commit is contained in:
Katie Kroell 2026-01-30 08:34:57 -05:00
commit 26cbce89fc
7 changed files with 74 additions and 228 deletions

View file

@ -4,7 +4,8 @@ const dynamicStrings = Object.freeze({
ROUTER_LINK: 'routerLink:', ROUTER_LINK: 'routerLink:',
MODAL_LINK: 'modalLink', MODAL_LINK: 'modalLink',
TEXT_LINK: 'textLink', TEXT_LINK: 'textLink',
EXTERNAL_LINK: 'externalLink' EXTERNAL_LINK: 'externalLink',
PHONE_LINK: 'phoneLink'
}); });
export default dynamicStrings; export default dynamicStrings;

View file

@ -623,6 +623,15 @@ export function getRouterLinkHtmlStringFromCopy(copy) {
return `<a href='/?issPage=${getRouterLinkRouteFromCopy(copy)}' class='router-link'>${getRouterLinkDisplayTextFromCopy(copy)}</a>`; return `<a href='/?issPage=${getRouterLinkRouteFromCopy(copy)}' class='router-link'>${getRouterLinkDisplayTextFromCopy(copy)}</a>`;
} }
/**
* Returns a phone link as an 'a' tag element
* @param copy
* @returns string
*/
export function getPhoneLinkHtmlStringFromPhoneNumber(phoneNumber) {
return `<a href='tel:${phoneNumber}' class='phone-link'>${phoneNumber}.</a>`;
}
// Copy returned from the CMS that has newlines will return blocks wrapped in // Copy returned from the CMS that has newlines will return blocks wrapped in
// <p ... >...</p> // <p ... >...</p>
// 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

View file

@ -40,12 +40,12 @@ export async function submitWorkOrder({ submitType }) {
shouldAwaitSaveSessionQueue: true, shouldAwaitSaveSessionQueue: true,
submitAfterSave: true submitAfterSave: true
}); });
store.createSubmittedOrder(submitType); await store.createSubmittedOrder(submitType);
} }
export async function submitBailout() { export async function submitBailout() {
const store = useMainStore(); const store = useMainStore();
store.resetSubmittedOrder(); store.resetSubmittedOrder();
await store.saveSession({}); await store.saveSession({});
store.createSubmittedOrder(submitType.BAILOUT); await store.createSubmittedOrder(submitType.BAILOUT);
} }

View file

@ -2,14 +2,12 @@
import tpaConfirmation from '@/layouts/tpa-confirmation/tpa-confirmation.vue'; import tpaConfirmation from '@/layouts/tpa-confirmation/tpa-confirmation.vue';
// Supporting Files // Supporting Files
import { getEnumName, getMountOptions } from '@/helpers/unit-test-helper.js'; import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { getDefaultState, useMainStore } from '@/store/index.js'; import { getDefaultState, useMainStore } from '@/store/index.js';
import settleAllPromises from '@/helpers/layout-helper.js'; import settleAllPromises from '@/helpers/layout-helper.js';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import coverageStatuses from '@/constants/coverage-statuses';
import { deepClone } from '@/helpers/object-helper';
jest.mock('@/helpers/layout-helper.js', () => jest.fn()); jest.mock('@/helpers/layout-helper.js', () => jest.fn());
@ -20,6 +18,7 @@ jest.mock('@/helpers/cms-content-helper', () => ({
splitCopyOnCMSPlaceHolder: jest.fn().mockImplementation(() => 'test'), splitCopyOnCMSPlaceHolder: jest.fn().mockImplementation(() => 'test'),
getRouterLinkRouteFromCopy: jest.fn(), getRouterLinkRouteFromCopy: jest.fn(),
getRouterLinkDisplayTextFromCopy: jest.fn(), getRouterLinkDisplayTextFromCopy: jest.fn(),
getPhoneLinkHtmlStringFromPhoneNumber: jest.fn(),
doesCopyContainTextLink: jest.fn() doesCopyContainTextLink: jest.fn()
})); }));
@ -122,27 +121,6 @@ describe('TPAConfirmation.vue', () => {
// Assert // Assert
expect(bodyTwo.exists()).toBe(true); expect(bodyTwo.exists()).toBe(true);
}); });
test('Should render Order Details title', () => {
// Act
const orderDetailsTitle = wrapper.findComponent({ ref: 'tpaConfirmationOrderDetailsTitle' });
// Assert
expect(orderDetailsTitle.exists()).toBe(true);
});
test('Should render Order Details body', () => {
// Act
const orderDetailsBody = wrapper.findComponent({ ref: 'tpaConfirmationOrderDetailsBody' });
// Assert
expect(orderDetailsBody.exists()).toBe(true);
});
test('Should render Deductible Box', () => {
// Act
const deductibleBox = wrapper.findComponent({ ref: 'deductibleBox' });
// Assert
expect(deductibleBox.exists()).toBe(true);
});
test('If Advanced flow, should display Site Footer', () => { test('If Advanced flow, should display Site Footer', () => {
// Arrange // Arrange
const carrierReturnUrl = 'testURL'; const carrierReturnUrl = 'testURL';
@ -167,127 +145,7 @@ describe('TPAConfirmation.vue', () => {
expect(siteFooter.isVisible()).toBe(false); expect(siteFooter.isVisible()).toBe(false);
}); });
}); });
describe('Computed properties', () => {
describe('Deductible Box value', () => {
test('Should return "Verifying coverage" when isVerified false', () => {
// Arrange
const initialStore = {
order: {
insuranceCoverage: {
coverageStatus: coverageStatuses.PENDING
}
}
};
const mockStoreActions = () => {
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => defaultOrder);
};
const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions);
const expected = 'Verifying coverage';
// Assert
expect(wrapper.vm.deductibleBoxValue).toBe(expected);
});
test('Should return deductible value when isVerified true', () => {
// Arrange
const currentDeductible = '500';
const initialStore = {
order: {
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED
},
currentDeductible
}
};
const order = deepClone(defaultOrder);
order.isVerified = true;
const mockStoreActions = () => {
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
};
const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions);
const notExpected = 'Verifying coverage';
// Assert
expect(wrapper.vm.deductibleBoxValue).not.toBe(notExpected);
});
});
});
describe('Methods', () => { describe('Methods', () => {
describe.only('getCustomValueFromString', () => {
describe.each([
[false, false, 0],
[false, false, 500],
[false, true, 0],
[true, true, 500]
])('with argument deductibleAboveZero', (expected, isVerified, deductible) => {
test(`returns ${expected} when coverageStatus is ${getEnumName(coverageStatuses, status)} and currentDeductible is ${deductible}`, () => {
// Arrange
const order = deepClone(defaultOrder);
order.isVerified = isVerified;
order.currentDeductible = deductible;
const mockStoreActions = () => {
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
};
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
const argument = 'deductibleAboveZero';
// Act
const result = wrapper.vm.getCustomValueFromString(argument);
// Assert
expect(result).toBe(expected);
});
});
describe('with argument zeroDeductible', () => {
describe.each([
[false, false, 0],
[false, false, 500],
[true, true, 0],
[false, true, 500]
])('with argument zeroDeductible', (expected, isVerified, deductible) => {
test(`returns ${expected} when coverageStatus is ${getEnumName(coverageStatuses, status)} and currentDeductible is ${deductible}`, () => {
// Arrange
const order = deepClone(defaultOrder);
order.isVerified = isVerified;
order.currentDeductible = deductible;
const mockStoreActions = () => {
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
};
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
const argument = 'zeroDeductible';
// Act
const result = wrapper.vm.getCustomValueFromString(argument);
// Assert
expect(result).toBe(expected);
});
});
});
describe('with argument verifyingCoverage', () => {
describe.each([
[true, false],
[false, true]
])('with argument zeroDeductible', (expected, isVerified) => {
test(`returns ${expected} when coverageStatus is ${getEnumName(coverageStatuses, status)}`, () => {
// Arrange
const order = deepClone(defaultOrder);
order.isVerified = isVerified;
order.currentDeductible = 123;
const mockStoreActions = () => {
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => order);
};
const { wrapper } = getMountedComponent({}, {}, mockStoreActions);
const argument = 'verifyingCoverage';
// Act
const result = wrapper.vm.getCustomValueFromString(argument);
// Assert
expect(result).toBe(expected);
});
});
});
});
describe('Navigation', () => { describe('Navigation', () => {
const mockStoreActions = () => { const mockStoreActions = () => {
useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => defaultOrder); useMainStore().getSubmittedOrder = jest.fn().mockImplementation(() => defaultOrder);

View file

@ -12,40 +12,21 @@
</div> </div>
<div class="iss-heritage-container-width"> <div class="iss-heritage-container-width">
<div class="tpa-confirmation-container iss-heritage-content-container-width"> <div class="tpa-confirmation-container iss-heritage-content-container-width">
<div class="d-flex justify-content-center align-items-center text-color--black pb-2 fs-5"> <div class="header">
<img :src="tpaConfirmationImage" /> <img :src="tpaConfirmationImage" />
<span <span v-html="tpaConfirmationHeaderText"></span>
class="ms-2"
v-html="tpaConfirmationHeaderText"></span>
</div> </div>
<div class="text-center text-color--black mb-3 fw-bold"> <div class="subheader">
<span v-html="tpaConfirmationSubheaderText"></span> <span v-html="tpaConfirmationSubheaderText"></span>
</div> </div>
<textBlock <textBlock
ref="tpaConfirmationBodyOne" ref="tpaConfirmationBodyOne"
class="tpa-body-one mt-0" class="tpa-body-one"
:customText="tpaConfirmationBodyOne" /> :customText="tpaConfirmationBodyOne" />
<textBlock <textBlock
ref="tpaConfirmationBodyTwo" ref="tpaConfirmationBodyTwo"
:customText="tpaConfirmationBodyTwo" class="tpa-body-one"
class="small mt-0" /> :customText="tpaConfirmationBodyTwo" />
<div ref="confirmationOrderDetailsSection">
<textBlock
ref="tpaConfirmationOrderDetailsTitle"
:customText="orderDetailsTitle"
class="text-color--black fw-bold lh-base mt-5 mb-4"
:marginTopSizeOverride="4" />
<textBlock
id="tpaConfirmationOrderDetailsBody"
ref="tpaConfirmationOrderDetailsBody"
:customText="orderDetailsBody"
:marginTopSizeOverride="4"
class="mb-4 small" />
<deductibleBox
ref="deductibleBox"
class="px-3"
:value="deductibleBoxValue" />
</div>
<siteFooter <siteFooter
v-if="carrierUrl" v-if="carrierUrl"
ref="siteFooter" ref="siteFooter"
@ -64,11 +45,11 @@
import siteHeader from '@/iss-components/site-header/site-header.vue'; import siteHeader from '@/iss-components/site-header/site-header.vue';
import siteFooter from '@/iss-components/site-footer/site-footer.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import textBlock from '@/digital-components/text-block/text-block.vue'; import textBlock from '@/digital-components/text-block/text-block.vue';
import deductibleBox from '@/layouts/tpa-submit/deductible-box/deductible-box.vue';
// Supporting files // Supporting files
import { import {
fetchCmsContentForPage, fetchCmsContentForPage,
getPhoneLinkHtmlStringFromPhoneNumber,
processIfStatements processIfStatements
} from '@/helpers/cms-content-helper'; } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper'; import settleAllPromises from '@/helpers/layout-helper';
@ -77,8 +58,7 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import { import {
toTitleCase, toTitleCase,
toDisplayPhoneNumber, toDisplayPhoneNumber
formatAmountInDollars
} from '@/helpers/text-helper.js'; } from '@/helpers/text-helper.js';
const VERIFYING_COVERAGE = 'Verifying coverage'; const VERIFYING_COVERAGE = 'Verifying coverage';
@ -89,7 +69,6 @@ export default {
siteHeader, siteHeader,
siteFooter, siteFooter,
textBlock, textBlock,
deductibleBox,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
Form Form
}, },
@ -132,26 +111,17 @@ export default {
return this.getCmsContent('TPAConfirmationContent', 'BodyText') return this.getCmsContent('TPAConfirmationContent', 'BodyText')
?.replaceAll( ?.replaceAll(
'{custom:phoneNumber}', '{custom:phoneNumber}',
this.preferredShopPhoneNumber getPhoneLinkHtmlStringFromPhoneNumber(this.preferredShopPhoneNumber)
) )
?.replaceAll('{custom:glassShop}', this.preferredShopName); ?.replaceAll('{custom:glassShop}', this.preferredShopName);
}, },
tpaConfirmationBodyTwo() { tpaConfirmationBodyTwo() {
return this.getCmsContent('TPAConfirmationContent', 'BodyText2'); return this.getCmsContent('TPAConfirmationContent', 'BodyText2')
}, ?.replaceAll(
orderDetailsTitle() { '{custom:carrierPhone}',
return this.getCmsContent('OrderDetailsContent', 'HeaderText'); getPhoneLinkHtmlStringFromPhoneNumber(this.carrierPhoneNumber)
}, )
orderDetailsBody() { ?.replaceAll('{custom:carrierName}', this.carrierName);
const orderDetailsBodyText = this.getCmsContent(
'OrderDetailsContent',
'BodyText'
);
return this.processIfStatements(
orderDetailsBodyText,
'custom',
this.getCustomValueFromString
);
}, },
deductibleBoxValue() { deductibleBoxValue() {
return this.isVerified return this.isVerified
@ -171,7 +141,7 @@ export default {
return this.submittedOrder.isVerified; return this.submittedOrder.isVerified;
}, },
carrierName() { carrierName() {
return this.mainStore.issConfig.clientName; return this.mainStore.issConfig.clientFullName;
}, },
carrierUrl() { carrierUrl() {
return this.mainStore.issConfig.successReturnURL; return this.mainStore.issConfig.successReturnURL;
@ -189,48 +159,43 @@ export default {
async forwardButtonAction() { async forwardButtonAction() {
this.$router.navigateToExternalUrl(this.carrierUrl); this.$router.navigateToExternalUrl(this.carrierUrl);
}, },
getCustomValueFromString(str) {
switch (str) {
case 'deductibleAboveZero':
return this.isVerified && this.currentDeductible !== 0;
case 'zeroDeductible':
return this.isVerified && this.currentDeductible === 0;
case 'verifyingCoverage':
return !this.isVerified;
case 'coverageVerified':
return this.isVerified;
default:
return null;
}
},
toDisplayPhoneNumber, toDisplayPhoneNumber,
formatAmountInDollars,
processIfStatements processIfStatements
} }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.iss-heritage-container-width { .tpa-confirmation-container {
.tpa-confirmation-container { position: relative;
position: relative; min-height: 1px;
min-height: 1px; padding-left: .9375rem;
padding-left: .9375rem; padding-right: .9375rem;
padding-right: .9375rem;
}
}
.text-color--black { .header {
color: $black; margin-top: 1.25rem;
} margin-bottom: .625rem;
font-size: 1.25rem;
.tpa-body-one { color: $black;
:deep(p) { img {
line-height: map-get($spacers, 5); height: 2.25rem;
font-size: $h6-font-size; width: 2.25rem;
margin-right: .5rem;
strong {
color: $black;
} }
} }
.subheader {
margin-bottom: 1.25rem;
color: $black;
font-weight: $font-weight-bold;
}
:deep(p) {
margin-bottom: .625rem;
}
:deep(strong) {
color: $black;
font-weight: 600;
}
} }
</style> </style>

View file

@ -2898,10 +2898,11 @@ export const useMainStore = defineStore({
return JSON.parse(window.sessionStorage.getItem(webStorageConstants.SUBMITTED_ORDER)); return JSON.parse(window.sessionStorage.getItem(webStorageConstants.SUBMITTED_ORDER));
}, },
createSubmittedOrder(submitType) { async createSubmittedOrder(submitType) {
if (this.hasSubmittedOrder()) { if (this.hasSubmittedOrder()) {
return; return;
} }
await this.getCarrierAccountInfo();
const submittedOrder = this.order; const submittedOrder = this.order;
const { experiments } = this.applicationUser; const { experiments } = this.applicationUser;
const { issConfig } = this; const { issConfig } = this;

View file

@ -206,6 +206,18 @@ a.router-link {
text-decoration: none; text-decoration: none;
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
&:hover,
&:focus {
color: #125b7e;
text-decoration: underline;
}
}
a.phone-link {
color: $heritage-blue-primary;
text-decoration: none;
font-weight: $font-weight-normal;
&:hover, &:hover,
&:focus { &:focus {
color: #125b7e; color: #125b7e;