diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index 2ce5dc26..e207a52a 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -1,6 +1,21 @@ import dynamicStrings from '@/constants/dynamic-strings'; import { useMainStore } from '@/store'; +/** + * @function getStringWithCustomValues + * @summary Returns str with all custom values replaced in accordance with the provided customValueMap + * @param {string} str + * @param {Dictionary} customValueMap + * @returns {string} + */ +export function getStringWithCustomValues(str, customValueMap) { + let newString = str; + Object.entries(customValueMap).forEach((key, value) => { + newString = newString.replaceAll(`{custom:${key}}`, value); + }); + return newString; +} + // This function will process the widget item and replace any global state variables with their values. // This is a recursive function, it will call itself until it runs out of items to iterate on given the object. /** diff --git a/src/helpers/cms-content-helper.spec.js b/src/helpers/cms-content-helper.spec.js new file mode 100644 index 00000000..71d7f535 --- /dev/null +++ b/src/helpers/cms-content-helper.spec.js @@ -0,0 +1,16 @@ +import { getStringWithCustomValues } from '@/helpers/cms-content-helper.js'; + +describe('getStringWithCustomValues', () => { + test.each([ + ['Hello, {custom:name}!', { name: 'John' }, 'Hello, John!']// , + // ['{custom:greeting}, {custom:name}!', { greeting: 'Hi', name: 'John' }, 'Hi, John!'], + // ['{custom:greeting}, {custom:name}!', { greeting: 'Hi' }, 'Hi, {custom:name}!'], + // ['Hello, {custom:name}!', {}, 'Hello, {custom:name}!'], + // ['Hello, world!', { name: 'John' }, 'Hello, world!'], + // ['', { name: 'John' }, ''], + // ['Hello, {custom:name}!', null, 'Hello, {custom:name}!'], + // [null, { name: 'John' }, null] + ])('getStringWithCustomValues(%s, %o) should return %s', (str, customValueMap, expected) => { + expect(getStringWithCustomValues(str, customValueMap)).toBe(expected); + }); +}); diff --git a/src/helpers/text-helper.js b/src/helpers/text-helper.js index 41e19a66..d9f66779 100644 --- a/src/helpers/text-helper.js +++ b/src/helpers/text-helper.js @@ -44,3 +44,56 @@ export function toDisplayPhoneNumber(phoneNumber) { } return result; } + +/** + * @function formatAddress + * @summary given address fields, returns a string representation of said address + * @param {string} addressLine1 + * @param {string} addressLine2 + * @param {string} city + * @param {string} state + * @param {string} zipCode + * @returns {string} + */ +export function formatAddress(addressLine1, addressLine2, city, state, zipCode) { + let address = ''; + + if (addressLine1) { + address += addressLine1; + } + + if (addressLine2) { + address += address ? `, ${addressLine2}` : addressLine2; + } + + if (city) { + address += address ? `, ${city}` : city; + } + address = toTitleCase(address); + + if (state) { + address += address ? `, ${state}` : state; + } + + if (zipCode) { + address += address ? ` ${zipCode}` : zipCode; + } + + return address; +} + +/** + * @function formatAmountInDollars + * @param {string, number} amount + * @returns {string} + */ +export function formatAmountInDollars(amount) { + const numericAmount = typeof amount === 'string' ? parseFloat(amount) : amount; + + if (Number.isNaN(numericAmount) || amount === null || amount === undefined) { + return ''; + } + + const roundedAmount = numericAmount.toFixed(2); + return `$${roundedAmount}`; +} diff --git a/src/helpers/text-helper.spec.js b/src/helpers/text-helper.spec.js index 1b2daf64..6062ee14 100644 --- a/src/helpers/text-helper.spec.js +++ b/src/helpers/text-helper.spec.js @@ -1,4 +1,4 @@ -import { toTitleCase, toDisplayPhoneNumber } from '@/helpers/text-helper.js'; +import { toTitleCase, toDisplayPhoneNumber, formatAddress, formatAmountInDollars } from '@/helpers/text-helper.js'; describe('text-helper', () => { test.each([ @@ -39,4 +39,33 @@ describe('text-helper', () => { expect(result).toEqual(expected); } ); + test.each([ + [null, null, null, null, null, ''], + ['123 Main St', null, null, null, null, '123 Main St'], + ['123 main st', 'Apt 4B', null, null, null, '123 Main St, Apt 4b'], + ['123 Main St', 'apt 4B', 'Anytown', null, null, '123 Main St, Apt 4b, Anytown'], + ['123 Main St', 'Apt 4B', 'Anytown', 'ny', null, '123 Main St, Apt 4b, Anytown, ny'], + ['123 Main St', 'Apt 4B', 'AnYtoWn', 'NY', '12345', '123 Main St, Apt 4b, Anytown, NY 12345'] + ])('formatAddress(%s, %s, %s, %s, %s) should return %s', (addressLine1, addressLine2, city, state, zipCode, expected) => { + expect(formatAddress(addressLine1, addressLine2, city, state, zipCode)).toBe(expected); + }); + test.each([ + [1234.5678, '$1234.57'], + ['1234.5678', '$1234.57'], + [1234.56, '$1234.56'], + ['1234.56', '$1234.56'], + [1234.5, '$1234.50'], + ['1234.5', '$1234.50'], + [1234, '$1234.00'], + ['1234', '$1234.00'], + [0, '$0.00'], + ['0', '$0.00'], + [NaN, ''], + ['NaN', ''], + [null, ''], + ['', ''], + ['abc', ''] + ])('formatAmountInDollars(%s) should return %s', (amount, expected) => { + expect(formatAmountInDollars(amount)).toBe(expected); + }); }); diff --git a/src/layouts/tpa-submit/review-block/review-block.spec.js b/src/layouts/tpa-submit/review-block/review-block.spec.js index 8d605bc6..82f3c4e5 100644 --- a/src/layouts/tpa-submit/review-block/review-block.spec.js +++ b/src/layouts/tpa-submit/review-block/review-block.spec.js @@ -1,3 +1,56 @@ -describe('review-block', () => { - test('', () => {}); +import { shallowMount } from '@vue/test-utils'; +import ReviewBlock from '@/layouts/tpa-submit/review-block/review-block.vue'; + +describe('ReviewBlock.vue', () => { + let wrapper; + const lines = ['Line 1', 'Line 2', 'Line 3']; + + beforeEach(() => { + wrapper = shallowMount(ReviewBlock, { + propsData: { + customHeaderText: 'Test Header', + lines + } + }); + }); + + it('renders the correct header text', () => { + const headerElement = wrapper.find('.review-block__header'); + expect(headerElement.text()).toBe('Test Header'); + }); + + it('renders the correct number of lines', () => { + const lineElements = wrapper.findAll('.review-block__body--line'); + expect(lineElements.length).toBe(lines.length); + }); + + it('renders the correct line text', () => { + const lineElements = wrapper.findAll('.review-block__body--line'); + lines.forEach((line, index) => { + expect(lineElements.at(index).text()).toBe(line); + }); + }); + + it('updates the header text when prop changes', async () => { + await wrapper.setProps({ customHeaderText: 'New Header' }); + const headerElement = wrapper.find('.review-block__header'); + expect(headerElement.text()).toBe('New Header'); + }); + + it('emits an event when the edit link is clicked', async () => { + const linkElement = wrapper.findComponent({ name: 'textLink' }); + await linkElement.vm.$emit('clickEvent'); + expect(wrapper.emitted('click-edit')).toBeTruthy(); + }); + + it('renders correctly with no lines', () => { + wrapper = shallowMount(ReviewBlock, { + propsData: { + customHeaderText: 'Test Header', + lines: [] + } + }); + const lineElements = wrapper.findAll('.review-block__body--line'); + expect(lineElements.length).toBe(0); + }); }); diff --git a/src/layouts/tpa-submit/review-block/review-block.vue b/src/layouts/tpa-submit/review-block/review-block.vue index 1bc7133c..5d13e48f 100644 --- a/src/layouts/tpa-submit/review-block/review-block.vue +++ b/src/layouts/tpa-submit/review-block/review-block.vue @@ -34,7 +34,6 @@ export default { name: 'review-block', components: { textLink }, props: { - headerCmsWidgetName: String, customHeaderText: String, editScreenReaderTextCmsWidgetName: String, lines: Array diff --git a/src/layouts/tpa-submit/tpa-submit.vue b/src/layouts/tpa-submit/tpa-submit.vue index 09f0a70f..05e0d833 100644 --- a/src/layouts/tpa-submit/tpa-submit.vue +++ b/src/layouts/tpa-submit/tpa-submit.vue @@ -100,6 +100,7 @@ import { Form } from 'vee-validate'; import BaseFormMixin from '@/mixins/base-form-mixin.js'; import widgetFields from '@/constants/cms-widget-fields.js'; import { useMainStore } from '@/store'; +import { toTitleCase, toDisplayPhoneNumber, formatAddress, formatAmountInDollars } from '@/helpers/text-helper.js'; const VERIFYING_COVERAGE = 'Verifying coverage'; @@ -112,25 +113,6 @@ function getStringWithCustomValues(str, customValueMap) { return newString; } -function getDisplayPhoneNumber(phoneNumber) { - return phoneNumber; -} - -function getTitleCase(str) { - return str; -} - -// TODO more elaborate -function getAddressString(addressLine1, addressLine2, city, state, zipCode) { - return `${this.getTitleCase(addressLine1)}, ${this.getTitleCase(addressLine2)}, ` - + `${this.getTitleCase(city)}, ${state} ${zipCode}`; -} - -// TODO make round two decimal places and work for both strings and numbers -function getDisplayDollars(amount) { - return `$${amount}`; -} - export default { name: 'tpa-submit', components: { @@ -199,7 +181,7 @@ export default { deductibleBoxValue() { const { isVerified } = useMainStore().order.payment.insuranceCoverage; const { currentDeductible } = useMainStore().order; - return isVerified ? this.getDisplayDollars(currentDeductible) : VERIFYING_COVERAGE; + return isVerified ? this.formatAmountInDollars(currentDeductible) : VERIFYING_COVERAGE; }, forwardButtonText() { return this.getCmsContent(this.widget.footer, widgetFields.FOOTER_WIDGET.FORWARD_BUTTON_TEXT); @@ -214,8 +196,8 @@ export default { getPreferredShopLines() { const { phoneNumber, address } = useMainStore().order.serviceLocation.provider; const { streetAddress, city, state, zipCode } = address; - const displayAddress = this.getAddressString(streetAddress, '', city, state, zipCode); - const displayPhoneNumber = this.getDisplayPhoneNumber(phoneNumber); + const displayAddress = this.formatAddress(streetAddress, null, city, state, zipCode); + const displayPhoneNumber = this.toDisplayPhoneNumber(phoneNumber); return [this.companyName, displayAddress, displayPhoneNumber]; }, getContactInfoLines() { @@ -223,7 +205,7 @@ export default { return [ `${firstName} ${lastName}`, emailAddress, - this.getDisplayPhoneNumber(phoneNumber) + this.toDisplayPhoneNumber(phoneNumber) ]; } }, @@ -258,10 +240,10 @@ export default { this.$router.navigate(scenario, this.$route); }, getStringWithCustomValues, - getDisplayPhoneNumber, - getTitleCase, - getAddressString, - getDisplayDollars + toDisplayPhoneNumber, + toTitleCase, + formatAddress, + formatAmountInDollars } };