Merge pull request #80 from Safelite/feature/Digital/SSR-175

SSR-175
This commit is contained in:
Kulbhushan Kaushik 2022-12-22 07:55:58 -05:00 committed by GitHub
commit e60089a32a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 599 additions and 11 deletions

View file

@ -9,7 +9,7 @@ describe("site-footer.vue", () => {
});
wrapper.vm.buttonClick();
// Assert
expect(wrapper.emitted()["ForwardClicked"][0]).toHaveBeenCalled;
expect(wrapper.emitted()["forwardClicked"][0]).toHaveBeenCalled;
});
it("Should emit BackClicked on link click", async () => {
@ -19,7 +19,7 @@ describe("site-footer.vue", () => {
});
wrapper.vm.linkClick();
// Assert
expect(wrapper.emitted()["BackClicked"][0]).toHaveBeenCalled;
expect(wrapper.emitted()["backClicked"][0]).toHaveBeenCalled;
});
it("Should change button text when update button text is called", async () => {

View file

@ -13,7 +13,8 @@
:isDisabled="isForwardActionDisabled"
@click-event="buttonClick"
data-bs-target="#footerModal"
data-bs-dismiss="modal" />
data-bs-dismiss="modal"
data-test-id="site-footer-main-button" />
</div>
<div v-if="!isBackButtonHidden" class="col-auto link-col py-1 text-break">
<textLink
@ -22,7 +23,8 @@
@click-event="linkClick"
href="javascript:void(0)"
data-bs-target="#footerModal"
data-bs-dismiss="modal" />
data-bs-dismiss="modal"
data-test-id="site-footer-back-button" />
</div>
</div>
</footer>
@ -34,6 +36,7 @@ import buttonMain from "@/ux-components/button-main/button-main";
export default {
name: "siteFooter",
emits: ['backClicked', 'forwardClicked'],
props: {
isForwardActionDisabled: Boolean,
isBackButtonHidden: { type: Boolean, default: false },
@ -89,10 +92,10 @@ export default {
document.onkeydown = function (e) {
return false;
};
this.$emit("ForwardClicked");
this.$emit("forwardClicked");
},
linkClick() {
this.$emit("BackClicked");
this.$emit("backClicked");
},
},
};

View file

@ -0,0 +1,7 @@
const VIN_LOOKUP_METHODS = Object.freeze({
manualVin: 'ManualVin',
licensePlate: 'LicensePlate',
homeAddress: 'HomeAddress',
});
export default VIN_LOOKUP_METHODS;

View file

@ -0,0 +1,90 @@
<template>
<VeeValidateForm @submit="onSubmit" @invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles overflow-auto">
<SiteHeader
cms-widget-name="SiteHeaderWidget"
/>
<VehicleBanner
cms-widget-name="VehicleBannerWidget"
:display-generic-vehicle-image="false"
/>
<SiteSubHeader
cms-widget-name="SiteSubHeaderWidget"
/>
<div class="fade-on-route-transition sub-container make-tall">
<p>Address Lookup Page Placeholder</p>
<SiteFooter
cms-widget-name="SiteFooterWidget"
:is-forward-action-disabled="isForwardActionDisabled"
@back-clicked="backButtonAction"
@forward-clicked="forwardButtonAction"
/>
</div>
</div>
</VeeValidateForm>
</template>
<script>
// Import Supporting Files
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper';
import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
// Import Component
import SiteFooter from '@/common-components/site-footer/site-footer.vue';
import SiteHeader from '@/common-components/site-header/site-header.vue';
import SiteSubHeader from '@/common-components/site-sub-header/site-sub-header.vue';
import VehicleBanner from '@/common-components/vehicle-banner/vehicle-banner.vue';
export default {
name: 'address-lookup',
mixins: [BaseFormMixin],
components: {
VeeValidateForm: Form,
SiteFooter,
SiteHeader,
SiteSubHeader,
VehicleBanner,
},
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);
});
},
computed: {
isForwardActionDisabled() {
return true;
}
},
methods: {
arePagePrerequisiteValid() {
return true;
},
backButtonAction() {
/**
* this.navigationScenarios comes from base-mixin
*/
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
return true;
},
resetDependentState() {},
},
};
</script>

View file

@ -0,0 +1,90 @@
<template>
<VeeValidateForm @submit="onSubmit" @invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles overflow-auto">
<SiteHeader
cms-widget-name="SiteHeaderWidget"
/>
<VehicleBanner
cms-widget-name="VehicleBannerWidget"
:display-generic-vehicle-image="false"
/>
<SiteSubHeader
cms-widget-name="SiteSubHeaderWidget"
/>
<div class="fade-on-route-transition sub-container make-tall">
<p>License Plate Lookup Page Placeholder</p>
<SiteFooter
cms-widget-name="SiteFooterWidget"
:is-forward-action-disabled="isForwardActionDisabled"
@back-clicked="backButtonAction"
@forward-clicked="forwardButtonAction"
/>
</div>
</div>
</VeeValidateForm>
</template>
<script>
// Import Supporting Files
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper';
import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
// Import Component
import SiteFooter from '@/common-components/site-footer/site-footer.vue';
import SiteHeader from '@/common-components/site-header/site-header.vue';
import SiteSubHeader from '@/common-components/site-sub-header/site-sub-header.vue';
import VehicleBanner from '@/common-components/vehicle-banner/vehicle-banner.vue';
export default {
name: 'license-plate-lookup',
mixins: [BaseFormMixin],
components: {
VeeValidateForm: Form,
SiteFooter,
SiteHeader,
SiteSubHeader,
VehicleBanner,
},
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);
});
},
computed: {
isForwardActionDisabled() {
return true;
}
},
methods: {
arePagePrerequisiteValid() {
return true;
},
backButtonAction() {
/**
* this.navigationScenarios comes from base-mixin
*/
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
return true;
},
resetDependentState() {},
},
};
</script>

View file

@ -0,0 +1,249 @@
/* eslint-env jest */
import { mount } from '@vue/test-utils';
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios';
import VehicleLookup from './vehicle-lookup.vue';
const vinLookupMethodsMockData = {
QuestionText: 'To find the right part, lets get your VIN. Or we can look it up for you!',
Answers: [
{
Name: 'ManualVin',
Text: 'Provide my VIN manually',
SubText: 'Most specific to your vehicle',
ImageId: '',
SubWidgetName: '',
},
{
Name: 'LicensePlate',
Text: 'Provide my license plate #',
SubText: 'Most accurate VIN match',
ImageId: '',
SubWidgetName: '',
},
{
Name: 'HomeAddress',
Text: 'Provide my home address',
SubText: 'Most convenient VIN match',
ImageId: '',
SubWidgetName: '',
},
],
};
const siteFooterWidgetMockData = {
ForwardButtonText: 'Continue',
};
function setupMocks() {
const mockRoute = {
query: {
issPage: 'vehicle-lookup',
},
};
const mockRouter = {
navigate: jest.fn(),
};
const wrapper = mount(VehicleLookup, {
data() {
return {
selectedVinLookupMethod: null,
};
},
global: {
mixins: [
{
computed: {
navigationScenarios() {
return navigationScenarios;
},
},
methods: {
getCmsContent: jest.fn((cmsWidgetName, fieldName) => {
if (cmsWidgetName === 'SiteFooterWidget') {
if (fieldName === 'ForwardButtonText') {
return siteFooterWidgetMockData.ForwardButtonText;
}
}
if (cmsWidgetName === 'VINLookupMethod') {
if (fieldName === 'QuestionText') {
return vinLookupMethodsMockData.QuestionText;
}
if (fieldName === 'Answers') {
return vinLookupMethodsMockData.Answers;
}
}
return '';
}),
getFooterInfoBoxHeight: jest.fn(() => 80),
},
},
],
mocks: {
$route: mockRoute,
$router: mockRouter,
},
stubs: {
SiteHeader: true,
SiteSubHeader: true,
VehicleBanner: true,
},
},
});
return { mockRoute, mockRouter, wrapper };
}
describe('vehicle-lookup.vue', () => {
test('"Continue" button is disabled when no VIN Lookup method is selected.', () => {
const { wrapper } = setupMocks();
const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');
expect(continueButton.attributes()['aria-disabled']).toBe('true');
/**
* To Do: Review if need to use VeeValidate meta.valid to check whether an element is disabled.
* The issue if relying on VeeValidate meta is not available inside the script
* (can only be used inside a template)
*/
});
test('"Continue" button is enabled after a VIN Lookup method is selected.', async () => {
const { wrapper } = setupMocks();
const vinLookupMethodsWrapper = wrapper.findComponent({ name: 'vin-lookup-methods' });
// Not using initializeComponent() because it doesn't trigger reactivity in test.
await vinLookupMethodsWrapper.setData({
questionTextFromCms: vinLookupMethodsMockData.QuestionText,
answersFromCms: vinLookupMethodsMockData.Answers,
});
const lookupByManualVinOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-ManualVin"]');
await lookupByManualVinOptionLabel.trigger('mousedown');
const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');
expect(continueButton.attributes()['aria-disabled']).toBe('false');
});
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);
});
test('Select "Provide VIN Manually" then click "Continue". Navigates to "vin-lookup" page.', async () => {
const { mockRoute, mockRouter, wrapper } = setupMocks();
const vinLookupMethodsWrapper = wrapper.findComponent({ name: 'vin-lookup-methods' });
// Not using initializeComponent() because it doesn't trigger reactivity in test.
await vinLookupMethodsWrapper.setData({
questionTextFromCms: vinLookupMethodsMockData.QuestionText,
answersFromCms: vinLookupMethodsMockData.Answers,
});
const lookupByManualVinOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-ManualVin"]');
await lookupByManualVinOptionLabel.trigger('mousedown');
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.SELECTED_MANUAL_VIN, 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('Select "Provide My License Plate Number" then click "Continue". Navigates to "license-plate-lookup" page.', async () => {
const { mockRoute, mockRouter, wrapper } = setupMocks();
const vinLookupMethodsWrapper = wrapper.findComponent({ name: 'vin-lookup-methods' });
// Not using initializeComponent() because it doesn't trigger reactivity in test.
await vinLookupMethodsWrapper.setData({
questionTextFromCms: vinLookupMethodsMockData.QuestionText,
answersFromCms: vinLookupMethodsMockData.Answers,
});
const lookupByLicensePlateOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-LicensePlate"]');
await lookupByLicensePlateOptionLabel.trigger('mousedown');
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.SELECTED_LICENSE_PLATE, 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('Select "Provide My Home Address" then click "Continue". Navigates to "address-lookup" page.', async () => {
const { mockRoute, mockRouter, wrapper } = setupMocks();
const vinLookupMethodsWrapper = wrapper.findComponent({ name: 'vin-lookup-methods' });
// Not using initializeComponent() because it doesn't trigger reactivity in test.
await vinLookupMethodsWrapper.setData({
questionTextFromCms: vinLookupMethodsMockData.QuestionText,
answersFromCms: vinLookupMethodsMockData.Answers,
});
const lookupByAddressOptionLabel = wrapper.find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-HomeAddress"]');
await lookupByAddressOptionLabel.trigger('mousedown');
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.SELECTED_HOME_ADDRESS, 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.
*/
});
/**
* To Do: Error message is not displayed when running test
* but in reality it shows up. Need to find out
* how to write a test that verifies the error message is displayed.
*/
// test.only('Error message is diplayed when no selection is made
// and "Continue" button clicked.', async () => {
// const { wrapper } = setupMocks();
// const vinLookupMethodsWrapper = wrapper.findComponent({ name: 'vin-lookup-methods' });
// // Not using initializeComponent() because it doesn't trigger reactivity in test.
// await vinLookupMethodsWrapper.setData({
// questionTextFromCms: vinLookupMethodsMockData.QuestionText,
// answersFromCms: vinLookupMethodsMockData.Answers,
// });
// // const lookupByManualVinOptionLabel =
// // wrapper.find('[data-test-id="vin-lookup-methods-button-question"]
// //label[for="VinLookupMethods-ManualVin"]');
// // await lookupByManualVinOptionLabel.trigger('mousedown');
// const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]');
// await continueButton.trigger('click');
// await flushPromises();
// await nextTick();
// console.log(vinLookupMethodsWrapper.get('.form-test-error').html());
// //const veeValidateForm = wrapper.findComponent()
// //console.log(wrapper.emitted());
// // console.log(vinLookupMethodsWrapper.html());
// // console.log(vinLookupMethodsWrapper.get('.form-test-error').html());
// // console.log(vinLookupMethodsWrapper.get('.form-test-error').text());
// // expect(vinLookupMethodsWrapper.get('.form-test-error').);
// });
});

View file

@ -1,5 +1,8 @@
<template>
<VeeValidateForm>
<VeeValidateForm
@submit="onSubmit"
@invalid-submit="onInvalidSubmit"
>
<div class="page-container-grouped-styles overflow-auto">
<SiteHeader
cms-widget-name="SiteHeaderWidget"
@ -15,10 +18,14 @@
<VinLookupMethods
v-model="selectedVinLookupMethod"
cms-widget-name="VINLookupMethod"
group-name="VinLookupMethods"
ref="VinLookupMethods"
/>
<SiteFooter
cms-widget-name="SiteFooterWidget"
:is-forward-action-disabled="isForwardActionDisabled"
@back-clicked="backButtonAction"
@forward-clicked="forwardButtonAction"
/>
</div>
</div>
@ -29,6 +36,8 @@
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper';
import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin';
import VIN_LOOKUP_METHODS from '@/constants/vin-lookup-methods';
// Import Component
import SiteFooter from '@/common-components/site-footer/site-footer.vue';
@ -39,6 +48,7 @@ import VinLookupMethods from './vin-lookup-methods/vin-lookup-methods.vue';
export default {
name: 'vehicle-lookup',
mixins: [BaseFormMixin],
components: {
VeeValidateForm: Form,
SiteFooter,
@ -70,10 +80,36 @@ export default {
vm.$refs.VinLookupMethods.initializeComponent();
});
},
computed: {
isForwardActionDisabled() {
return this.selectedVinLookupMethod === null;
},
},
methods: {
arePagePrerequisitedValid() {
arePagePrerequisiteValid() {
return true;
},
backButtonAction() {
/**
* this.navigationScenarios comes from base-mixin
*/
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
switch (this.selectedVinLookupMethod) {
case VIN_LOOKUP_METHODS.manualVin:
this.$router.navigate(this.navigationScenarios.SELECTED_MANUAL_VIN, this.$route);
break;
case VIN_LOOKUP_METHODS.licensePlate:
this.$router.navigate(this.navigationScenarios.SELECTED_LICENSE_PLATE, this.$route);
break;
case VIN_LOOKUP_METHODS.homeAddress:
this.$router.navigate(this.navigationScenarios.SELECTED_HOME_ADDRESS, this.$route);
break;
default:
break;
}
},
resetDependentState() {},
},
};

View file

@ -6,6 +6,7 @@
:question-text="questionTextFromCms"
is-required
validation-rules="option-required"
data-test-id="vin-lookup-methods-button-question"
/>
</template>

View file

@ -0,0 +1,90 @@
<template>
<VeeValidateForm @submit="onSubmit" @invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles overflow-auto">
<SiteHeader
cms-widget-name="SiteHeaderWidget"
/>
<VehicleBanner
cms-widget-name="VehicleBannerWidget"
:display-generic-vehicle-image="false"
/>
<SiteSubHeader
cms-widget-name="SiteSubHeaderWidget"
/>
<div class="fade-on-route-transition sub-container make-tall">
<p>VIN Lookup Page Placeholder</p>
<SiteFooter
cms-widget-name="SiteFooterWidget"
:is-forward-action-disabled="isForwardActionDisabled"
@back-clicked="backButtonAction"
@forward-clicked="forwardButtonAction"
/>
</div>
</div>
</VeeValidateForm>
</template>
<script>
// Import Supporting Files
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper';
import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
// Import Component
import SiteFooter from '@/common-components/site-footer/site-footer.vue';
import SiteHeader from '@/common-components/site-header/site-header.vue';
import SiteSubHeader from '@/common-components/site-sub-header/site-sub-header.vue';
import VehicleBanner from '@/common-components/vehicle-banner/vehicle-banner.vue';
export default {
name: 'vin-lookup',
mixins: [BaseFormMixin],
components: {
VeeValidateForm: Form,
SiteFooter,
SiteHeader,
SiteSubHeader,
VehicleBanner,
},
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);
});
},
computed: {
isForwardActionDisabled() {
return true;
}
},
methods: {
arePagePrerequisiteValid() {
return true;
},
backButtonAction() {
/**
* this.navigationScenarios comes from base-mixin
*/
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
return true;
},
resetDependentState() {},
},
};
</script>

View file

@ -5,13 +5,14 @@ export const issPageValues = {
VEHICLE_MODEL: "vehicle-model",
VEHICLE_STYLE: "vehicle-style",
VEHICLE_DAMAGE: "vehicle-damage",
VEHICLE_LOOKUP: "vehicle-lookup",
ADDRESS_LOOKUP: "address-lookup",
LICENSE_PLATE_LOOKUP: "license-plate-lookup",
VIN_LOOKUP: "vin-lookup",
VEHICLE_PARTS: "vehicle-parts",
PART_QUESTIONS: "part-questions",
MOLDING_QUESTIONS: "molding-questions",
CAPABILITY_QUESTIONS: "capability-questions",
LICENSE_PLATE_LOOKUP: "license-plate-lookup",
REVEAL: "reveal",
ESTIMATE: "estimate",
ADDRESS_VEHICLES: "address-vehicles",

View file

@ -65,11 +65,32 @@ const routingTable = function(store) {
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_VIN,
destinationIssPageValue: issPageValues.WELCOME_PAGE,
destinationIssPageValue: issPageValues.VEHICLE_LOOKUP,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
destinationIssPageValue: issPageValues.WELCOME_PAGE,
destinationIssPageValue: issPageValues.VEHICLE_LOOKUP,
},
],
},
{
issPageValue: issPageValues.VEHICLE_LOOKUP,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationIssPageValue: issPageValues.VEHICLE_DAMAGE,
},
{
scenario: navigationScenarios.SELECTED_MANUAL_VIN,
destinationIssPageValue: issPageValues.VIN_LOOKUP,
},
{
scenario: navigationScenarios.SELECTED_LICENSE_PLATE,
destinationIssPageValue: issPageValues.LICENSE_PLATE_LOOKUP,
},
{
scenario: navigationScenarios.SELECTED_HOME_ADDRESS,
destinationIssPageValue: issPageValues.ADDRESS_LOOKUP,
},
],
},