Merge pull request #493 from Safelite/feature/digital/SSR-754
Feature/digital/ssr 754
This commit is contained in:
commit
ecd71c451f
24 changed files with 3779 additions and 236 deletions
16
package-lock.json
generated
16
package-lock.json
generated
|
|
@ -43,6 +43,7 @@
|
|||
"eslint-plugin-vue": "^9.15.1",
|
||||
"jest": "^27.0.5",
|
||||
"jest-junit": "^13.0.0",
|
||||
"jest-serializer-vue": "^3.1.0",
|
||||
"jsdoc": "^4.0.2",
|
||||
"jsdom": "^22.1.0",
|
||||
"sass": "^1.32.7",
|
||||
|
|
@ -4413,6 +4414,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/cli-plugin-unit-jest/node_modules/jest-serializer-vue": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-serializer-vue/-/jest-serializer-vue-2.0.2.tgz",
|
||||
"integrity": "sha512-nK/YIFo6qe3i9Ge+hr3h4PpRehuPPGZFt8LDBdTHYldMb7ZWlkanZS8Ls7D8h6qmQP2lBQVDLP0DKn5bJ9QApQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pretty": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/cli-plugin-vuex": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.8.tgz",
|
||||
|
|
@ -12245,9 +12255,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jest-serializer-vue": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-serializer-vue/-/jest-serializer-vue-2.0.2.tgz",
|
||||
"integrity": "sha512-nK/YIFo6qe3i9Ge+hr3h4PpRehuPPGZFt8LDBdTHYldMb7ZWlkanZS8Ls7D8h6qmQP2lBQVDLP0DKn5bJ9QApQ==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-serializer-vue/-/jest-serializer-vue-3.1.0.tgz",
|
||||
"integrity": "sha512-vXz9/3IgBbLhsaVANYLG4ROCQd+Wg3qbB6ICofzFL+fbhSFPlqb0/MMGXcueVsjaovdWlYiRaLQLpdi1PTcoRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pretty": "2.0.0"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"test:unit": "vue-cli-service test:unit --coverage --ci",
|
||||
"test:unit": "vue-cli-service test:unit --coverage --ci --colors",
|
||||
"test:unit:lite": "vue-cli-service test:unit --ci"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -50,6 +50,7 @@
|
|||
"eslint-plugin-vue": "^9.15.1",
|
||||
"jest": "^27.0.5",
|
||||
"jest-junit": "^13.0.0",
|
||||
"jest-serializer-vue": "^3.1.0",
|
||||
"jsdoc": "^4.0.2",
|
||||
"jsdom": "^22.1.0",
|
||||
"sass": "^1.32.7",
|
||||
|
|
|
|||
45
src/constants/cms-widget-fields.js
Normal file
45
src/constants/cms-widget-fields.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
const widgetFields = Object.freeze({
|
||||
HEADER_WIDGET: {
|
||||
NAME: 'Name',
|
||||
ANSWERS: 'Answers'
|
||||
},
|
||||
SUB_HEADER_WIDGET: {
|
||||
NAME: 'Name',
|
||||
SUB_HEADER_TEXT: 'SubHeaderText',
|
||||
SECONDARY_TEXT: 'SecondaryText',
|
||||
BACK_BUTTON_ACCESSIBLE_TEXT: 'BackButtonAccessibleText'
|
||||
},
|
||||
INPUT_QUESTION_WIDGET: {
|
||||
NAME: 'Name',
|
||||
QUESTION_TEXT: 'QuestionText',
|
||||
BUTTON_TEXT: 'ButtonText',
|
||||
ANSWERS: 'Answers'
|
||||
},
|
||||
TEXT_BLOCK_WIDGET: {
|
||||
NAME: 'Name',
|
||||
TEXT: 'Text'
|
||||
},
|
||||
CONTENT_GROUP_WIDGET: {
|
||||
NAME: 'Name',
|
||||
HEADER_TEXT: 'HeaderText',
|
||||
SUBHEADER_TEXT: 'SubheaderText',
|
||||
BODY_TEXT: 'BodyText',
|
||||
BODY_TEXT_2: 'BodyText2',
|
||||
FOOTER_TEXT: 'FooterText',
|
||||
IMAGE: 'Image'
|
||||
},
|
||||
ALERT_WIDGET: {
|
||||
NAME: 'Name',
|
||||
HEADLINE_TEXT: 'HeadlineText',
|
||||
BODY_TEXT: 'BodyText'
|
||||
},
|
||||
FOOTER_WIDGET: {
|
||||
NAME: 'Name',
|
||||
BACK_BUTTON_TEXT: 'BackButtonText',
|
||||
FORWARD_BUTTON_TEXT: 'ForwardButtonText',
|
||||
FOOTER_IMAGE: 'FooterImage',
|
||||
ALT_TEXT: 'AltText'
|
||||
}
|
||||
});
|
||||
|
||||
export default widgetFields;
|
||||
|
|
@ -13,6 +13,8 @@ const globalRules = Object.freeze({
|
|||
EMAIL_ADDRESS_FORMAT: 'email-address-format',
|
||||
PHONE_NUMBER_REQUIRED: 'phone-number-required',
|
||||
PHONE_NUMBER_FORMAT: 'phone-number-format',
|
||||
ZIP_CODE_REQUIRED: 'zip-code-required',
|
||||
ZIP_CODE_SEARCH_FORMAT: 'zip-code-search-format',
|
||||
OPTION_REQUIRED: 'option-required'
|
||||
});
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -30,7 +30,7 @@
|
|||
:aria-label="questionText"
|
||||
:min="min"
|
||||
:max="max"
|
||||
required
|
||||
:required="isRequired"
|
||||
:class="[
|
||||
hasIcon ? 'has-icon' : '',
|
||||
iconRight ? 'icon-right' : '',
|
||||
|
|
@ -48,7 +48,8 @@
|
|||
<button
|
||||
v-if="includeSearchIcon"
|
||||
type="submit"
|
||||
aria-label="Search button" />
|
||||
aria-label="Search button"
|
||||
@click="clickedSearch" />
|
||||
<button
|
||||
v-if="includeSelectIcon"
|
||||
type="submit"
|
||||
|
|
@ -58,6 +59,7 @@
|
|||
</div>
|
||||
<div
|
||||
v-show="errorMessage"
|
||||
ref="errorMessageDiv"
|
||||
class="row my-1 form-test-error">
|
||||
<span
|
||||
class="d-inline-flex mt-0"
|
||||
|
|
@ -99,7 +101,10 @@ export default {
|
|||
default: ''
|
||||
},
|
||||
validationRules: String,
|
||||
cmsWidgetName: String,
|
||||
cmsWidgetName: {
|
||||
String,
|
||||
default: ''
|
||||
},
|
||||
maxLength: String,
|
||||
questionAlignment: String, // Left or center. Left is default.
|
||||
cornerStyle: String, // Rounded or square. Square is default.
|
||||
|
|
@ -109,7 +114,7 @@ export default {
|
|||
max: String,
|
||||
disableAutoFill: Boolean
|
||||
},
|
||||
emits: ['focus', 'update:modelValue', 'textboxQuestionEvent.inputIdAssigned'],
|
||||
emits: ['focus', 'update:modelValue', 'textboxQuestionEvent.inputIdAssigned', 'click-event'],
|
||||
setup(props) {
|
||||
const propsClone = { ...props };
|
||||
const { modelValue } = propsClone;
|
||||
|
|
@ -217,12 +222,18 @@ export default {
|
|||
const blendedString = arrOriginalString.join('');
|
||||
|
||||
this.$emit('update:modelValue', blendedString);
|
||||
},
|
||||
clickedSearch() {
|
||||
if (this.meta.valid) {
|
||||
this.$emit('click-event');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/ux-variables-svg-strings.scss";
|
||||
input[type='date']::-webkit-inner-spin-button {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -232,7 +243,7 @@ input[type='date']::-webkit-calendar-picker-indicator {
|
|||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 1px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='%23167CAC'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='%23167CAC'/%3E%3C/svg%3E");
|
||||
background-image: url($svg-calendar-picker);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
width: 16px; // check this
|
||||
|
|
@ -267,17 +278,17 @@ input[type='date']::-webkit-calendar-picker-indicator {
|
|||
position: relative;
|
||||
&.has-search-icon {
|
||||
input[type='text'] {
|
||||
border-radius: 50rem;
|
||||
border-radius: $border-radius-lg;
|
||||
}
|
||||
button[type='submit'] {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 0;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.7817 14.7328L11.8252 10.7762C12.8833 9.45005 13.3936 7.76911 13.2513 6.07849C13.1091 4.38788 12.325 2.81587 11.0601 1.6852C9.79515 0.554524 8.14538 -0.0490261 6.44946 -0.00154744C4.75353 0.0459312 3.14012 0.740836 1.94045 1.94051C0.740775 3.14018 0.0458701 4.75359 -0.00160848 6.44952C-0.0490871 8.14545 0.554463 9.79521 1.68514 11.0601C2.81581 12.325 4.38782 13.1091 6.07843 13.2514C7.76905 13.3937 9.44999 12.8834 10.7762 11.8252L14.7349 15.7839C14.8044 15.8527 14.8869 15.907 14.9774 15.9439C15.068 15.9808 15.165 15.9995 15.2628 15.9989C15.3606 15.9983 15.4573 15.9784 15.5475 15.9405C15.6376 15.9025 15.7194 15.8471 15.7881 15.7776C15.8568 15.708 15.9112 15.6256 15.9481 15.535C15.985 15.4444 16.0036 15.3474 16.0031 15.2496C16.0025 15.1518 15.9826 15.0551 15.9446 14.965C15.9067 14.8748 15.8513 14.7931 15.7817 14.7243V14.7328ZM6.63737 11.7913C5.61803 11.7913 4.62157 11.4891 3.77402 10.9228C2.92646 10.3564 2.26587 9.5515 1.87578 8.60975C1.4857 7.668 1.38363 6.63172 1.5825 5.63196C1.78136 4.6322 2.27222 3.71386 2.99301 2.99307C3.7138 2.27229 4.63214 1.78142 5.6319 1.58256C6.63166 1.38369 7.66793 1.48576 8.60969 1.87585C9.55144 2.26593 10.3564 2.92652 10.9227 3.77408C11.489 4.62163 11.7913 5.61809 11.7913 6.63743C11.7896 8.00382 11.2461 9.31376 10.2799 10.2799C9.3137 11.2461 8.00376 11.7897 6.63737 11.7913Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
|
||||
background-image: url($svg-search-icon);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
border-radius: 0 50rem 50rem 0;
|
||||
border-radius: 0 $border-radius-lg $border-radius-lg 0;
|
||||
background-color: $blue-100;
|
||||
width: 2.75rem;
|
||||
height: 100%;
|
||||
|
|
@ -292,7 +303,7 @@ input[type='date']::-webkit-calendar-picker-indicator {
|
|||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 1rem;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%231474a2'/%3e%3c/svg%3e");
|
||||
background-image: url($svg-select-icon);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-color: transparent;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,14 @@ function defineGlobalPhoneNumberRules() {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Define global rules related to zip codes
|
||||
*/
|
||||
function defineGlobalZipCodeRules() {
|
||||
defineRule(globalRules.ZIP_CODE_REQUIRED, required(errorMessages.ZIP_REQUIRED));
|
||||
defineRule(globalRules.ZIP_CODE_SEARCH_FORMAT, regex(/^\d{5}$/, errorMessages.ZIP_FORMAT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @function defineGlobalRules
|
||||
* @summary Define all global rules
|
||||
|
|
@ -55,6 +63,7 @@ export default function defineGlobalRules() {
|
|||
defineGlobalNameRules();
|
||||
defineGlobalEmailRules();
|
||||
defineGlobalPhoneNumberRules();
|
||||
defineGlobalZipCodeRules();
|
||||
|
||||
defineRule(globalRules.OPTION_REQUIRED, required(errorMessages.OPTION_REQUIRED));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Shop list button should render correctly with all relevant props 1`] = `
|
||||
<transition-stub name="fade" mode="out-in" appear="false" persisted="false" css="true" selectedvalue="selected value">
|
||||
<base-input-button-stub modelvalue="value of modal" groupname="name of group" buttonwrapperclasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2" ismultiselect="false" validationrules="" isrequired="true" selectinginitiatesload="false" suppresserror="false" buttonlabel="Label of button" buttonlabelsubcopy="Sub copy of button" buttonbodycopy="button body copy" screenreaderonlytext="screen reader only text" additionalbuttondata="[object Object]" alttext="" iswide="false" value="1234"></base-input-button-stub>
|
||||
</transition-stub>
|
||||
`;
|
||||
|
||||
exports[`Shop list button should render correctly with required props 1`] = `
|
||||
<transition-stub name="fade" mode="out-in" appear="false" persisted="false" css="true">
|
||||
<base-input-button-stub modelvalue="value of modal" groupname="name of group" buttonwrapperclasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2" ismultiselect="false" validationrules="" isrequired="true" selectinginitiatesload="false" suppresserror="false" alttext="" iswide="false" value="1234"></base-input-button-stub>
|
||||
</transition-stub>
|
||||
`;
|
||||
|
||||
exports[`Shop list button should render expected data with all relevant props 1`] = `
|
||||
Object {
|
||||
"availabilityRating": null,
|
||||
"displayAvailabilityIndicators": true,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Shop list button should render expected data with only required props 1`] = `
|
||||
Object {
|
||||
"availabilityRating": null,
|
||||
"displayAvailabilityIndicators": false,
|
||||
}
|
||||
`;
|
||||
893
src/iss-components/shop-list-button/shop-list-button.spec.js
Normal file
893
src/iss-components/shop-list-button/shop-list-button.spec.js
Normal file
|
|
@ -0,0 +1,893 @@
|
|||
import { shallowMount, mount } from '@vue/test-utils';
|
||||
import inputButtonWrapperMixin from '@/mixins/input-button-wrapper-mixin';
|
||||
import shopListButton from '@/iss-components/shop-list-button/shop-list-button.vue';
|
||||
|
||||
const buttonLabelReference = 'span[id="buttonLabelSpan"]';
|
||||
const buttonLabelCopyReference = 'span[id="buttonLabelSubCopySpan"]';
|
||||
const availabilityIndicatorBlockReference = 'div[id="availabilityIndicatorBlock"]';
|
||||
const loaderReference = { ref: 'availabilityIndicatorLoader' };
|
||||
const availabilityBadgeReference = 'div[id="availabilityBadge"]';
|
||||
const buttonBodyCopyReference = 'span[id="buttonBodyCopy"]';
|
||||
const screenReaderOnlySpanReference = '#screenReaderOnlyTextSpan';
|
||||
|
||||
describe('Shop list button', () => {
|
||||
describe('should render', () => {
|
||||
test('correctly with required props', async () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group'
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
test('correctly with all relevant props', async () => {
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(null)
|
||||
};
|
||||
const wrapper = shallowMount(shopListButton, {
|
||||
propsData: {
|
||||
buttonLabel: 'Label of button',
|
||||
buttonLabelSubCopy: 'Sub copy of button',
|
||||
selectedValue: 'selected value',
|
||||
buttonBodyCopy: 'button body copy',
|
||||
screenReaderOnlyText: 'screen reader only text',
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
test('expected data with only required props', async () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group'
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$data).toMatchSnapshot();
|
||||
});
|
||||
test('expected data with all relevant props', async () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
additionalButtonData: {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(null)
|
||||
}
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$data).toMatchSnapshot();
|
||||
});
|
||||
describe('button label with expected classes', () => {
|
||||
test('when "textPosition" prop provided', async () => {
|
||||
// Arrange
|
||||
const textPosition = 'positionOfText';
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
textPosition
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const buttonLabel = wrapper.find(buttonLabelReference);
|
||||
|
||||
// Assert
|
||||
expect(buttonLabel.exists()).toBeTruthy();
|
||||
expect(buttonLabel.classes()).toContain(textPosition);
|
||||
});
|
||||
test('when "textPosition" prop not provided', async () => {
|
||||
// Arrange
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button'
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const buttonLabel = wrapper.find(buttonLabelReference);
|
||||
|
||||
// Assert
|
||||
expect(buttonLabel.exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
describe('button label sub copy with expected classes', () => {
|
||||
test('when "textPosition" prop provided', async () => {
|
||||
// Arrange
|
||||
const textPosition = 'positionOfText';
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
textPosition
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const buttonLabelSubCopy = wrapper.find(buttonLabelCopyReference);
|
||||
|
||||
// Assert
|
||||
expect(buttonLabelSubCopy.exists()).toBeTruthy();
|
||||
expect(buttonLabelSubCopy.classes().length).toBe(4);
|
||||
expect(buttonLabelSubCopy.classes()).toContain('m-0');
|
||||
expect(buttonLabelSubCopy.classes()).toContain('caption');
|
||||
expect(buttonLabelSubCopy.classes()).toContain('ms-1');
|
||||
expect(buttonLabelSubCopy.classes()).toContain(textPosition);
|
||||
});
|
||||
test('when "textPosition" prop not provided', async () => {
|
||||
// Arrange
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button'
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const buttonLabelSubCopy = wrapper.find(buttonLabelCopyReference);
|
||||
|
||||
// Assert
|
||||
expect(buttonLabelSubCopy.exists()).toBeTruthy();
|
||||
expect(buttonLabelSubCopy.classes().length).toBe(3);
|
||||
expect(buttonLabelSubCopy.classes()).toContain('m-0');
|
||||
expect(buttonLabelSubCopy.classes()).toContain('caption');
|
||||
expect(buttonLabelSubCopy.classes()).toContain('ms-1');
|
||||
});
|
||||
});
|
||||
describe('availability indicator block with expected when displayAvailabilityIndicators true', () => {
|
||||
test('and availability rating is high', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve('high')
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const availabilityIndicatorBlock = wrapper.find(availabilityIndicatorBlockReference);
|
||||
|
||||
// Assert
|
||||
expect(availabilityIndicatorBlock.exists()).toBeTruthy();
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('availability-indicator');
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('rounded-pill');
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('green');
|
||||
});
|
||||
test.each(['low', ''])(
|
||||
'and availability rating is not high or null',
|
||||
async (availabilityBadge) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityBadge)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const availabilityIndicatorBlock = wrapper.find(availabilityIndicatorBlockReference);
|
||||
|
||||
// Assert
|
||||
expect(availabilityIndicatorBlock.exists()).toBeTruthy();
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('availability-indicator');
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('rounded-pill');
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('orange');
|
||||
}
|
||||
);
|
||||
test.each([null, undefined])(
|
||||
'and availability rating is null',
|
||||
async (availabilityRating) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const availabilityIndicatorBlock = wrapper.find(availabilityIndicatorBlockReference);
|
||||
|
||||
// Assert
|
||||
expect(availabilityIndicatorBlock.exists()).toBeTruthy();
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('availability-indicator');
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('rounded-pill');
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('gray');
|
||||
}
|
||||
);
|
||||
});
|
||||
test('availability indicator block with expected classes when displayAvailabilityIndicators true', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve({})
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const availabilityIndicatorBlock = wrapper.find(availabilityIndicatorBlockReference);
|
||||
|
||||
// Assert
|
||||
expect(availabilityIndicatorBlock.exists()).toBeTruthy();
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('availability-indicator');
|
||||
expect(availabilityIndicatorBlock.classes()).toContain('rounded-pill');
|
||||
});
|
||||
test('availability indicator loader when displayAvailabilityIndicators', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(null)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const availabilityIndicatorLoader = wrapper.findComponent(loaderReference);
|
||||
|
||||
// Assert
|
||||
expect(availabilityIndicatorLoader.exists()).toBeTruthy();
|
||||
expect(availabilityIndicatorLoader.props().loaderPosition).toBe('left');
|
||||
expect(availabilityIndicatorLoader.attributes().allowpageinteraction).toBe('true');
|
||||
});
|
||||
describe('availability badge when displayAvailabilityIndicators true', () => {
|
||||
test('and "availabilityRating" is "high"', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve('high')
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const availabilityBadge = wrapper.find(availabilityBadgeReference);
|
||||
|
||||
// Assert
|
||||
expect(availabilityBadge.exists()).toBeTruthy();
|
||||
expect(availabilityBadge.classes().length).toBe(2);
|
||||
expect(availabilityBadge.classes()).toContain('availability-badge');
|
||||
expect(availabilityBadge.classes()).toContain('green');
|
||||
});
|
||||
test.each(['low', ''])(
|
||||
'and "availabilityRating" is not "high" or null',
|
||||
async (availabilityRating) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const availabilityBadge = wrapper.find(availabilityBadgeReference);
|
||||
|
||||
// Assert
|
||||
expect(availabilityBadge.exists()).toBeTruthy();
|
||||
expect(availabilityBadge.classes().length).toBe(2);
|
||||
expect(availabilityBadge.classes()).toContain('availability-badge');
|
||||
expect(availabilityBadge.classes()).toContain('orange');
|
||||
}
|
||||
);
|
||||
});
|
||||
test('button body copy when buttonBodyCopy provided', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve({})
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData,
|
||||
buttonBodyCopy: 'body copy'
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const buttonBodyCopy = wrapper.find(buttonBodyCopyReference);
|
||||
|
||||
// Assert
|
||||
expect(buttonBodyCopy.exists()).toBeTruthy();
|
||||
expect(buttonBodyCopy.classes()).toContain('m-0');
|
||||
expect(buttonBodyCopy.classes()).toContain('button-label-sub-copy');
|
||||
expect(buttonBodyCopy.classes()).toContain('small');
|
||||
});
|
||||
test('screen reader only text when screenReaderOnlyText provided', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve({})
|
||||
};
|
||||
const screenReaderOnlyText = 'text just for screen reader';
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData,
|
||||
screenReaderOnlyText
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const screenReaderOnlySpan = wrapper.find(screenReaderOnlySpanReference);
|
||||
|
||||
// Assert
|
||||
expect(screenReaderOnlySpan.exists()).toBeTruthy();
|
||||
expect(screenReaderOnlySpan.classes()).toContain('sr-only');
|
||||
expect(screenReaderOnlySpan.text()).toContain(screenReaderOnlyText);
|
||||
});
|
||||
});
|
||||
describe('should not render', () => {
|
||||
test.each([false, null, undefined])(
|
||||
'availability indicator block when displayAvailabilityIndicators falsy',
|
||||
async (displayAvailabilityIndicators) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators,
|
||||
availabilityRatingCallback: () => Promise.resolve({})
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const availabilityIndicatorBlock = wrapper.find(availabilityIndicatorBlockReference);
|
||||
|
||||
// Assert
|
||||
expect(availabilityIndicatorBlock.exists()).toBeFalsy();
|
||||
}
|
||||
);
|
||||
test.each([null, undefined])(
|
||||
'availability badge when displayAvailabilityIndicators true and "availabilityRating" is null',
|
||||
async (availabilityRating) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const availabilityBadge = wrapper.find(availabilityBadgeReference);
|
||||
|
||||
// Assert
|
||||
expect(availabilityBadge.exists()).toBeFalsy();
|
||||
}
|
||||
);
|
||||
test('button body copy when buttonBodyCopy not provided', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve({})
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const buttonBodyCopy = wrapper.find(buttonBodyCopyReference);
|
||||
|
||||
// Assert
|
||||
expect(buttonBodyCopy.exists()).toBeFalsy();
|
||||
});
|
||||
test('screen reader only text when screenReaderOnlyText not provided', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve({})
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const screenReaderOnlySpan = wrapper.find(screenReaderOnlySpanReference);
|
||||
|
||||
// Assert
|
||||
expect(screenReaderOnlySpan.exists()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
describe('computed', () => {
|
||||
describe('isLoaderDisplayed', () => {
|
||||
test.each([null, undefined])(
|
||||
'should return true when availabilityRating is null or undefined',
|
||||
async (availabilityRating) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.isLoaderDisplayed;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeTruthy();
|
||||
}
|
||||
);
|
||||
test('should return false when availabilityRating is empty string', async () => {
|
||||
// Arrange
|
||||
const availabilityRating = '';
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.isLoaderDisplayed;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
test('should return false when availabilityRating is non-empty string', async () => {
|
||||
// Arrange
|
||||
const availabilityRating = 'not empty string';
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.isLoaderDisplayed;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
describe('availabilityRatingClass', () => {
|
||||
test.each([null, undefined])(
|
||||
'returns "gray" if availability rating null or undefined',
|
||||
async (availabilityRating) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
const expected = 'gray';
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.availabilityRatingClass;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
}
|
||||
);
|
||||
test('returns green if availability rating is "high"', async () => {
|
||||
// Arrange
|
||||
const availabilityRating = 'high';
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
const expected = 'green';
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.availabilityRatingClass;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
test.each(['', 'not high'])(
|
||||
'returns orange if availability not "high"',
|
||||
async (availabilityRating) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
const expected = 'orange';
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.availabilityRatingClass;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
}
|
||||
);
|
||||
});
|
||||
describe('badgeText', () => {
|
||||
test.each([null, undefined])(
|
||||
'returns empty string if availability rating null or undefined',
|
||||
async (availabilityRating) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
const expected = '';
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.badgeText;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
}
|
||||
);
|
||||
test('returns "Appts available" if availability rating "high"', async () => {
|
||||
// Arrange
|
||||
const availabilityRating = 'high';
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
const expected = 'Appts available';
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.badgeText;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
test.each(['', 'not high'])(
|
||||
'returns "Appts low" if availability rating not null or "high"',
|
||||
async (availabilityRating) => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve(availabilityRating)
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
const expected = 'Appts low';
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.badgeText;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
}
|
||||
);
|
||||
});
|
||||
describe('selectedValue', () => {
|
||||
test('"get" returns modalValue prop', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve({})
|
||||
};
|
||||
const modelValue = 'value of modal';
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue,
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.selectedValue;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(modelValue);
|
||||
});
|
||||
test('"set" emits "update:modalValue" event', async () => {
|
||||
// Arrange
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: () => Promise.resolve({})
|
||||
};
|
||||
const modelValue = 'value of modal';
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue,
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
const newValue = 'new value';
|
||||
|
||||
// Act
|
||||
wrapper.vm.selectedValue = newValue;
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()['update:modelValue'][0][0]).toEqual(newValue);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('before mount', () => {
|
||||
test('should call availabilityRatingCallback with expected data if displayAvailabilityIndicators true', async () => {
|
||||
// Arrange
|
||||
const availabilityRating = 'rating';
|
||||
const availabilityRatingCallback = jest.fn().mockImplementation(() => Promise.resolve(availabilityRating));
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(availabilityRatingCallback).toBeCalledTimes(1);
|
||||
});
|
||||
test('should not call availabilityRatingCallback if displayAvailabilityIndicators false', async () => {
|
||||
// Arrange
|
||||
const availabilityRating = 'rating';
|
||||
const availabilityRatingCallback = jest.fn().mockImplementation(() => Promise.resolve(availabilityRating));
|
||||
const additionalButtonData = {
|
||||
displayAvailabilityIndicators: false,
|
||||
availabilityRatingCallback
|
||||
};
|
||||
const wrapper = mount(shopListButton, {
|
||||
propsData: {
|
||||
value: 1234,
|
||||
modelValue: 'value of modal',
|
||||
groupName: 'name of group',
|
||||
buttonLabel: 'label of button',
|
||||
additionalButtonData
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin]
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(availabilityRatingCallback).toBeCalledTimes(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,55 +1,62 @@
|
|||
<template>
|
||||
<transition
|
||||
name="fade"
|
||||
mode="out-in">
|
||||
<baseInputButton
|
||||
v-bind="$props"
|
||||
v-model="selectedValue"
|
||||
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2">
|
||||
<div
|
||||
:aria-label="buttonLabel"
|
||||
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
||||
<div class="row-one">
|
||||
<span
|
||||
class="m-0 button-label-copy"
|
||||
:class="textPosition">{{
|
||||
buttonLabel
|
||||
}}</span>
|
||||
<span
|
||||
class="m-0 caption ms-1"
|
||||
:class="textPosition">{{
|
||||
buttonLabelSubCopy
|
||||
}}</span>
|
||||
<div
|
||||
<transition
|
||||
name="fade"
|
||||
mode="out-in">
|
||||
<baseInputButton
|
||||
v-bind="$props"
|
||||
v-model="selectedValue"
|
||||
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2">
|
||||
<div
|
||||
:aria-label="buttonLabel"
|
||||
class="button-content list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
||||
<div class="row-one">
|
||||
<span
|
||||
id="buttonLabelSpan"
|
||||
class="m-0 button-label-copy"
|
||||
:class="textPosition">{{ buttonLabel }}
|
||||
</span>
|
||||
<span
|
||||
id="buttonLabelSubCopySpan"
|
||||
class="m-0 caption ms-1"
|
||||
:class="textPosition">{{ buttonLabelSubCopy }}
|
||||
</span>
|
||||
<div
|
||||
v-if="displayAvailabilityIndicators"
|
||||
id="availabilityIndicatorBlock"
|
||||
class="availability-indicator rounded-pill"
|
||||
:class="availabilityRatingClass">
|
||||
<div
|
||||
v-if="!isLoaderDisplayed"
|
||||
class="availability-badge"
|
||||
:class="availabilityRating == 'high' ? 'green' : 'orange'"></div>
|
||||
<span
|
||||
v-if="!isLoaderDisplayed"
|
||||
class="m-0 button-auxillary-copy">{{ badgeText }}</span>
|
||||
<loader
|
||||
v-if="isLoaderDisplayed"
|
||||
ref="availabilityIndicatorLoader"
|
||||
loaderPosition="left"
|
||||
:allowPageInteraction="true" />
|
||||
<div
|
||||
v-else
|
||||
id="availabilityIndicator">
|
||||
<div
|
||||
id="availabilityBadge"
|
||||
class="availability-badge"
|
||||
:class="availabilityRating == 'high' ? 'green' : 'orange'">
|
||||
</div>
|
||||
<span class="m-0 button-auxillary-copy">{{ badgeText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-two">
|
||||
<span
|
||||
v-if="buttonBodyCopy"
|
||||
class="m-0 button-label-sub-copy small"
|
||||
v-html="buttonBodyCopy"></span>
|
||||
</div>
|
||||
<span
|
||||
v-if="screenReaderOnlyText"
|
||||
class="sr-only">
|
||||
{{ screenReaderOnlyText }}
|
||||
</span>
|
||||
</div>
|
||||
</baseInputButton>
|
||||
</div>
|
||||
<div class="row-two">
|
||||
<span
|
||||
v-if="buttonBodyCopy"
|
||||
id="buttonBodyCopy"
|
||||
class="m-0 button-label-sub-copy small"
|
||||
v-html="buttonBodyCopy"></span>
|
||||
</div>
|
||||
<span
|
||||
v-if="screenReaderOnlyText"
|
||||
id="screenReaderOnlyTextSpan"
|
||||
class="sr-only">
|
||||
{{ screenReaderOnlyText }}
|
||||
</span>
|
||||
</div>
|
||||
</baseInputButton>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
|
|
@ -67,13 +74,11 @@ export default {
|
|||
mixins: [inputButtonWrapperMixin],
|
||||
data() {
|
||||
return {
|
||||
availabilityRating: null
|
||||
availabilityRating: null,
|
||||
displayAvailabilityIndicators: this.additionalButtonData?.displayAvailabilityIndicators ?? false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
displayAvailabilityIndicators() {
|
||||
return true;
|
||||
},
|
||||
isLoaderDisplayed() {
|
||||
return this.availabilityRating == null;
|
||||
},
|
||||
|
|
@ -102,8 +107,7 @@ export default {
|
|||
this.availabilityRating = data;
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: { }
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -182,7 +186,7 @@ export default {
|
|||
margin: 0 0.25rem 0 0;
|
||||
|
||||
&.green {
|
||||
background-image: url($svg-shop-list-button-green-availability);
|
||||
background-image: url($svg-shop-list-button-green-availability);
|
||||
}
|
||||
|
||||
&.orange {
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Bailout page returns the initial data 1`] = `
|
||||
Object {
|
||||
"bailoutPageModel": Object {
|
||||
"email": "alexander.hamilton45@gmail.com",
|
||||
"firstName": "Alexander",
|
||||
"lastName": "Hamilton",
|
||||
"phoneNumber": "6145550909",
|
||||
},
|
||||
"notSeeingPreferredShop": false,
|
||||
"rules": Object {
|
||||
"email": "email-required|email-address-format",
|
||||
"firstName": "first-name-required",
|
||||
"lastName": "last-name-required",
|
||||
"phoneNumber": "phone-number-required|phone-number-format",
|
||||
},
|
||||
"widget": Object {
|
||||
"defaultSiteHeader": "SiteSubHeaderWidget",
|
||||
"noTpa": "ContentGroupNoTPAWidget",
|
||||
"notSeeingPreferredShop": "ContentGroupNotSeeingPreferredShop",
|
||||
},
|
||||
}
|
||||
`;
|
||||
487
src/layouts/bailout-page/bailout-page.spec.js
Normal file
487
src/layouts/bailout-page/bailout-page.spec.js
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
// Components
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import bailoutPage from '@/layouts/bailout-page/bailout-page.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import routerParams from '@/router/router-constants/router-params';
|
||||
import { useMainStore } from '@/store';
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
||||
import settleAllPromises from '@/helpers/layout-helper.js';
|
||||
|
||||
// Mock fetchCmsContentForPage
|
||||
jest.mock('@/helpers/cms-content-helper', () => ({
|
||||
fetchCmsContentForPage: jest.fn(),
|
||||
doesCopyContainRouterLink: jest.fn()
|
||||
}));
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock('@/helpers/layout-helper.js', () => jest.fn());
|
||||
|
||||
function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRunAfterInitializingStore = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
const testingPinia = createTestingPinia({
|
||||
initialState: {
|
||||
main: mainInitialState
|
||||
}
|
||||
});
|
||||
useMainStore(testingPinia);
|
||||
methodToRunAfterInitializingStore();
|
||||
|
||||
mountOptions.global.plugins = [testingPinia];
|
||||
mountOptions.data = () => (initialData);
|
||||
|
||||
const apiResponses = {
|
||||
cmsContent: {}
|
||||
};
|
||||
|
||||
settleAllPromises.mockImplementation(() => apiResponses);
|
||||
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
|
||||
|
||||
const wrapper = shallowMount(bailoutPage, mountOptions);
|
||||
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => '');
|
||||
wrapper.vm.setCmsContent = jest.fn();
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
describe('Bailout page', () => {
|
||||
test('returns the initial data', () => {
|
||||
// Arrange
|
||||
const firstName = 'Alexander';
|
||||
const lastName = 'Hamilton';
|
||||
const phoneNumber = '6145550909';
|
||||
const emailAddress = 'alexander.hamilton45@gmail.com';
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
customer: {
|
||||
firstName,
|
||||
lastName,
|
||||
phoneNumber,
|
||||
emailAddress
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$data).toMatchSnapshot();
|
||||
});
|
||||
describe('renders', () => {
|
||||
test('site header', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(bailoutPage, getMountOptions());
|
||||
|
||||
// Act
|
||||
const siteHeader = wrapper.findComponent({ ref: 'siteHeader' });
|
||||
|
||||
// Assert
|
||||
expect(siteHeader.exists()).toBeTruthy();
|
||||
});
|
||||
test('sub header', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(bailoutPage, getMountOptions());
|
||||
|
||||
// Act
|
||||
const siteSubHeader = wrapper.findComponent({ ref: 'siteSubHeader' });
|
||||
|
||||
// Assert
|
||||
expect(siteSubHeader.exists()).toBeTruthy();
|
||||
});
|
||||
test('first name question', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(bailoutPage, getMountOptions());
|
||||
|
||||
// Act
|
||||
const firstName = wrapper.findComponent({ ref: 'firstName' });
|
||||
|
||||
// Assert
|
||||
expect(firstName.exists()).toBeTruthy();
|
||||
});
|
||||
test('last name question', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(bailoutPage, getMountOptions());
|
||||
|
||||
// Act
|
||||
const lastName = wrapper.findComponent({ ref: 'lastName' });
|
||||
|
||||
// Assert
|
||||
expect(lastName.exists()).toBeTruthy();
|
||||
});
|
||||
test('phone number name question', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(bailoutPage, getMountOptions());
|
||||
|
||||
// Act
|
||||
const phoneNumber = wrapper.findComponent({ ref: 'phoneNumber' });
|
||||
|
||||
// Assert
|
||||
expect(phoneNumber.exists()).toBeTruthy();
|
||||
});
|
||||
test('email question', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(bailoutPage, getMountOptions());
|
||||
|
||||
// Act
|
||||
const email = wrapper.findComponent({ ref: 'emailAddress' });
|
||||
|
||||
// Assert
|
||||
expect(email.exists()).toBeTruthy();
|
||||
});
|
||||
test('footer', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(bailoutPage, getMountOptions());
|
||||
|
||||
// Act
|
||||
const siteFooter = wrapper.findComponent({ ref: 'siteFooter' });
|
||||
|
||||
// Assert
|
||||
expect(siteFooter.exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
describe('computed', () => {
|
||||
describe('subHeaderCmsWidgetName', () => {
|
||||
test.each([true, false])(
|
||||
'returns noTpa widget name when tpa flow not enabled',
|
||||
(notSeeingPreferredShop) => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: false }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = 'ContentGroupNoTPAWidget';
|
||||
|
||||
// Act
|
||||
const name = wrapper.vm.subHeaderCmsWidgetName;
|
||||
|
||||
// Assert
|
||||
expect(name).toBe(expected);
|
||||
}
|
||||
);
|
||||
test('returns notSeeingPreferredShop widget name when tpa enabled and notSeeingPreferredShop true', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: true }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop: true };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = 'ContentGroupNotSeeingPreferredShop';
|
||||
|
||||
// Act
|
||||
const name = wrapper.vm.subHeaderCmsWidgetName;
|
||||
|
||||
// Assert
|
||||
expect(name).toBe(expected);
|
||||
});
|
||||
test('returns default widget name when tpa enabled and notSeeingPreferredShop false', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: true }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop: false };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = 'SiteSubHeaderWidget';
|
||||
|
||||
// Act
|
||||
const name = wrapper.vm.subHeaderCmsWidgetName;
|
||||
|
||||
// Assert
|
||||
expect(name).toBe(expected);
|
||||
});
|
||||
});
|
||||
describe('subHeaderContentProperty', () => {
|
||||
test('returns "SubHeaderText" when tpa flow enabled and not seeing preferred shop flag false', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: true }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop: false };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = 'SubHeaderText';
|
||||
|
||||
// Act
|
||||
const name = wrapper.vm.subHeaderContentProperty;
|
||||
|
||||
// Assert
|
||||
expect(name).toBe(expected);
|
||||
});
|
||||
test.each([true, false])(
|
||||
'returns "HeaderText" when tpa flow not enabled',
|
||||
(notSeeingPreferredShop) => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: false }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = 'HeaderText';
|
||||
|
||||
// Act
|
||||
const name = wrapper.vm.subHeaderContentProperty;
|
||||
|
||||
// Assert
|
||||
expect(name).toBe(expected);
|
||||
}
|
||||
);
|
||||
test('returns "HeaderText" when tpa flow enabled and not seeing preferred shop flag true', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: false }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop: true };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = 'HeaderText';
|
||||
|
||||
// Act
|
||||
const name = wrapper.vm.subHeaderContentProperty;
|
||||
|
||||
// Assert
|
||||
expect(name).toBe(expected);
|
||||
});
|
||||
});
|
||||
describe('subContentProperty', () => {
|
||||
test('returns "SecondaryText" when tpa flow enabled and not seeing preferred shop flag false', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: true }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop: false };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = 'SecondaryText';
|
||||
|
||||
// Act
|
||||
const name = wrapper.vm.subContentProperty;
|
||||
|
||||
// Assert
|
||||
expect(name).toBe(expected);
|
||||
});
|
||||
test.each([true, false])(
|
||||
'returns "BodyText" when tpa flow not enabled',
|
||||
(notSeeingPreferredShop) => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: false }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = 'BodyText';
|
||||
|
||||
// Act
|
||||
const name = wrapper.vm.subContentProperty;
|
||||
|
||||
// Assert
|
||||
expect(name).toBe(expected);
|
||||
}
|
||||
);
|
||||
test('returns "BodyText" when tpa flow enabled and not seeing preferred shop flag true', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: true }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop: true };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = 'BodyText';
|
||||
|
||||
// Act
|
||||
const name = wrapper.vm.subContentProperty;
|
||||
|
||||
// Assert
|
||||
expect(name).toBe(expected);
|
||||
});
|
||||
});
|
||||
describe('stripRteStyle', () => {
|
||||
test('returns false when tpa flow enabled and not seeing preferred shop flag false', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: true }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop: false };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = false;
|
||||
|
||||
// Act
|
||||
const flag = wrapper.vm.stripRteStyle;
|
||||
|
||||
// Assert
|
||||
expect(flag).toBe(expected);
|
||||
});
|
||||
test('returns true when tpa flow not enabled and not seeing preferred shop flag false', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: false }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop: false };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = true;
|
||||
|
||||
// Act
|
||||
const flag = wrapper.vm.stripRteStyle;
|
||||
|
||||
// Assert
|
||||
expect(flag).toBe(expected);
|
||||
});
|
||||
test('returns true when tpa flow enabled and not seeing preferred shop flag true', () => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow: true }
|
||||
};
|
||||
const initialData = { notSeeingPreferredShop: true };
|
||||
const { wrapper } = getMountedComponent(mainInitialState, initialData);
|
||||
const expected = true;
|
||||
|
||||
// Act
|
||||
const flag = wrapper.vm.stripRteStyle;
|
||||
|
||||
// Assert
|
||||
expect(flag).toBe(expected);
|
||||
});
|
||||
});
|
||||
describe('isTpaEnabled', () => {
|
||||
test.each([true, false])(
|
||||
'matches enableTPAFlow in store',
|
||||
(enableTPAFlow) => {
|
||||
// Arrange
|
||||
const mainInitialState = {
|
||||
issConfig: { enableTPAFlow }
|
||||
};
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Act
|
||||
const flag = wrapper.vm.isTpaEnabled;
|
||||
|
||||
// Assert
|
||||
expect(flag).toBe(enableTPAFlow);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('method', () => {
|
||||
describe('backButtonAction', () => {
|
||||
test('should navigate with CLICKED_BACK_PREVIOUS scenario', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
|
||||
wrapper.vm.navigationScenarios.CLICKED_BACK_PREVIOUS,
|
||||
wrapper.vm.$route
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('forwardButtonAction', () => {
|
||||
test('should navigate to the next route', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
|
||||
wrapper.vm.navigationScenarios.CLICKED_FORWARD,
|
||||
wrapper.vm.$route,
|
||||
{},
|
||||
{},
|
||||
wrapper.vm.bailoutPageModel
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('getBailoutPageModelFromStore', () => {
|
||||
test('should return expected bailout page model', () => {
|
||||
// Arrange
|
||||
const firstName = 'Jake';
|
||||
const lastName = 'From State Farm';
|
||||
const phoneNumber = '4215558234';
|
||||
const emailAddress = 'jake.from.state.farm993@yahoo.com';
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
customer: {
|
||||
firstName,
|
||||
lastName,
|
||||
phoneNumber,
|
||||
emailAddress
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Act
|
||||
const pageModel = wrapper.vm.getBailoutPageModelFromStore();
|
||||
|
||||
// Assert
|
||||
expect(pageModel).toEqual({
|
||||
firstName,
|
||||
lastName,
|
||||
phoneNumber,
|
||||
email: emailAddress
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('setNotSeeingPreferShop', () => {
|
||||
test.each([true, false])(
|
||||
'updates notSeeingPreferredShop value',
|
||||
(flag) => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.setNotSeeingPreferShop(flag);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.notSeeingPreferredShop).toBe(flag);
|
||||
}
|
||||
);
|
||||
test('sets notSeeingPreferredShop to true when null is passed', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.setNotSeeingPreferShop(null);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.notSeeingPreferredShop).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('before entering the route', () => {
|
||||
test.each([true, false])(
|
||||
'sets notSeeingPreferredShop flag based value returned by store method pageData',
|
||||
async (notSeeingPreferredShop) => {
|
||||
// Arrange
|
||||
const page = 'bailout-page';
|
||||
const initialStoreState = {
|
||||
applicationUser: {
|
||||
pageData: {
|
||||
[page]: {
|
||||
[routerParams.NOT_SEEING_PREFERRED_SHOP]: notSeeingPreferredShop,
|
||||
turtle: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(initialStoreState);
|
||||
|
||||
// Act
|
||||
await bailoutPage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: page } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.notSeeingPreferredShop).toBe(notSeeingPreferredShop);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -5,21 +5,18 @@
|
|||
@submit="onSubmit"
|
||||
@invalidSubmit="onInvalidSubmit">
|
||||
<div class="page-container-grouped-styles">
|
||||
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||
<siteHeader
|
||||
ref="siteHeader"
|
||||
cmsWidgetName="SiteHeaderWidget" />
|
||||
<div class="main-content-container">
|
||||
<siteSubHeader
|
||||
v-if="!isNoTpa"
|
||||
cmsWidgetName="SiteSubHeaderWidget"
|
||||
ref="siteSubHeader"
|
||||
:cmsWidgetName="subHeaderCmsWidgetName"
|
||||
:contentProperty="subHeaderContentProperty"
|
||||
:stripRteStyle="stripRteStyle"
|
||||
:subContentProperty="subContentProperty"
|
||||
class="sub-header-content"
|
||||
justification="left" />
|
||||
<siteSubHeader
|
||||
v-else
|
||||
cmsWidgetName="ContentGroupNoTPAWidget"
|
||||
class="sub-header-content"
|
||||
contentProperty="HeaderText"
|
||||
justification="left"
|
||||
:stripRteStyle="true"
|
||||
subContentProperty="BodyText" />
|
||||
<textboxQuestion
|
||||
ref="firstName"
|
||||
v-model="bailoutPageModel.firstName"
|
||||
|
|
@ -54,6 +51,7 @@
|
|||
disableAutoFill
|
||||
:validationRules="rules.email" />
|
||||
<siteFooter
|
||||
ref="siteFooter"
|
||||
class="footer-content-container"
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
|
|
@ -65,6 +63,7 @@
|
|||
</template>
|
||||
<script>
|
||||
// Components
|
||||
import { Form } from 'vee-validate';
|
||||
import siteHeader from '@/iss-components/site-header/site-header.vue';
|
||||
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue';
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
||||
|
|
@ -72,10 +71,11 @@ import textboxQuestion from '@/digital-components/textbox-question/textbox-quest
|
|||
// Supporting files
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
||||
import { Form } from 'vee-validate';
|
||||
import globalRules from '@/constants/global-rules';
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
import { useMainStore } from '@/store';
|
||||
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||
import routerParams from '@/router/router-constants/router-params';
|
||||
|
||||
export default {
|
||||
name: 'bailout-page',
|
||||
|
|
@ -101,17 +101,21 @@ export default {
|
|||
// use resultMap to populate layout content.
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
const pageData = useMainStore().pageData(to.query.issPage);
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.setNotSeeingPreferShop(pageData[routerParams.NOT_SEEING_PREFERRED_SHOP]);
|
||||
});
|
||||
},
|
||||
setup() {
|
||||
const mainStore = useMainStore();
|
||||
return { mainStore };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notSeeingPreferredShop: false,
|
||||
bailoutPageModel: this.getBailoutPageModelFromStore(),
|
||||
widget: {
|
||||
defaultSiteHeader: 'SiteSubHeaderWidget',
|
||||
noTpa: 'ContentGroupNoTPAWidget',
|
||||
notSeeingPreferredShop: 'ContentGroupNotSeeingPreferredShop'
|
||||
},
|
||||
rules: {
|
||||
firstName: globalRules.FIRST_NAME_REQUIRED,
|
||||
lastName: globalRules.LAST_NAME_REQUIRED,
|
||||
|
|
@ -121,8 +125,30 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
isNoTpa() {
|
||||
return !this.mainStore.issConfig.enableTPAFlow;
|
||||
subHeaderCmsWidgetName() {
|
||||
if (!this.isTpaEnabled) {
|
||||
return this.widget.noTpa;
|
||||
}
|
||||
if (this.notSeeingPreferredShop) {
|
||||
return this.widget.notSeeingPreferredShop;
|
||||
}
|
||||
return this.widget.defaultSiteHeader;
|
||||
},
|
||||
subHeaderContentProperty() {
|
||||
return !this.isTpaEnabled || this.notSeeingPreferredShop
|
||||
? widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT
|
||||
: widgetFields.SUB_HEADER_WIDGET.SUB_HEADER_TEXT;
|
||||
},
|
||||
subContentProperty() {
|
||||
return !this.isTpaEnabled || this.notSeeingPreferredShop
|
||||
? widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT
|
||||
: widgetFields.SUB_HEADER_WIDGET.SECONDARY_TEXT;
|
||||
},
|
||||
stripRteStyle() {
|
||||
return !this.isTpaEnabled || this.notSeeingPreferredShop;
|
||||
},
|
||||
isTpaEnabled() {
|
||||
return useMainStore().issConfig.enableTPAFlow;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -131,9 +157,6 @@ export default {
|
|||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK_PREVIOUS, this.$route);
|
||||
},
|
||||
forwardButtonAction() {
|
||||
return this.navigateForward();
|
||||
},
|
||||
navigateForward() {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route,
|
||||
|
|
@ -144,11 +167,14 @@ export default {
|
|||
},
|
||||
getBailoutPageModelFromStore() {
|
||||
return {
|
||||
firstName: this.mainStore.order.customer.firstName,
|
||||
lastName: this.mainStore.order.customer.lastName,
|
||||
phoneNumber: this.mainStore.order.customer.phoneNumber,
|
||||
email: this.mainStore.order.customer.emailAddress
|
||||
firstName: useMainStore().order.customer.firstName,
|
||||
lastName: useMainStore().order.customer.lastName,
|
||||
phoneNumber: useMainStore().order.customer.phoneNumber,
|
||||
email: useMainStore().order.customer.emailAddress
|
||||
};
|
||||
},
|
||||
setNotSeeingPreferShop(value) {
|
||||
this.notSeeingPreferredShop = value ?? false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ import errorMessages from '@/constants/error-messages';
|
|||
import { useMainStore } from '@/store/index.js';
|
||||
import { markRaw, nextTick } from 'vue';
|
||||
import { getAvailabilityRating } from '@/helpers/service-location-helper';
|
||||
import shopListButton from './shop-list-button/shop-list-button.vue';
|
||||
import shopListButton from '@/iss-components/shop-list-button/shop-list-button.vue';
|
||||
|
||||
defineRule('option-required', required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
|
|
@ -124,6 +124,7 @@ export default {
|
|||
const formattedEndDate = endDate.toISOString().split('T')[0];
|
||||
|
||||
return {
|
||||
displayAvailabilityIndicators: true,
|
||||
availabilityRatingCallback: getAvailabilityRating,
|
||||
startDate: formattedStartDate,
|
||||
endDate: formattedEndDate,
|
||||
|
|
|
|||
193
src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap
Normal file
193
src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TPA search page returns the initial data 1`] = `
|
||||
Object {
|
||||
"additionalButtonData": Object {
|
||||
"displayAvailabilityIndicators": false,
|
||||
},
|
||||
"filter": "",
|
||||
"providers": Array [],
|
||||
"rules": Object {
|
||||
"filter": "option-required",
|
||||
"provider": "option-required",
|
||||
"zipCode": "zip-code-required|zip-code-search-format",
|
||||
},
|
||||
"selectedProviderNumber": "",
|
||||
"shopListButton": Object {
|
||||
"beforeMount": [Function],
|
||||
"components": Object {
|
||||
"baseInputButton": Object {
|
||||
"computed": Object {
|
||||
"buttonId": [Function],
|
||||
"eventTypes": [Function],
|
||||
"inputType": [Function],
|
||||
"isChecked": [Function],
|
||||
"isValueSelectedOnClick": [Function],
|
||||
},
|
||||
"data": [Function],
|
||||
"emits": Array [
|
||||
"update:modelValue",
|
||||
],
|
||||
"methods": Object {
|
||||
"handleBlur": [Function],
|
||||
"handleClick": [Function],
|
||||
"handleEventAction": [Function],
|
||||
"handleFocus": [Function],
|
||||
"handleSelectionChange": [Function],
|
||||
},
|
||||
"mounted": [Function],
|
||||
"name": "base-input-button",
|
||||
"props": Object {
|
||||
"buttonWrapperClasses": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"groupName": Object {
|
||||
"required": true,
|
||||
"type": [Function],
|
||||
},
|
||||
"inputClasses": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"isMultiSelect": [Function],
|
||||
"isRequired": Object {
|
||||
"default": true,
|
||||
"type": [Function],
|
||||
},
|
||||
"lastValuePushedToGa": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"modelValue": Object {
|
||||
"required": true,
|
||||
"validator": [Function],
|
||||
},
|
||||
"selectingInitiatesLoad": Object {
|
||||
"default": false,
|
||||
"type": [Function],
|
||||
},
|
||||
"setLastValuePushedToGa": [Function],
|
||||
"suppressError": [Function],
|
||||
"validationRules": Object {
|
||||
"default": "",
|
||||
"type": [Function],
|
||||
},
|
||||
"value": Object {
|
||||
"required": true,
|
||||
"type": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
},
|
||||
"valueToLogType": [Function],
|
||||
},
|
||||
"render": [Function],
|
||||
"setup": [Function],
|
||||
},
|
||||
"loader": Object {
|
||||
"name": "loader",
|
||||
"props": Object {
|
||||
"loaderColor": Object {
|
||||
"type": [Function],
|
||||
},
|
||||
"loaderPosition": Object {
|
||||
"type": [Function],
|
||||
},
|
||||
},
|
||||
"render": [Function],
|
||||
},
|
||||
},
|
||||
"computed": Object {
|
||||
"availabilityRatingClass": [Function],
|
||||
"badgeText": [Function],
|
||||
"isLoaderDisplayed": [Function],
|
||||
},
|
||||
"data": [Function],
|
||||
"mixins": Array [
|
||||
Object {
|
||||
"computed": Object {
|
||||
"selectedValue": Object {
|
||||
"get": [Function],
|
||||
"set": [Function],
|
||||
},
|
||||
},
|
||||
"model": Object {
|
||||
"event": "change",
|
||||
"prop": "modelValue",
|
||||
},
|
||||
"props": Object {
|
||||
"additionalButtonData": [Function],
|
||||
"additionalButtonStyling": [Function],
|
||||
"altText": Object {
|
||||
"default": "",
|
||||
"type": [Function],
|
||||
},
|
||||
"buttonAuxiliaryCopy": [Function],
|
||||
"buttonBodyCopy": [Function],
|
||||
"buttonFooterCopy": [Function],
|
||||
"buttonImage": [Function],
|
||||
"buttonImageId": [Function],
|
||||
"buttonLabel": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"buttonLabelSubCopy": [Function],
|
||||
"groupName": Object {
|
||||
"required": true,
|
||||
"type": [Function],
|
||||
},
|
||||
"isMultiSelect": [Function],
|
||||
"isRequired": Object {
|
||||
"default": true,
|
||||
"type": [Function],
|
||||
},
|
||||
"isWide": [Function],
|
||||
"lastValuePushedToGa": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
"modelValue": Object {
|
||||
"required": true,
|
||||
"validator": [Function],
|
||||
},
|
||||
"screenReaderOnlyText": [Function],
|
||||
"selectingInitiatesLoad": Object {
|
||||
"default": false,
|
||||
"type": [Function],
|
||||
},
|
||||
"setLastValuePushedToGa": [Function],
|
||||
"suppressError": [Function],
|
||||
"textPosition": [Function],
|
||||
"validationRules": Object {
|
||||
"default": "",
|
||||
"type": [Function],
|
||||
},
|
||||
"value": Object {
|
||||
"required": true,
|
||||
"type": Array [
|
||||
[Function],
|
||||
[Function],
|
||||
],
|
||||
},
|
||||
"valueToLogType": [Function],
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "shop-list-button",
|
||||
"render": [Function],
|
||||
},
|
||||
"widget": Object {
|
||||
"filterByQuestion": "FilterByQuestion",
|
||||
"noNetworkShopsAlert": "NoNetworkShopsAlertWidget",
|
||||
"searchInstructions": "SearchInstructions",
|
||||
"shopNotListedLink": "ShopNotListedLink",
|
||||
"siteFooter": "SiteFooterWidget",
|
||||
"siteHeader": "SiteHeaderWidget",
|
||||
"tpaSearchQuestion": "TPASearchQuestion",
|
||||
},
|
||||
"zipCode": "12663",
|
||||
}
|
||||
`;
|
||||
734
src/layouts/tpa-search/tpa-search.spec.js
Normal file
734
src/layouts/tpa-search/tpa-search.spec.js
Normal file
|
|
@ -0,0 +1,734 @@
|
|||
// Components
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import tpaSearch from '@/layouts/tpa-search/tpa-search.vue';
|
||||
|
||||
// Supporting Files
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import { useMainStore } from '@/store';
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
||||
import settleAllPromises from '@/helpers/layout-helper.js';
|
||||
import globalRules from '@/constants/global-rules.js';
|
||||
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||
|
||||
// Mock fetchCmsContentForPage
|
||||
jest.mock('@/helpers/cms-content-helper', () => ({
|
||||
fetchCmsContentForPage: jest.fn(),
|
||||
doesCopyContainRouterLink: jest.fn()
|
||||
}));
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock('@/helpers/layout-helper.js', () => jest.fn());
|
||||
|
||||
function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRunAfterInitializingStore = () => {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
}
|
||||
});
|
||||
|
||||
const testingPinia = createTestingPinia({
|
||||
initialState: {
|
||||
main: mainInitialState
|
||||
}
|
||||
});
|
||||
useMainStore(testingPinia);
|
||||
methodToRunAfterInitializingStore();
|
||||
|
||||
mountOptions.global.plugins = [testingPinia];
|
||||
mountOptions.data = () => (initialData);
|
||||
|
||||
const apiResponses = { cmsContent: {} };
|
||||
|
||||
settleAllPromises.mockImplementation(() => apiResponses);
|
||||
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
|
||||
|
||||
const wrapper = shallowMount(tpaSearch, mountOptions);
|
||||
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => cmsContent);
|
||||
wrapper.vm.setCmsContent = jest.fn();
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
describe('TPA search page', () => {
|
||||
test('returns the initial data', () => {
|
||||
// Arrange
|
||||
const zipCode = '12663';
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
customer: {
|
||||
address: { zipCode }
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(mainInitialState);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$data).toMatchSnapshot();
|
||||
});
|
||||
describe('should render', () => {
|
||||
test('site header', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
const expectedWidgetName = 'SiteHeaderWidget';
|
||||
|
||||
// Act
|
||||
const siteHeader = wrapper.findComponent({ ref: 'siteHeader' });
|
||||
|
||||
// Assert
|
||||
expect(siteHeader.exists()).toBeTruthy();
|
||||
expect(siteHeader.props().cmsWidgetName).toBe(expectedWidgetName);
|
||||
});
|
||||
test('search providers form', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
|
||||
// Act
|
||||
const searchProvidersForm = wrapper.findComponent('#searchProvidersForm');
|
||||
|
||||
// Assert
|
||||
expect(searchProvidersForm.exists()).toBeTruthy();
|
||||
});
|
||||
test('search question label', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
|
||||
// Act
|
||||
const searchQuestionLabel = wrapper.find('#tpaSearchQuestionLabel');
|
||||
|
||||
// Assert
|
||||
expect(searchQuestionLabel.exists()).toBeTruthy();
|
||||
expect(searchQuestionLabel.attributes().for).toBe('tpaSearchQuestionField');
|
||||
expect(searchQuestionLabel.classes()).toContain('text-center');
|
||||
expect(searchQuestionLabel.classes()).toContain('mt-5');
|
||||
expect(searchQuestionLabel.classes()).toContain('mb-0');
|
||||
expect(searchQuestionLabel.classes()).toContain('text-black');
|
||||
expect(searchQuestionLabel.classes()).toContain('w-100');
|
||||
expect(searchQuestionLabel.classes()).toContain('search-question');
|
||||
});
|
||||
test('search instructions', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
|
||||
// Act
|
||||
const searchInstructions = wrapper.find('#searchInstructions');
|
||||
|
||||
// Assert
|
||||
expect(searchInstructions.exists()).toBeTruthy();
|
||||
expect(searchInstructions.attributes().for).toBe('tpaSearchQuestionField');
|
||||
expect(searchInstructions.classes()).toContain('text-center');
|
||||
expect(searchInstructions.classes()).toContain('small');
|
||||
expect(searchInstructions.classes()).toContain('darker-gray');
|
||||
expect(searchInstructions.classes()).toContain('w-100');
|
||||
expect(searchInstructions.classes()).toContain('mb-4');
|
||||
});
|
||||
test('search question field', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
const expectedWidgetName = 'TPASearchQuestion';
|
||||
|
||||
// Act
|
||||
const searchQuestionField = wrapper.findComponent('#tpaSearchQuestionField');
|
||||
|
||||
// Assert
|
||||
expect(searchQuestionField.exists()).toBeTruthy();
|
||||
expect(searchQuestionField.props().inputId).toBe('tpaSearchQuestionField');
|
||||
expect(searchQuestionField.props().isRequired).toBeTruthy();
|
||||
expect(searchQuestionField.props().cmsWidgetName).toBe(expectedWidgetName);
|
||||
expect(searchQuestionField.props().includeSearchIcon).toBeTruthy();
|
||||
expect(searchQuestionField.props().displayQuestionText).toBeFalsy();
|
||||
expect(searchQuestionField.props().validationRules)
|
||||
.toBe(`${globalRules.ZIP_CODE_REQUIRED}|${globalRules.ZIP_CODE_SEARCH_FORMAT}`);
|
||||
});
|
||||
test('provider selection form', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
|
||||
// Act
|
||||
const providerSelectionForm = wrapper.findComponent('#providerSelectionForm');
|
||||
|
||||
// Assert
|
||||
expect(providerSelectionForm.exists()).toBeTruthy();
|
||||
});
|
||||
test('map', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
|
||||
// Act
|
||||
const map = wrapper.find('#map');
|
||||
|
||||
// Assert
|
||||
expect(map.exists()).toBeTruthy();
|
||||
expect(map.classes()).toContain('mb-4');
|
||||
});
|
||||
test('search radius filter', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
const expectedWidgetName = 'FilterByQuestion';
|
||||
|
||||
// Act
|
||||
const searchRadiusFilter = wrapper.findComponent('#searchRadiusFilter');
|
||||
|
||||
// Assert
|
||||
expect(searchRadiusFilter.exists()).toBeTruthy();
|
||||
expect(searchRadiusFilter.props().cmsWidgetName).toBe(expectedWidgetName);
|
||||
expect(searchRadiusFilter.props().validationRules).toBe(globalRules.OPTION_REQUIRED);
|
||||
});
|
||||
test('select provider question', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
|
||||
// Act
|
||||
const selectProviderQuestion = wrapper.findComponent('#selectProviderQuestion');
|
||||
|
||||
// Assert
|
||||
expect(selectProviderQuestion.exists()).toBeTruthy();
|
||||
expect(selectProviderQuestion.props().buttonTypeString).toBe('shopListButton');
|
||||
expect(selectProviderQuestion.classes()).toContain('radioQuestion');
|
||||
expect(selectProviderQuestion.props().groupName).toBe('chooseShop');
|
||||
expect(selectProviderQuestion.props().textPosition).toBe('text-start');
|
||||
expect(selectProviderQuestion.props().isRequired).toBeTruthy();
|
||||
expect(selectProviderQuestion.props().validationRules).toBe(globalRules.OPTION_REQUIRED);
|
||||
});
|
||||
test.each([[], null, undefined])(
|
||||
'no network providers alert when providers length is 0, undefined, or null',
|
||||
(providers) => {
|
||||
// Arrange
|
||||
const initialData = {
|
||||
providers
|
||||
};
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions({}, initialData));
|
||||
|
||||
// Act
|
||||
const noNetworkProvidersAlert = wrapper.findComponent('#alertNoNetworkProviders');
|
||||
|
||||
// Assert
|
||||
expect(noNetworkProvidersAlert.exists()).toBeTruthy();
|
||||
expect(noNetworkProvidersAlert.props().cmsWidgetName).toBeTruthy();
|
||||
expect(noNetworkProvidersAlert.props().alertClass).toBe('alert-warning');
|
||||
expect(noNetworkProvidersAlert.props().isDismissible).toBeFalsy();
|
||||
}
|
||||
);
|
||||
test('preferred shop not listed link', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
|
||||
// Act
|
||||
const preferredShopNotListedLink = wrapper.findComponent('#preferredShopNotListedLink');
|
||||
|
||||
// Assert
|
||||
expect(preferredShopNotListedLink.exists()).toBeTruthy();
|
||||
expect(preferredShopNotListedLink.props().linkType).toBe('navigation');
|
||||
expect(preferredShopNotListedLink.props().href).toBe('#!');
|
||||
});
|
||||
test('site footer', () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
|
||||
// Act
|
||||
const footer = wrapper.findComponent({ ref: 'siteFooter' });
|
||||
|
||||
// Assert
|
||||
expect(footer.exists()).toBeTruthy();
|
||||
expect(footer.props().cmsWidgetName).toBe('SiteFooterWidget');
|
||||
});
|
||||
});
|
||||
test('should not render no network providers alert when providers length is not 0', async () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(tpaSearch, getMountOptions());
|
||||
await wrapper.setData({ providers: ['p1', 'p2'] });
|
||||
|
||||
// Act
|
||||
const noNetworkProvidersAlert = wrapper.findComponent('#alertNoNetworkProviders');
|
||||
|
||||
// Assert
|
||||
expect(noNetworkProvidersAlert.exists()).toBeFalsy();
|
||||
});
|
||||
describe('computed', () => {
|
||||
describe('filterOptions', () => {
|
||||
test.each([[], null, undefined])(
|
||||
'returns empty object when cms content is empty, null or undefined',
|
||||
(cmsContent) => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.mixins = [{
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation((widget, field) =>
|
||||
(widget === 'FilterByQuestion' && field === widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
|
||||
? cmsContent
|
||||
: ''))
|
||||
}
|
||||
}];
|
||||
const wrapper = shallowMount(tpaSearch, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.filterOptions;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual({});
|
||||
}
|
||||
);
|
||||
test('returns expected when cms content is some non empty iterable', () => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions();
|
||||
const cmsContent = [{ Name: 'foo' }, { Name: 'bar' }];
|
||||
mountOptions.mixins = [{
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation((widget, field) =>
|
||||
(widget === 'FilterByQuestion' && field === widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
|
||||
? cmsContent
|
||||
: ''))
|
||||
}
|
||||
}];
|
||||
const wrapper = shallowMount(tpaSearch, mountOptions);
|
||||
const expected = { foo: 'foo', bar: 'bar' };
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.filterOptions;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
test.each([null, undefined, '', 'non empty string'])(
|
||||
'tpaSearchQuestionLabel returns value from getCmsContent',
|
||||
(cmsContent) => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.mixins = [{
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation((widget, field) =>
|
||||
(widget === 'TPASearchQuestion' && field === widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT
|
||||
? cmsContent
|
||||
: ''))
|
||||
}
|
||||
}];
|
||||
const wrapper = shallowMount(tpaSearch, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.tpaSearchQuestionLabel;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(cmsContent);
|
||||
}
|
||||
);
|
||||
test.each([null, undefined, '', 'non empty string'])(
|
||||
'searchInstructionsText returns value from getCmsContent',
|
||||
(cmsContent) => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.mixins = [{
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation((widget, field) =>
|
||||
(widget === 'SearchInstructions' && field === widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
? cmsContent
|
||||
: ''))
|
||||
}
|
||||
}];
|
||||
const wrapper = shallowMount(tpaSearch, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.searchInstructionsText;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(cmsContent);
|
||||
}
|
||||
);
|
||||
test.each([null, undefined, '', 'non empty string'])(
|
||||
'shopNotListedModalLink returns value from getCmsContent',
|
||||
(cmsContent) => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.mixins = [{
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation((widget, field) =>
|
||||
(widget === 'ShopNotListedLink' && field === widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
? cmsContent
|
||||
: ''))
|
||||
}
|
||||
}];
|
||||
const wrapper = shallowMount(tpaSearch, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.shopNotListedModalLink;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(cmsContent);
|
||||
}
|
||||
);
|
||||
describe('radiusInMiles', () => {
|
||||
test('returns 25 when filter is "25 miles"', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
await wrapper.setData({ filter: '25 miles' });
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.radiusInMiles;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(25);
|
||||
});
|
||||
test('returns 50 when filter is "50 miles"', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
await wrapper.setData({ filter: '50 miles' });
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.radiusInMiles;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(50);
|
||||
});
|
||||
test('returns 100 when filter is "100 miles"', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
await wrapper.setData({ filter: '100 miles' });
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.radiusInMiles;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(100);
|
||||
});
|
||||
test.each([null, undefined, 'some random string'])(
|
||||
'returns 0 when filter is not "25 miles", "50 miles", or "100 miles"',
|
||||
async (filter) => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
await wrapper.setData({ filter });
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.radiusInMiles;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(0);
|
||||
}
|
||||
);
|
||||
});
|
||||
describe('noNetworkShopsAlertHeaderText', () => {
|
||||
test('returns exact cmsContent value when "{custom:radiusInMiles}" is not a substring', async () => {
|
||||
// Arrange
|
||||
const cmsContent = 'content returned from cms';
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.mixins = [{
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation((widget, field) =>
|
||||
(widget === 'NoNetworkShopsAlertWidget' && field === widgetFields.ALERT_WIDGET.HEADLINE_TEXT
|
||||
? cmsContent
|
||||
: ''))
|
||||
}
|
||||
}];
|
||||
const wrapper = shallowMount(tpaSearch, mountOptions);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.noNetworkShopsAlertHeaderText;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(cmsContent);
|
||||
});
|
||||
test('returns cmsContent value with all instances of "{custom:radiusInMiles}" replaced with radiusInMiles', () => {
|
||||
// Arrange
|
||||
const cmsContent = '{custom:radiusInMiles} ret{custom:radiusInMiles}urned {custom:radiusInMiles}cms{custom:radiusInMiles}';
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.mixins = [{
|
||||
methods: {
|
||||
getCmsContent: jest.fn().mockImplementation((widget, field) =>
|
||||
(widget === 'NoNetworkShopsAlertWidget' && field === widgetFields.ALERT_WIDGET.HEADLINE_TEXT
|
||||
? cmsContent
|
||||
: ''))
|
||||
}
|
||||
}];
|
||||
const wrapper = shallowMount(tpaSearch, mountOptions);
|
||||
const expected = '0 ret0urned 0cms0';
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.noNetworkShopsAlertHeaderText;
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('watch', () => {
|
||||
test('on filter calls getTpaProviders and sets providers', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const providers = { data: ['some data', 'some more data'] };
|
||||
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers));
|
||||
const newFilter = 'new filter';
|
||||
|
||||
// Act
|
||||
await wrapper.vm.$options.watch.filter.call(wrapper.vm, newFilter);
|
||||
|
||||
// Assert
|
||||
expect(useMainStore().getTpaProviders).toBeCalled();
|
||||
expect(wrapper.vm.providers).toEqual(providers.data);
|
||||
});
|
||||
describe('on providers', () => {
|
||||
test.each([null, undefined, []])(
|
||||
'sets selected provider number to "" when there are no providers',
|
||||
(newProviders) => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.selectedProviderNumber).toBe('');
|
||||
}
|
||||
);
|
||||
test('sets selected provider number to value of provider when there is one provider', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const value = 'some value';
|
||||
const newProviders = [{ value }];
|
||||
|
||||
// Act
|
||||
wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.selectedProviderNumber).toBe(value);
|
||||
});
|
||||
test('sets selected provider number to "" when there are more than one provider', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const newProviders = [{ value: 'val1' }, { value: 'val2' }];
|
||||
|
||||
// Act
|
||||
wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.selectedProviderNumber).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('method', () => {
|
||||
describe('getProviders', () => {
|
||||
test.each([null, undefined, {}])(
|
||||
'returns empty list when getTpaProviders returns no data',
|
||||
async (newProviders) => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders));
|
||||
|
||||
// Act
|
||||
const result = await wrapper.vm.getProviders();
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual([]);
|
||||
}
|
||||
);
|
||||
test('returns expected when getTpaProviders returns data', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const providers = { data: [{ name: 'foo' }] };
|
||||
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers));
|
||||
|
||||
// Act
|
||||
const result = await wrapper.vm.getProviders();
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(providers.data);
|
||||
});
|
||||
});
|
||||
test('doNotSeeMyShopLinkClick invokes navigate method', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.doNotSeeMyShopLinkClick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
});
|
||||
describe('searchClick', () => {
|
||||
test.each([null, undefined, {}])(
|
||||
'sets providers to empty list when no data returned from getProviders',
|
||||
async (newProviders) => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders));
|
||||
|
||||
// Act
|
||||
await wrapper.vm.searchClick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.providers).toEqual([]);
|
||||
}
|
||||
);
|
||||
test('returns value from getProviders', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const newProviders = { data: [{ name: 'foo' }] };
|
||||
useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders));
|
||||
|
||||
// Act
|
||||
await wrapper.vm.searchClick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.providers).toEqual(newProviders.data);
|
||||
});
|
||||
});
|
||||
test('backButtonAction invokes navigate method', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
});
|
||||
test('forwardButtonAction invokes navigate method', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
});
|
||||
describe('getCustomValueFromString', () => {
|
||||
test('returns radius in miles value if "radiusInMiles"', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const str = 'radiusInMiles';
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.getCustomValueFromString(str);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
test('returns radius in miles value if not "radiusInMiles"', () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const str = 'some other string';
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.getCustomValueFromString(str);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
test.each([undefined, null, 'some value', ''])(
|
||||
'setFilter sets value of filter',
|
||||
(filter) => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.setFilter(filter);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.filter).toEqual(filter);
|
||||
}
|
||||
);
|
||||
test.each([undefined, null, ['some value'], []])(
|
||||
'setProviders sets value of providers',
|
||||
(providers) => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
wrapper.vm.setProviders(providers);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.providers).toEqual(providers);
|
||||
}
|
||||
);
|
||||
});
|
||||
describe('before route enter', () => {
|
||||
test('when providers exist at 25 mile radius, filter is set to "25 miles" and providers set to expected', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const zipCode = '18394';
|
||||
useMainStore().order.customer.address.zipCode = zipCode;
|
||||
const providers = { data: [{ name: 'provider' }] };
|
||||
useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => (radius === 25 ? providers : []));
|
||||
const expectedFilter = '25 miles';
|
||||
|
||||
// Act
|
||||
await tpaSearch.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: 'tpa-search' } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.filter).toBe(expectedFilter);
|
||||
expect(wrapper.vm.providers).toEqual(providers);
|
||||
});
|
||||
test('when providers exist at 50 mile radius but not 25, filter is set to "50 miles" and providers set to expected', async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const zipCode = '18394';
|
||||
useMainStore().order.customer.address.zipCode = zipCode;
|
||||
const providers = { data: [{ name: 'provider' }] };
|
||||
useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => (radius === 50 ? providers : []));
|
||||
const expectedFilter = '50 miles';
|
||||
|
||||
// Act
|
||||
await tpaSearch.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: 'tpa-search' } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.filter).toBe(expectedFilter);
|
||||
expect(wrapper.vm.providers).toEqual(providers);
|
||||
});
|
||||
test(
|
||||
'when providers exist at 100 mile radius but not 50 or 25, filter is set to "100 miles" and providers set to expected',
|
||||
async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const zipCode = '18394';
|
||||
useMainStore().order.customer.address.zipCode = zipCode;
|
||||
const providers = { data: [{ name: 'provider' }] };
|
||||
useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => (radius === 100 ? providers : []));
|
||||
const expectedFilter = '100 miles';
|
||||
|
||||
// Act
|
||||
await tpaSearch.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: 'tpa-search' } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.filter).toBe(expectedFilter);
|
||||
expect(wrapper.vm.providers).toEqual(providers);
|
||||
}
|
||||
);
|
||||
test(
|
||||
'when no providers exist at 100, 50, or 25 mile radius, filter is set to "100 miles" and providers set to empty list',
|
||||
async () => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
const zipCode = '18394';
|
||||
useMainStore().order.customer.address.zipCode = zipCode;
|
||||
useMainStore().getTpaProviders = jest.fn().mockImplementation(() => ([]));
|
||||
const expectedFilter = '100 miles';
|
||||
|
||||
// Act
|
||||
await tpaSearch.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: 'tpa-search' } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.filter).toBe(expectedFilter);
|
||||
expect(wrapper.vm.providers).toEqual([]);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,81 +1,309 @@
|
|||
<template>
|
||||
<Form
|
||||
ref="theForm"
|
||||
v-slot="{ meta }"
|
||||
@submit="onSubmit"
|
||||
@invalidSubmit="onInvalidSubmit">
|
||||
<div class="page-container-grouped-styles">
|
||||
<div class="fade-on-route-transition position-relative">
|
||||
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||
<div class="container-fluid pb-2 search">
|
||||
<p>Placeholder for TPA-Search</p>
|
||||
<siteFooter
|
||||
<div>
|
||||
<siteHeader
|
||||
ref="siteHeader"
|
||||
:cmsWidgetName="widget.siteHeader" />
|
||||
<Form
|
||||
id="searchProvidersForm"
|
||||
@submit="onSubmit"
|
||||
@invalidSubmit="onInvalidSubmit">
|
||||
<div class="container-fluid pb-2">
|
||||
<div class="mx-5">
|
||||
<label
|
||||
id="tpaSearchQuestionLabel"
|
||||
for="tpaSearchQuestionField"
|
||||
class="text-center mt-5 mb-0 text-black w-100 search-question">
|
||||
{{ tpaSearchQuestionLabel }}
|
||||
</label>
|
||||
<label
|
||||
id="searchInstructions"
|
||||
for="tpaSearchQuestionField"
|
||||
class="text-center small darker-gray w-100 mb-4">
|
||||
{{ searchInstructionsText }}
|
||||
</label>
|
||||
<div class="mb-5">
|
||||
<textboxQuestion
|
||||
id="tpaSearchQuestionField"
|
||||
v-model="zipCode"
|
||||
inputId="tpaSearchQuestionField"
|
||||
:cmsWidgetName="widget.tpaSearchQuestion"
|
||||
:includeSearchIcon="true"
|
||||
:displayQuestionText="false"
|
||||
isRequired
|
||||
:validationRules="rules.zipCode"
|
||||
@clickEvent="searchClick" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
<Form
|
||||
id="providerSelectionForm"
|
||||
v-slot="{ meta }"
|
||||
@submit="onSubmit"
|
||||
@invalidSubmit="onInvalidSubmit">
|
||||
<div class="container-fluid pb-2">
|
||||
<p
|
||||
id="map"
|
||||
class="mb-4">
|
||||
Placeholder for Map
|
||||
</p>
|
||||
|
||||
<div class="mx-5">
|
||||
<dropdownQuestion
|
||||
id="searchRadiusFilter"
|
||||
v-model="filter"
|
||||
:cmsWidgetName="widget.filterByQuestion"
|
||||
inputId="filterByQuestionField"
|
||||
:options="filterOptions"
|
||||
disableAutoFill
|
||||
:validationRules="rules.filter" />
|
||||
|
||||
<div class="my-4">
|
||||
<buttonQuestion
|
||||
id="selectProviderQuestion"
|
||||
v-model="selectedProviderNumber"
|
||||
buttonTypeString="shopListButton"
|
||||
:buttonTypeObject="shopListButton"
|
||||
class="radioQuestion"
|
||||
:answers="providers"
|
||||
groupName="chooseShop"
|
||||
textPosition="text-start"
|
||||
isRequired
|
||||
:validationRules="rules.provider"
|
||||
:additionalButtonData="additionalButtonData" />
|
||||
<alert
|
||||
v-if="providers?.length === 0 ?? true"
|
||||
id="alertNoNetworkProviders"
|
||||
:cmsWidgetName="widget.noNetworkShopsAlert"
|
||||
alertClass="alert-warning"
|
||||
:isDismissible="false"
|
||||
:manualHeadline="noNetworkShopsAlertHeaderText" />
|
||||
</div>
|
||||
<div
|
||||
class="text-center">
|
||||
<textLink
|
||||
id="preferredShopNotListedLink"
|
||||
linkType="navigation"
|
||||
href="#!"
|
||||
:text="shopNotListedModalLink"
|
||||
@clickEvent="doNotSeeMyShopLinkClick" />
|
||||
</div>
|
||||
</div>
|
||||
<siteFooter
|
||||
ref="siteFooter"
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
:cmsWidgetName="widget.siteFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
@backClicked="backButtonAction" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</Form>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// Components
|
||||
import { Form } from 'vee-validate';
|
||||
import siteHeader from '@/iss-components/site-header/site-header.vue';
|
||||
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
|
||||
import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question.vue';
|
||||
import buttonQuestion from '@/digital-components/button-question/button-question.vue'
|
||||
import shopListButton from '@/iss-components/shop-list-button/shop-list-button.vue'
|
||||
import textLink from '@/ux-components/text-link/text-link.vue';
|
||||
import alert from '@/ux-components/alert/alert.vue';
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
||||
import settleAllPromises from '@/helpers/layout-helper';
|
||||
import { Form } from 'vee-validate';
|
||||
import { useMainStore } from '@/store';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import globalRules from '@/constants/global-rules.js';
|
||||
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||
import routerParams from '@/router/router-constants/router-params';
|
||||
|
||||
export default {
|
||||
name: 'tpa-search',
|
||||
components: {
|
||||
siteHeader,
|
||||
textboxQuestion,
|
||||
dropdownQuestion,
|
||||
buttonQuestion,
|
||||
textLink,
|
||||
alert,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
},
|
||||
mixins: [BaseFormMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: 'cmsContent',
|
||||
promise: cmsContentPromise
|
||||
}];
|
||||
// use resultMap to populate layout content.
|
||||
}
|
||||
];
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
const radiusOptions = [25, 50, 100];
|
||||
const { zipCode } = useMainStore().order.customer.address;
|
||||
|
||||
const tpaProvidersRadius25 = await useMainStore().getTpaProviders(zipCode, radiusOptions[0]);
|
||||
const tpaProvidersRadius50 = await useMainStore().getTpaProviders(zipCode, radiusOptions[1]);
|
||||
const tpaProvidersRadius100 = await useMainStore().getTpaProviders(zipCode, radiusOptions[2]);
|
||||
let radius = '25 miles';
|
||||
|
||||
let providers = tpaProvidersRadius25;
|
||||
if ((tpaProvidersRadius25?.data ?? []).length === 0) {
|
||||
radius = '50 miles';
|
||||
providers = tpaProvidersRadius50;
|
||||
if ((tpaProvidersRadius50?.data ?? []).length === 0) {
|
||||
radius = '100 miles';
|
||||
providers = tpaProvidersRadius100;
|
||||
}
|
||||
}
|
||||
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.setFilter(radius);
|
||||
vm.setProviders(providers);
|
||||
});
|
||||
},
|
||||
data() {
|
||||
const { zipCode } = useMainStore().order.customer.address;
|
||||
return {
|
||||
zipCode,
|
||||
filter: '',
|
||||
providers: [],
|
||||
selectedProviderNumber: '',
|
||||
additionalButtonData: {
|
||||
displayAvailabilityIndicators: false
|
||||
},
|
||||
widget: {
|
||||
siteHeader: 'SiteHeaderWidget',
|
||||
tpaSearchQuestion: 'TPASearchQuestion',
|
||||
searchInstructions: 'SearchInstructions',
|
||||
filterByQuestion: 'FilterByQuestion',
|
||||
noNetworkShopsAlert: 'NoNetworkShopsAlertWidget',
|
||||
shopNotListedLink: 'ShopNotListedLink',
|
||||
siteFooter: 'SiteFooterWidget'
|
||||
},
|
||||
rules: {
|
||||
zipCode: `${globalRules.ZIP_CODE_REQUIRED}|${globalRules.ZIP_CODE_SEARCH_FORMAT}`,
|
||||
filter: globalRules.OPTION_REQUIRED, // TODO do we even need this?
|
||||
provider: globalRules.OPTION_REQUIRED
|
||||
},
|
||||
shopListButton
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filterOptions() {
|
||||
const filterByAnswers = this.getCmsContent(
|
||||
this.widget.filterByQuestion,
|
||||
widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
|
||||
) ?? [];
|
||||
const filterByAnswersObj = {};
|
||||
[...filterByAnswers].forEach((answer) => {
|
||||
filterByAnswersObj[answer.Name] = answer.Name;
|
||||
});
|
||||
return filterByAnswersObj;
|
||||
},
|
||||
tpaSearchQuestionLabel() {
|
||||
return this.getCmsContent(
|
||||
this.widget.tpaSearchQuestion,
|
||||
widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT
|
||||
);
|
||||
},
|
||||
searchInstructionsText() {
|
||||
return this.getCmsContent(
|
||||
this.widget.searchInstructions,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
},
|
||||
shopNotListedModalLink() {
|
||||
return this.getCmsContent(
|
||||
this.widget.shopNotListedLink,
|
||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||
);
|
||||
},
|
||||
radiusInMiles() {
|
||||
switch (this.filter) {
|
||||
case '25 miles':
|
||||
return 25;
|
||||
case '50 miles':
|
||||
return 50;
|
||||
case '100 miles':
|
||||
return 100;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
noNetworkShopsAlertHeaderText() {
|
||||
return this.getCmsContent(
|
||||
this.widget.noNetworkShopsAlert,
|
||||
widgetFields.ALERT_WIDGET.HEADLINE_TEXT
|
||||
)?.replaceAll('{custom:radiusInMiles}', this.radiusInMiles);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
async filter() {
|
||||
// TODO right now this results in getProviders being called once more than it needs to be
|
||||
this.providers = await this.getProviders();
|
||||
},
|
||||
providers(newProviders) {
|
||||
this.selectedProviderNumber = newProviders?.length === 1 ?? false
|
||||
? newProviders[0].value
|
||||
: '';
|
||||
}
|
||||
},
|
||||
methods:
|
||||
{
|
||||
async getProviders() {
|
||||
const getTpaProvidersResult = await useMainStore().getTpaProviders(this.zipCode, this.radiusInMiles);
|
||||
return getTpaProvidersResult?.data ?? [];
|
||||
},
|
||||
doNotSeeMyShopLinkClick() {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_DO_NOT_SEE_MY_SHOP_LINK,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
async searchClick() {
|
||||
this.providers = await this.getProviders();
|
||||
},
|
||||
backButtonAction() {
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
forwardButtonAction() {
|
||||
return this.navigateForward();
|
||||
},
|
||||
navigateForward() {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
getCustomValueFromString(str) {
|
||||
switch (str) {
|
||||
case 'radiusInMiles':
|
||||
return this.radiusInMiles;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
setFilter(filter) {
|
||||
this.filter = filter;
|
||||
},
|
||||
setProviders(providers) {
|
||||
this.providers = providers;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search p{
|
||||
margin-bottom:0.75rem;
|
||||
|
||||
.search-question {
|
||||
font-size: $h5-font-size;
|
||||
}
|
||||
|
||||
.darker-gray {
|
||||
color: map-get($colors, "darker-gray");
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -154,7 +154,8 @@ router.navigateWithoutSaving = (
|
|||
optionalParams = {},
|
||||
optionalPageData = {}
|
||||
) => {
|
||||
navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData);
|
||||
// TODO does not save session
|
||||
navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData);
|
||||
};
|
||||
|
||||
// Get route information by page name.
|
||||
|
|
@ -252,7 +253,7 @@ function navigate(
|
|||
query: Object.assign(optionalQuery, {
|
||||
issPage: matchingScenarioMap.destinationIssPageValue
|
||||
}),
|
||||
params: optionalParams
|
||||
params: optionalParams // TODO I don't think this actually works
|
||||
});
|
||||
} else if (matchingScenarioMap.destinationUrl) {
|
||||
navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery);
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ const navigationScenarios = Object.freeze({
|
|||
CLICKED_BACK_WITH_REPAIR: 'CLICKED_BACK_WITH_REPAIR',
|
||||
CLICKED_FORWARD_WITH_INVALID_STATE: 'CLICKED_FORWARD_WITH_INVALID_STATE',
|
||||
|
||||
// TPA Search
|
||||
CLICKED_DO_NOT_SEE_MY_SHOP_LINK: 'CLICKED_DO_NOT_SEE_MY_SHOP_LINK',
|
||||
|
||||
// Provider Preference
|
||||
CLICKED_FORWARD_WITH_SAFELITE: 'CLICKED_FORWARD_WITH_SAFELITE',
|
||||
CLICKED_FORWARD_WITH_TPA_ENABLED: 'CLICKED_FORWARD_WITH_TPA_ENABLED',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
const routerParams = Object.freeze({
|
||||
DISPLAY_VEHICLE_CHANGE_ALERT: 'displayVehicleChangeAlert',
|
||||
SAVE_SESSION_SYNCHRONOUS: 'saveSessionSynchronous'
|
||||
SAVE_SESSION_SYNCHRONOUS: 'saveSessionSynchronous',
|
||||
NOT_SEEING_PREFERRED_SHOP: 'notSeeingPreferredShop'
|
||||
});
|
||||
|
||||
export default routerParams;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ const routingTable = () => [
|
|||
destinationIssPageValue: issPageValues.VEHICLE_LOOKUP
|
||||
},
|
||||
{
|
||||
scenario: issPageValues.BAILOUT_PAGE,
|
||||
scenario: issPageValues.BAILOUT_PAGE, // TODO is this a bug
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
},
|
||||
{
|
||||
|
|
@ -666,6 +666,10 @@ const routingTable = () => [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||
destinationIssPageValue: issPageValues.TPA_SUBMIT
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_DO_NOT_SEE_MY_SHOP_LINK,
|
||||
destinationIssPageValue: issPageValues.BAILOUT_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -416,12 +416,12 @@ export const useMainStore = defineStore({
|
|||
order.customer.address.streetAddress = insured?.address;
|
||||
order.customer.address.city = insured?.city;
|
||||
order.customer.address.state = insured?.state;
|
||||
order.customer.address.zipCode = insured?.zipCode;
|
||||
order.customer.address.zipCode = insured?.zipCode?.toString();
|
||||
order.customer.firstName = insured?.firstName;
|
||||
order.customer.lastName = insured?.lastName;
|
||||
|
||||
// populate additional fields
|
||||
order.serviceLocation.zipCode = insured?.zipCode;
|
||||
order.serviceLocation.zipCode = insured?.zipCode?.toString();
|
||||
order.policy.policyData = responsePolicy.policyData;
|
||||
|
||||
// populate vehicles
|
||||
|
|
@ -806,6 +806,43 @@ export const useMainStore = defineStore({
|
|||
});
|
||||
},
|
||||
|
||||
// TODO what info is returned here
|
||||
getTpaProviders(zipCode, radius) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (radius === 25) {
|
||||
resolve({});
|
||||
} else if (radius === 50) {
|
||||
resolve({
|
||||
data: [
|
||||
{
|
||||
buttonLabel: 'USA Auto Glass',
|
||||
buttonLabelSubCopy: '1.5 mi',
|
||||
buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085<br>614-123-5555',
|
||||
value: '000123'
|
||||
}
|
||||
]
|
||||
});
|
||||
} else {
|
||||
resolve({
|
||||
data: [
|
||||
{
|
||||
buttonLabel: 'USA Auto Glass',
|
||||
buttonLabelSubCopy: '1.5 mi',
|
||||
buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085<br>614-123-5555',
|
||||
value: '000123'
|
||||
},
|
||||
{
|
||||
buttonLabel: 'USA Auto Glass',
|
||||
buttonLabelSubCopy: '1.5 mi',
|
||||
buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085<br>614-123-5555',
|
||||
value: '000123'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async getSupportingItems() {
|
||||
const glassPartsArray = this.order.lineItems.glassParts ?? [];
|
||||
const { carId } = this.order.vehicle;
|
||||
|
|
@ -989,7 +1026,7 @@ export const useMainStore = defineStore({
|
|||
streetAddress2: customer.address?.streetAddress2,
|
||||
city: customer.address?.city,
|
||||
state: customer.address?.state,
|
||||
zipCode: customer.address?.zipCode
|
||||
zipCode: customer.address?.zipCode?.toString()
|
||||
},
|
||||
emailAddress: contactInfo.emailAddress,
|
||||
firstName: contactInfo.firstName || customer.firstName,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
$page-side-padding: 1.5rem;
|
||||
$font-size: 0.875rem;
|
||||
|
||||
.page-container-grouped-styles {
|
||||
> div.main-content-container {
|
||||
|
|
@ -19,13 +20,14 @@ $page-side-padding: 1.5rem;
|
|||
|
||||
p {
|
||||
line-height: 1.5rem;
|
||||
font-size: $font-size;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.875rem;
|
||||
font-size: $font-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,4 +3,7 @@ $svg-date-picker-forward-button: "data:image/svg+xml,%3Csvg width='7' height='12
|
|||
$svg-drop-off-alert: "data:image/svg+xml,%3Csvg viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_13957_112512)'%3E%3Cpath d='M5.99865 0C4.81147 4.82643e-07 3.65095 0.352111 2.66392 1.01179C1.67688 1.67146 0.907678 2.60907 0.45361 3.70599C-0.000459241 4.80291 -0.11899 6.00986 0.113013 7.17415C0.345015 8.33845 0.917126 9.40778 1.75697 10.2469C2.59682 11.086 3.66666 11.6571 4.83117 11.8881C5.99567 12.119 7.20251 11.9994 8.29902 11.5443C9.39553 11.0893 10.3324 10.3192 10.9912 9.33159C11.65 8.34396 12.0011 7.18313 12 5.99594C11.9971 4.40566 11.3638 2.88142 10.2388 1.75742C9.11375 0.633431 7.58894 0.00143011 5.99865 0V0ZM5.99865 11.2478C4.96135 11.2473 3.94748 10.9392 3.08518 10.3627C2.22288 9.7861 1.55085 8.96685 1.15401 8.00846C0.75718 7.05006 0.653353 5.99554 0.855656 4.97815C1.05796 3.96077 1.55731 3.02618 2.29061 2.29251C3.0239 1.55884 3.95823 1.059 4.97551 0.856176C5.99279 0.653349 7.04737 0.756633 8.00597 1.15297C8.96457 1.54931 9.78416 2.22092 10.3612 3.08293C10.9382 3.94493 11.2467 4.95864 11.2478 5.99594C11.2478 7.38835 10.6949 8.72377 9.71053 9.70861C8.7262 10.6934 7.39106 11.2471 5.99865 11.2478V11.2478Z' fill='%2306577C'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.22736 8.84695C6.30613 8.76818 6.35038 8.66135 6.35038 8.54996V5.30996C6.35038 5.19857 6.30613 5.09174 6.22736 5.01298C6.1486 4.93421 6.04177 4.88996 5.93038 4.88996C5.81899 4.88996 5.71216 4.93421 5.63339 5.01298C5.55463 5.09174 5.51038 5.19857 5.51038 5.30996V8.54996C5.51038 8.66135 5.55463 8.76818 5.63339 8.84695C5.71216 8.92571 5.81899 8.96996 5.93038 8.96996C6.04177 8.96996 6.1486 8.92571 6.22736 8.84695ZM5.69704 3.97918C5.76611 4.02533 5.84731 4.04996 5.93038 4.04996C5.98558 4.05012 6.04026 4.03936 6.09129 4.01831C6.14232 3.99726 6.18868 3.96633 6.22771 3.9273C6.26675 3.88827 6.29768 3.8419 6.31873 3.79088C6.33978 3.73985 6.35053 3.68516 6.35038 3.62996C6.35038 3.54689 6.32574 3.46569 6.27959 3.39662C6.23344 3.32755 6.16785 3.27372 6.0911 3.24193C6.01436 3.21014 5.92991 3.20183 5.84844 3.21803C5.76697 3.23424 5.69213 3.27424 5.63339 3.33298C5.57465 3.39171 5.53465 3.46655 5.51845 3.54802C5.50224 3.6295 5.51056 3.71394 5.54235 3.79069C5.57414 3.86743 5.62797 3.93303 5.69704 3.97918Z' fill='%2306577C'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_13957_112512'%3E%3Crect width='12' height='12' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A";
|
||||
$svg-shop-list-button-green-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 12C3.19159 12 0.5 9.30841 0.5 6C0.5 2.69159 3.19159 0 6.5 0C9.80841 0 12.5 2.69159 12.5 6C12.5 9.30841 9.80841 12 6.5 12ZM6.5 0.785047C3.62449 0.785047 1.28505 3.12449 1.28505 6C1.28505 8.87551 3.62449 11.215 6.5 11.215C9.37551 11.215 11.715 8.87551 11.715 6C11.715 3.12449 9.37551 0.785047 6.5 0.785047Z' fill='%23006A36'/%3E%3Cpath d='M5.697 7.95252C5.5927 7.95252 5.49289 7.91102 5.41999 7.837L3.90597 6.32299C3.75233 6.16934 3.75233 5.92149 3.90597 5.76785C4.05962 5.6142 4.30747 5.6142 4.46111 5.76785L5.69812 7.00373L8.53999 4.16186C8.69364 4.00822 8.94149 4.00822 9.09513 4.16186C9.24878 4.31551 9.24878 4.56336 9.09513 4.717L5.97626 7.83588C5.90224 7.9099 5.80242 7.9514 5.69925 7.9514L5.697 7.95252Z' fill='%23006A36'/%3E%3C/svg%3E%0A";
|
||||
$svg-shop-list-button-orange-availability: "data:image/svg+xml,%3Csvg viewBox='0 0 13 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.5 0C9.81368 0 12.5 2.68632 12.5 6C12.5 9.31368 9.81368 12 6.5 12C3.18632 12 0.5 9.31368 0.5 6C0.5 2.68632 3.18632 0 6.5 0ZM6.5 0.84C3.6548 0.84 1.34 3.1548 1.34 6C1.34 8.8452 3.6548 11.16 6.5 11.16C9.3452 11.16 11.66 8.8452 11.66 6C11.66 3.1548 9.3452 0.84 6.5 0.84ZM7.90018 4.00596C8.06422 3.84204 8.33002 3.84192 8.49406 4.00596C8.6581 4.17 8.6581 4.43592 8.49406 4.59996L7.0939 6L8.49406 7.40004C8.6581 7.56408 8.6581 7.83 8.49406 7.99404C8.4121 8.076 8.30458 8.11704 8.19706 8.11704C8.08966 8.11704 7.98214 8.076 7.90018 7.99404L6.50002 6.594L5.09986 7.99404C5.01778 8.076 4.91038 8.11704 4.80286 8.11704C4.69546 8.11704 4.58794 8.076 4.50598 7.99404C4.34182 7.83 4.34182 7.56408 4.50598 7.40004L5.90614 6L4.50598 4.59996C4.34182 4.43592 4.34182 4.17 4.50598 4.00596C4.66978 3.84192 4.93582 3.84204 5.09986 4.00596L6.50002 5.406L7.90018 4.00596Z' fill='%23E86421'/%3E%3C/svg%3E%0A";
|
||||
$svg-update-zip-text-link: "data:image/svg+xml,%3Csvg viewBox='0 0 13 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.49635 1.00142e-07C5.64734 -0.000153295 4.80722 0.175918 4.0274 0.517444C3.24757 0.858969 2.54443 1.35877 1.96099 1.98626C0.765713 3.27588 0.0999756 4.98141 0.0999756 6.75394C0.0999756 8.52646 0.765713 10.232 1.96099 11.5216L5.98324 15.777C6.04954 15.8475 6.12918 15.9036 6.21736 15.9419C6.30555 15.9802 6.40045 16 6.49635 16C6.59225 16 6.68716 15.9802 6.77534 15.9419C6.86353 15.9036 6.94317 15.8475 7.00946 15.777L11.0317 11.52C12.2391 10.2383 12.909 8.52914 12.8999 6.75394C12.9094 4.97818 12.2394 3.26832 11.0317 1.98626C10.4481 1.35899 9.74493 0.859347 8.96514 0.517839C8.18535 0.176331 7.34532 0.000130509 6.49635 1.00142e-07V1.00142e-07ZM6.49635 9.13131C6.02507 9.13131 5.56437 8.98913 5.17251 8.72275C4.78065 8.45637 4.47524 8.07776 4.29488 7.63479C4.11453 7.19181 4.06734 6.70438 4.15928 6.23412C4.25123 5.76387 4.47817 5.33191 4.81142 4.99287C5.14467 4.65384 5.56925 4.42295 6.03148 4.32941C6.49371 4.23587 6.97282 4.28388 7.40823 4.46736C7.84364 4.65085 8.21579 4.96157 8.47762 5.36023C8.73945 5.7589 8.87921 6.2276 8.87921 6.70707C8.87921 7.34974 8.62837 7.96611 8.18185 8.4207C7.73532 8.87528 7.12964 9.13088 6.49794 9.13131H6.49635Z' fill='%231574A1'/%3E%3C/svg%3E%0A";
|
||||
$svg-update-zip-text-link: "data:image/svg+xml,%3Csvg viewBox='0 0 13 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.49635 1.00142e-07C5.64734 -0.000153295 4.80722 0.175918 4.0274 0.517444C3.24757 0.858969 2.54443 1.35877 1.96099 1.98626C0.765713 3.27588 0.0999756 4.98141 0.0999756 6.75394C0.0999756 8.52646 0.765713 10.232 1.96099 11.5216L5.98324 15.777C6.04954 15.8475 6.12918 15.9036 6.21736 15.9419C6.30555 15.9802 6.40045 16 6.49635 16C6.59225 16 6.68716 15.9802 6.77534 15.9419C6.86353 15.9036 6.94317 15.8475 7.00946 15.777L11.0317 11.52C12.2391 10.2383 12.909 8.52914 12.8999 6.75394C12.9094 4.97818 12.2394 3.26832 11.0317 1.98626C10.4481 1.35899 9.74493 0.859347 8.96514 0.517839C8.18535 0.176331 7.34532 0.000130509 6.49635 1.00142e-07V1.00142e-07ZM6.49635 9.13131C6.02507 9.13131 5.56437 8.98913 5.17251 8.72275C4.78065 8.45637 4.47524 8.07776 4.29488 7.63479C4.11453 7.19181 4.06734 6.70438 4.15928 6.23412C4.25123 5.76387 4.47817 5.33191 4.81142 4.99287C5.14467 4.65384 5.56925 4.42295 6.03148 4.32941C6.49371 4.23587 6.97282 4.28388 7.40823 4.46736C7.84364 4.65085 8.21579 4.96157 8.47762 5.36023C8.73945 5.7589 8.87921 6.2276 8.87921 6.70707C8.87921 7.34974 8.62837 7.96611 8.18185 8.4207C7.73532 8.87528 7.12964 9.13088 6.49794 9.13131H6.49635Z' fill='%231574A1'/%3E%3C/svg%3E%0A";
|
||||
$svg-search-icon: "data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.7817 14.7328L11.8252 10.7762C12.8833 9.45005 13.3936 7.76911 13.2513 6.07849C13.1091 4.38788 12.325 2.81587 11.0601 1.6852C9.79515 0.554524 8.14538 -0.0490261 6.44946 -0.00154744C4.75353 0.0459312 3.14012 0.740836 1.94045 1.94051C0.740775 3.14018 0.0458701 4.75359 -0.00160848 6.44952C-0.0490871 8.14545 0.554463 9.79521 1.68514 11.0601C2.81581 12.325 4.38782 13.1091 6.07843 13.2514C7.76905 13.3937 9.44999 12.8834 10.7762 11.8252L14.7349 15.7839C14.8044 15.8527 14.8869 15.907 14.9774 15.9439C15.068 15.9808 15.165 15.9995 15.2628 15.9989C15.3606 15.9983 15.4573 15.9784 15.5475 15.9405C15.6376 15.9025 15.7194 15.8471 15.7881 15.7776C15.8568 15.708 15.9112 15.6256 15.9481 15.535C15.985 15.4444 16.0036 15.3474 16.0031 15.2496C16.0025 15.1518 15.9826 15.0551 15.9446 14.965C15.9067 14.8748 15.8513 14.7931 15.7817 14.7243V14.7328ZM6.63737 11.7913C5.61803 11.7913 4.62157 11.4891 3.77402 10.9228C2.92646 10.3564 2.26587 9.5515 1.87578 8.60975C1.4857 7.668 1.38363 6.63172 1.5825 5.63196C1.78136 4.6322 2.27222 3.71386 2.99301 2.99307C3.7138 2.27229 4.63214 1.78142 5.6319 1.58256C6.63166 1.38369 7.66793 1.48576 8.60969 1.87585C9.55144 2.26593 10.3564 2.92652 10.9227 3.77408C11.489 4.62163 11.7913 5.61809 11.7913 6.63743C11.7896 8.00382 11.2461 9.31376 10.2799 10.2799C9.3137 11.2461 8.00376 11.7897 6.63737 11.7913Z' fill='%231574A1'/%3E%3C/svg%3E%0A";
|
||||
$svg-select-icon: "data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%231474a2'/%3e%3c/svg%3e";
|
||||
$svg-calendar-picker: "data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.1762 1.52764H13.7656V1.50352C13.7656 1.10476 13.6076 0.722334 13.3263 0.44037C13.0451 0.158406 12.6636 0 12.2659 0C11.8681 0 11.4866 0.158406 11.2054 0.44037C10.9241 0.722334 10.7661 1.10476 10.7661 1.50352V1.52764H5.42476V1.50352C5.42476 1.10476 5.26675 0.722334 4.9855 0.44037C4.70424 0.158406 4.32277 0 3.92501 0C3.52725 0 3.14579 0.158406 2.86453 0.44037C2.58327 0.722334 2.42526 1.10476 2.42526 1.50352V1.52764H1.82376C1.34046 1.52891 0.877316 1.72195 0.53557 2.06455C0.193824 2.40716 0.00127018 2.87146 0 3.35598V14.1717C0.0016909 14.656 0.194379 15.1201 0.536035 15.4626C0.87769 15.8051 1.34059 15.9983 1.82376 16H14.1746C14.6581 15.9987 15.1214 15.8057 15.4634 15.4632C15.8054 15.1206 15.9983 14.6563 16 14.1717V3.35598C15.9987 2.87146 15.8062 2.40716 15.4644 2.06455C15.1227 1.72195 14.6595 1.52891 14.1762 1.52764ZM11.8889 1.50352C11.8889 1.4033 11.9286 1.30718 11.9993 1.23631C12.07 1.16544 12.1659 1.12563 12.2659 1.12563C12.3658 1.12563 12.4617 1.16544 12.5324 1.23631C12.6031 1.30718 12.6428 1.4033 12.6428 1.50352V2.99899C12.6428 3.09922 12.6031 3.19534 12.5324 3.2662C12.4617 3.33707 12.3658 3.37688 12.2659 3.37688C12.1659 3.37688 12.07 3.33707 11.9993 3.2662C11.9286 3.19534 11.8889 3.09922 11.8889 2.99899V1.50352ZM3.54807 1.50352C3.54807 1.4033 3.58778 1.30718 3.65847 1.23631C3.72916 1.16544 3.82504 1.12563 3.92501 1.12563C4.02498 1.12563 4.12086 1.16544 4.19155 1.23631C4.26224 1.30718 4.30195 1.4033 4.30195 1.50352V2.99899C4.30195 3.09922 4.26224 3.19534 4.19155 3.2662C4.12086 3.33707 4.02498 3.37688 3.92501 3.37688C3.82504 3.37688 3.72916 3.33707 3.65847 3.2662C3.58778 3.19534 3.54807 3.09922 3.54807 2.99899V1.50352ZM14.8772 14.1717C14.8747 14.3573 14.8001 14.5345 14.6691 14.6658C14.5382 14.797 14.3614 14.8719 14.1762 14.8744H1.82536C1.63995 14.8723 1.4627 14.7976 1.33144 14.6663C1.20018 14.5351 1.12531 14.3575 1.12281 14.1717V6.59296H14.8772V14.1717Z' fill='%23167CAC'/%3E%3Cpath d='M2.33063 11.282H3.93464V12.6006C3.93464 12.7499 3.99379 12.8931 4.09907 12.9986C4.20435 13.1041 4.34715 13.1634 4.49604 13.1634C4.64494 13.1634 4.78773 13.1041 4.89301 12.9986C4.9983 12.8931 5.05745 12.7499 5.05745 12.6006V11.282H7.46346V12.6006C7.46346 12.7499 7.52261 12.8931 7.62789 12.9986C7.73318 13.1041 7.87597 13.1634 8.02486 13.1634C8.17376 13.1634 8.31655 13.1041 8.42184 12.9986C8.52712 12.8931 8.58627 12.7499 8.58627 12.6006V11.282H10.9923V12.6006C10.9923 12.7499 11.0514 12.8931 11.1567 12.9986C11.262 13.1041 11.4048 13.1634 11.5537 13.1634C11.7026 13.1634 11.8454 13.1041 11.9507 12.9986C12.0559 12.8931 12.1151 12.7499 12.1151 12.6006V11.282H13.7191C13.868 11.282 14.0108 11.2227 14.1161 11.1172C14.2214 11.0116 14.2805 10.8685 14.2805 10.7192C14.2805 10.57 14.2214 10.4268 14.1161 10.3213C14.0108 10.2157 13.868 10.1564 13.7191 10.1564H12.1151V8.84425C12.1151 8.69498 12.0559 8.55183 11.9507 8.44628C11.8454 8.34073 11.7026 8.28143 11.5537 8.28143C11.4048 8.28143 11.262 8.34073 11.1567 8.44628C11.0514 8.55183 10.9923 8.69498 10.9923 8.84425V10.1628H8.58627V8.84425C8.58627 8.69498 8.52712 8.55183 8.42184 8.44628C8.31655 8.34073 8.17376 8.28143 8.02486 8.28143C7.87597 8.28143 7.73318 8.34073 7.62789 8.44628C7.52261 8.55183 7.46346 8.69498 7.46346 8.84425V10.1628H5.05745V8.84425C5.05745 8.69498 4.9983 8.55183 4.89301 8.44628C4.78773 8.34073 4.64494 8.28143 4.49604 8.28143C4.34715 8.28143 4.20435 8.34073 4.09907 8.44628C3.99379 8.55183 3.93464 8.69498 3.93464 8.84425V10.1628H2.33063C2.18174 10.1628 2.03894 10.2221 1.93366 10.3277C1.82837 10.4332 1.76923 10.5764 1.76923 10.7257C1.76923 10.8749 1.82837 11.0181 1.93366 11.1236C2.03894 11.2292 2.18174 11.2885 2.33063 11.2885V11.282Z' fill='%23167CAC'/%3E%3C/svg%3E";
|
||||
Loading…
Reference in a new issue