Merge branch 'develop' into feature/Digital/SSR-241
This commit is contained in:
commit
cba68329d4
9 changed files with 309 additions and 13 deletions
161
src/layouts/provider-preference/provider-preference.spec.js
Normal file
161
src/layouts/provider-preference/provider-preference.spec.js
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios';
|
||||||
|
import { issPageValues } from '@/router/router-constants/issPage-values';
|
||||||
|
import { queryStrings } from '@/constants/query-strings';
|
||||||
|
import { GaActions } from "@/constants/analytics";
|
||||||
|
import ProviderPreference from "@/layouts/provider-preference/provider-preference.vue"
|
||||||
|
import { useMainStore } from '@/store';
|
||||||
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
|
import { mapStores } from "pinia";
|
||||||
|
|
||||||
|
const pinia = createTestingPinia();
|
||||||
|
useMainStore(pinia);
|
||||||
|
|
||||||
|
const ProviderPreferenceMockData = {
|
||||||
|
QuestionText: 'Which auto glass shop would you like to work with?',
|
||||||
|
Answers: [
|
||||||
|
{
|
||||||
|
Name: 'Schedule with Safelite',
|
||||||
|
Text: 'Schedule with Safelite',
|
||||||
|
SubText: '',
|
||||||
|
ImageId: '',
|
||||||
|
SubWidgetName: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: 'Find another shop',
|
||||||
|
Text: 'Find another shop',
|
||||||
|
SubText: '',
|
||||||
|
ImageId: '',
|
||||||
|
SubWidgetName: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const siteFooterWidgetMockData = {
|
||||||
|
ForwardButtonText: 'Continue',
|
||||||
|
};
|
||||||
|
|
||||||
|
function setupMocks() {
|
||||||
|
const mockRoute = {
|
||||||
|
query: {
|
||||||
|
issPage: 'provider-preference',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockRouter = {
|
||||||
|
navigate: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(ProviderPreference, {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
SelectedshopLocation: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
mixins: [
|
||||||
|
{
|
||||||
|
computed: {
|
||||||
|
...mapStores(useMainStore),
|
||||||
|
GaActions() {
|
||||||
|
return GaActions;
|
||||||
|
},
|
||||||
|
queryStrings() {
|
||||||
|
return queryStrings;
|
||||||
|
},
|
||||||
|
navigationScenarios() {
|
||||||
|
return navigationScenarios;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn((cmsWidgetName, fieldName) => {
|
||||||
|
if (cmsWidgetName === 'SiteFooterWidget') {
|
||||||
|
if (fieldName === 'ForwardButtonText') {
|
||||||
|
return siteFooterWidgetMockData.ForwardButtonText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmsWidgetName === 'ProviderPreference') {
|
||||||
|
if (fieldName === 'QuestionText') {
|
||||||
|
return ProviderPreferenceMockData.QuestionText;
|
||||||
|
}
|
||||||
|
if (fieldName === 'Answers') {
|
||||||
|
return ProviderPreferenceMockData.Answers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}),
|
||||||
|
getFooterInfoBoxHeight: jest.fn(() => 80),
|
||||||
|
getPageNameByQueryString: jest.fn(() => "provider-preference"),
|
||||||
|
pushEventToGA: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
mocks: {
|
||||||
|
$route: mockRoute,
|
||||||
|
$router: mockRouter,
|
||||||
|
},
|
||||||
|
stubs: {
|
||||||
|
SiteHeader: true,
|
||||||
|
SiteSubHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { mockRoute, mockRouter, wrapper };
|
||||||
|
}
|
||||||
|
describe('provider-preference.vue', () => {
|
||||||
|
test('"Continue" button is disabled when no Shop Location is selected.', () => {
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
|
||||||
|
const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');
|
||||||
|
expect(continueButton.attributes()['aria-disabled']).toBe('true');
|
||||||
|
});
|
||||||
|
test('"Continue" button is enabled after a Shop Location is selected.', async () => {
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
|
||||||
|
const ProviderPreferenceWrapper = wrapper.findComponent({ name: 'provider-preference' });
|
||||||
|
// Not using initializeComponent() because it doesn't trigger reactivity in test.
|
||||||
|
await ProviderPreferenceWrapper.setData({
|
||||||
|
questionTextFromCms: ProviderPreferenceMockData.QuestionText,
|
||||||
|
answersFromCms: ProviderPreferenceMockData.Answers,
|
||||||
|
SelectedshopLocation:"Schedule with Safelite"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');
|
||||||
|
expect(continueButton.attributes()['aria-disabled']).toBe('false');
|
||||||
|
});
|
||||||
|
test('Select "Schedule with Safelite" then click "Continue". Navigates to "service-location" page.', async () => {
|
||||||
|
const { mockRoute, mockRouter, wrapper } = setupMocks();
|
||||||
|
|
||||||
|
const ProviderPreferenceWrapper = wrapper.findComponent({ name: 'provider-preference' });
|
||||||
|
// Not using initializeComponent() because it doesn't trigger reactivity in test.
|
||||||
|
await ProviderPreferenceWrapper.setData({
|
||||||
|
questionTextFromCms: ProviderPreferenceMockData.QuestionText,
|
||||||
|
answersFromCms: ProviderPreferenceMockData.Answers,
|
||||||
|
SelectedshopLocation:"Schedule with Safelite"
|
||||||
|
});
|
||||||
|
|
||||||
|
const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');
|
||||||
|
await continueButton.trigger('click');
|
||||||
|
|
||||||
|
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockRouter.navigate)
|
||||||
|
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, mockRoute);
|
||||||
|
/**
|
||||||
|
* To Do: find a way to confirm that Vue Router really routes to the expected page.
|
||||||
|
* Currently just trust that our custom navigate() method does its job properly.
|
||||||
|
*/
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Click the back button, trigger navigate function from Vue Router with CLICKED_BACK parameter.', async () => {
|
||||||
|
const { mockRoute, mockRouter, wrapper } = setupMocks();
|
||||||
|
|
||||||
|
await wrapper.get('[data-test-id="site-footer-back-button"]').trigger('click');
|
||||||
|
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockRouter.navigate)
|
||||||
|
.toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, mockRoute);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -17,11 +17,12 @@
|
||||||
:answers="answersFromCms"
|
:answers="answersFromCms"
|
||||||
buttonTypeString="listButton"
|
buttonTypeString="listButton"
|
||||||
isRequired
|
isRequired
|
||||||
|
v-model="SelectedshopLocation"
|
||||||
validationRules="questions-required" />
|
validationRules="questions-required" />
|
||||||
|
|
||||||
<siteFooter
|
<siteFooter
|
||||||
cmsWidgetName="SiteFooterWidget"
|
cmsWidgetName="SiteFooterWidget"
|
||||||
:isForwardActionDisabled="!meta.valid"
|
:isForwardActionDisabled="isForwardActionDisabled"
|
||||||
@backClicked="backButtonAction"
|
@backClicked="backButtonAction"
|
||||||
@forwardClicked="forwardButtonAction"
|
@forwardClicked="forwardButtonAction"
|
||||||
ref="siteFooter"
|
ref="siteFooter"
|
||||||
|
|
@ -50,10 +51,8 @@ import shoppreferenceModal from '@/layouts/provider-preference/shoppreference-mo
|
||||||
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
export default {
|
export default {
|
||||||
name: "provider-preference",
|
name: "provider-preference",
|
||||||
props:{
|
|
||||||
//answersToDisplay: Array
|
|
||||||
},
|
|
||||||
mixins: [baseFormMixin],
|
mixins: [baseFormMixin],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
siteFooter,
|
siteFooter,
|
||||||
siteHeader,
|
siteHeader,
|
||||||
|
|
@ -63,7 +62,9 @@ export default {
|
||||||
buttonQuestion
|
buttonQuestion
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {
|
||||||
|
SelectedshopLocation : null,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||||
|
|
@ -80,11 +81,14 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isForwardActionDisabled() {
|
||||||
|
return this.SelectedshopLocation === null;
|
||||||
|
},
|
||||||
ProviderPreferenceHeaderText(){
|
ProviderPreferenceHeaderText(){
|
||||||
return this.getCmsContent("ProviderPreference", "HeaderText");
|
return this.getCmsContent("ProviderPreference", "HeaderText");
|
||||||
},
|
},
|
||||||
ProviderPreferenceBodyText(){
|
ProviderPreferenceBodyText(){
|
||||||
return this.getCmsContent("ProviderPreference", "BodyText");
|
return this.getCmsContent("ProviderPreference", "BodyText");
|
||||||
},
|
},
|
||||||
questionText() {
|
questionText() {
|
||||||
return this.getCmsContent("ServiceLocationQuestion", "QuestionText");
|
return this.getCmsContent("ServiceLocationQuestion", "QuestionText");
|
||||||
|
|
@ -103,7 +107,12 @@ export default {
|
||||||
*/
|
*/
|
||||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
},
|
},
|
||||||
async forwardButtonAction() {},
|
forwardButtonAction() {
|
||||||
|
if(this.SelectedshopLocation == "Schedule with Safelite")
|
||||||
|
{
|
||||||
|
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, this.$route);
|
||||||
|
}
|
||||||
|
},
|
||||||
resetDependentState() {},
|
resetDependentState() {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import shoppreferenceModal from '@/layouts/provider-preference/shoppreference-modal/shoppreference-modal.vue';
|
||||||
|
|
||||||
|
describe("modal.vue", () => {
|
||||||
|
it("Should display header text when HeaderText is defined in the CMS", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(shoppreferenceModal, {
|
||||||
|
mixins: [mockMixin],
|
||||||
|
props: {
|
||||||
|
cmsWidgetName: "test",
|
||||||
|
},
|
||||||
|
attachTo: document.body,
|
||||||
|
});
|
||||||
|
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should display body text when BodyText is defined in the CMS", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(shoppreferenceModal, {
|
||||||
|
mixins: [mockMixin],
|
||||||
|
props: {
|
||||||
|
cmsWidgetName: "test",
|
||||||
|
},
|
||||||
|
attachTo: document.body,
|
||||||
|
});
|
||||||
|
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["BodyText"]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const mockMixin = {
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
||||||
|
return mockCmsContent[cmsFieldName];
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockCmsContent = {
|
||||||
|
HeaderText: "Sample header text here.",
|
||||||
|
BodyText: "Sample body text here.",
|
||||||
|
FooterText: "Sample footer text here.",
|
||||||
|
};
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body ps-4 pe-4 pt-4 pb-4">
|
<div class="modal-body ps-4 pe-4 pt-4 pb-4">
|
||||||
<h5 class="mb-4 text-center header-text" v-html="ModalHeadline"></h5>
|
<h5 class="mb-4 text-center header-text" v-html="ModalHeadline"></h5>
|
||||||
<p class="mb-2 subheader-text" v-html="ModalSubheadertext"></p>
|
<p class="mb-0 small" v-html="ModalBodyText"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer px-5 py-4">
|
<div class="modal-footer px-5 py-4">
|
||||||
<buttonMain
|
<buttonMain
|
||||||
|
|
@ -47,8 +47,8 @@ export default {
|
||||||
ModalHeadline() {
|
ModalHeadline() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
|
||||||
},
|
},
|
||||||
ModalSubheadertext() {
|
ModalBodyText() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
|
return this.getCmsContent(this.cmsWidgetName, "BodyText");
|
||||||
},
|
},
|
||||||
ModalCloseButtonText() {
|
ModalCloseButtonText() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "FooterText");
|
return this.getCmsContent(this.cmsWidgetName, "FooterText");
|
||||||
|
|
@ -76,9 +76,6 @@ export default {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
||||||
}
|
}
|
||||||
.subheader-text {
|
|
||||||
color: #525656;
|
|
||||||
}
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
.btn-close {
|
.btn-close {
|
||||||
|
|
|
||||||
71
src/layouts/service-location/service-location.vue
Normal file
71
src/layouts/service-location/service-location.vue
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Form @submit="onSubmit" @invalidSubmit="onInvalidSubmit" v-slot="{ meta }">
|
||||||
|
<div class="page-container-grouped-styles overflow-auto">
|
||||||
|
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||||
|
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
|
||||||
|
<div class="fade-on-route-transition sub-container make-tall px-5">
|
||||||
|
<p>Placeholder for service-location page</p>
|
||||||
|
<siteFooter
|
||||||
|
cmsWidgetName="SiteFooterWidget"
|
||||||
|
:isForwardActionDisabled="!meta.valid"
|
||||||
|
@backClicked="backButtonAction"
|
||||||
|
@forwardClicked="forwardButtonAction"
|
||||||
|
ref="siteFooter"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// Import Supporting Files
|
||||||
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
|
// Import Component
|
||||||
|
import baseFormMixin from "@/mixins/base-form-mixin";
|
||||||
|
import { Form } from "vee-validate";
|
||||||
|
import siteFooter from "@/iss-components/site-footer/site-footer.vue";
|
||||||
|
import siteHeader from "@/iss-components/site-header/site-header.vue";
|
||||||
|
import siteSubHeader from "@/iss-components/site-sub-header/site-sub-header.vue";
|
||||||
|
export default {
|
||||||
|
name: "provider-preference",
|
||||||
|
mixins: [baseFormMixin],
|
||||||
|
components: {
|
||||||
|
siteFooter,
|
||||||
|
siteHeader,
|
||||||
|
siteSubHeader,
|
||||||
|
Form,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||||
|
// Settle promises and get results
|
||||||
|
const promiseResultMap = [
|
||||||
|
{
|
||||||
|
resultKey: "cmsContent",
|
||||||
|
promise: cmsContentPromise,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
|
next((vm) => {
|
||||||
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisiteValid() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
backButtonAction() {
|
||||||
|
/**
|
||||||
|
* this.navigationScenarios comes from base-mixin
|
||||||
|
*/
|
||||||
|
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
|
},
|
||||||
|
async forwardButtonAction() {},
|
||||||
|
resetDependentState() {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -18,6 +18,7 @@ export const issPageValues = {
|
||||||
CAPABILITY_QUESTIONS: "capability-questions",
|
CAPABILITY_QUESTIONS: "capability-questions",
|
||||||
COVERAGE_STATEMENT: "coverage-statement",
|
COVERAGE_STATEMENT: "coverage-statement",
|
||||||
PROVIDER_PREFERENCE: "provider-preference",
|
PROVIDER_PREFERENCE: "provider-preference",
|
||||||
|
SERVICE_LOCATION: "service-location",
|
||||||
REVEAL: "reveal",
|
REVEAL: "reveal",
|
||||||
ESTIMATE: "estimate",
|
ESTIMATE: "estimate",
|
||||||
ADDRESS_VEHICLES: "address-vehicles",
|
ADDRESS_VEHICLES: "address-vehicles",
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ const navigationScenarios = {
|
||||||
CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS: "CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS",
|
CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS: "CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS",
|
||||||
|
|
||||||
CLICKED_FORWARD_COVERAGE_STATEMENT: "CLICKED_FORWARD_COVERAGE_STATEMENT",
|
CLICKED_FORWARD_COVERAGE_STATEMENT: "CLICKED_FORWARD_COVERAGE_STATEMENT",
|
||||||
|
|
||||||
|
CLICKED_FORWARD_WITH_SAFELITE: "CLICKED_FORWARD_WITH_SAFELITE",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { navigationScenarios };
|
export { navigationScenarios };
|
||||||
|
|
@ -427,6 +427,19 @@ const routingTable = function(store) {
|
||||||
],
|
],
|
||||||
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
issPageValue: issPageValues.PROVIDER_PREFERENCE,
|
||||||
|
maps: [
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
|
destinationIssPageValue: issPageValues.COVERAGE_STATEMENT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE,
|
||||||
|
destinationIssPageValue: issPageValues.SERVICE_LOCATION,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ export default {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
|
border-radius:50%;
|
||||||
animation: rotation 1s infinite linear;
|
animation: rotation 1s infinite linear;
|
||||||
@keyframes rotation {
|
@keyframes rotation {
|
||||||
100% {
|
100% {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue