Added logic to validate pages

This commit is contained in:
FrankRua 2022-01-27 17:23:09 -05:00
parent a05d77858e
commit 915cfd5a21
7 changed files with 157 additions and 62 deletions

View file

@ -1,11 +1,9 @@
<template> <template>
<div class="container-fluid shadow rounded-3 p-0 position-relative"> <div class="container-fluid shadow rounded-3 p-0 position-relative">
<funnelHeader ref="funnelHeader" /> <funnelHeader ref="funnelHeader" />
<vehicleBanner ref="vehicleBanner" /> <vehicleBanner ref="vehicleBanner" />
<funnelSubHeader <funnelSubHeader ref="funnelSubHeader" />
ref="funnelSubHeader" </div>
/>
</div>
</template> </template>
<script> <script>
@ -20,24 +18,28 @@ import store from "@/store";
import baseMixin from "@/mixins/base-mixin"; import baseMixin from "@/mixins/base-mixin";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
export default ({ export default {
name: "vehicle-damage", name: "vehicle-damage",
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const damageOptionsPromise = baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.GET_DAMAGE_OPTIONS, {carId: store.getters.vehicle.carId}); const damageOptionsPromise =
baseMixin.methods.dispatchNonBlockingStoreAction(
storeActions.GET_DAMAGE_OPTIONS,
{ carId: store.getters.vehicle.carId }
);
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: "cmsContent", resultKey: "cmsContent",
promise: cmsContentPromise, promise: cmsContentPromise,
}, },
{ {
resultKey: "damageOptions", resultKey: "damageOptions",
promise: damageOptionsPromise promise: damageOptionsPromise,
}, },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
@ -54,12 +56,16 @@ export default ({
); );
// use resultMap.damageOptions for damage options // use resultMap.damageOptions for damage options
}); });
},
methods: {
arePagePrerequisitesValid() {
return store.getters.vehicle.carId !== null;
},
}, },
components: { components: {
funnelHeader, funnelHeader,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
}, },
}); };
</script> </script>

View file

@ -7,22 +7,39 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import { settleAllPromises } from "@/helpers/layout-helper.js"; import { settleAllPromises } from "@/helpers/layout-helper.js";
import { nextTick } from "vue"; import { nextTick } from "vue";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
// Mock Store
jest.mock("@/store", () => ({
getters: {
vehicle: {
year: 2019,
}
}
}));
// Mock fetchCmsContentForPage
jest.mock("@/helpers/cms-content-helper", () => ({
fetchCmsContentForPage: jest.fn(),
}));
// Mock our module for promises. // Mock our module for promises.
jest.mock("@/helpers/layout-helper.js", () => ({ jest.mock("@/helpers/layout-helper.js", () => ({
settleAllPromises: jest.fn(), settleAllPromises: jest.fn(),
})); }));
describe("vehicle-make.vue", () => { describe("vehicle-make.vue", () => {
test("Make question component is initized with api data", async (done) => { test("Make question component is initized with api data", async (done) => {
//Arrange //Arrange
const vehicleMakeQuestionCmsContent = { QuestionText: "What make is your vehicle?" }; const vehicleMakeQuestionCmsContent = { QuestionText: "What make is your vehicle?" };
const makeQuestionInitialData = ["honda", "ford", "dodge"]; const makeQuestionInitialData = ["honda", "ford", "dodge"];
const { wrapper, apiPromise } = setupMocks( { const { wrapper, apiPromise } = setupMocks({
vehicleMakeQuestionCmsContent: vehicleMakeQuestionCmsContent, vehicleMakeQuestionCmsContent: vehicleMakeQuestionCmsContent,
makeQuestionInitialData: makeQuestionInitialData, makeQuestionInitialData: makeQuestionInitialData,
} ); });
//Act //Act
vehicleMake.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-make" } }, undefined, (c) => c(wrapper.vm)); vehicleMake.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-make" } }, undefined, (c) => c(wrapper.vm));
@ -40,7 +57,7 @@ describe("vehicle-make.vue", () => {
//Arrange //Arrange
const pageHeaderWidgetHeaderText = "Select a make to get started"; const pageHeaderWidgetHeaderText = "Select a make to get started";
const { wrapper, apiPromise } = setupMocks( { pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText }); const { wrapper, apiPromise } = setupMocks({ pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText });
//Act //Act
vehicleMake.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-make" } }, undefined, (c) => c(wrapper.vm)); vehicleMake.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-make" } }, undefined, (c) => c(wrapper.vm));
@ -61,7 +78,7 @@ describe("vehicle-make.vue", () => {
LogoImage: LogoImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
} }
const { wrapper, apiPromise } = setupMocks( { SiteHeaderWidget: SiteHeaderWidget}); const { wrapper, apiPromise } = setupMocks({ SiteHeaderWidget: SiteHeaderWidget });
//Act //Act
vehicleMake.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-make" } }, undefined, (c) => c(wrapper.vm)); vehicleMake.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-make" } }, undefined, (c) => c(wrapper.vm));
@ -82,7 +99,7 @@ describe("vehicle-make.vue", () => {
GenericVehicleImage: GenericVehicleImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
} }
const { wrapper, apiPromise } = setupMocks( { VehicleBannerWidget: VehicleBannerWidget}); const { wrapper, apiPromise } = setupMocks({ VehicleBannerWidget: VehicleBannerWidget });
//Act //Act
vehicleMake.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-make" } }, undefined, (c) => c(wrapper.vm)); vehicleMake.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-make" } }, undefined, (c) => c(wrapper.vm));
@ -99,7 +116,7 @@ describe("vehicle-make.vue", () => {
test("BackButtonAction triggers a router.navigate change", async (done) => { test("BackButtonAction triggers a router.navigate change", async (done) => {
//Arrange //Arrange
const { wrapper, apiPromise } = setupMocks( { const { wrapper, apiPromise } = setupMocks({
pageHeaderWidgetHeaderText: "Select a make to get started", pageHeaderWidgetHeaderText: "Select a make to get started",
mountOptionsMockData: { mountOptionsMockData: {
router: { router: {
@ -121,6 +138,30 @@ describe("vehicle-make.vue", () => {
}); });
}); });
describe("vehicle-make.vue", () => {
test("arePagePrerequisitesValid", async (done) => {
//Arrange
const { wrapper, apiPromise } = setupMocks({
pageHeaderWidgetHeaderText: "Select a make to get started",
mountOptionsMockData: {},
});
//Act
vehicleMake.beforeRouteEnter.call(wrapper.vm, { query: { fmgPage: "vehicle-make" } }, undefined, (c) => c(wrapper.vm));
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
await nextTick();
//Assert
apiPromise.finally(() => {
expect(arePagePrerequisitesValid).toBe(true);
done();
});
});
});
function setupMocks({ function setupMocks({
vehicleMakeQuestionCmsContent = {}, vehicleMakeQuestionCmsContent = {},
makeQuestionInitialData = {}, makeQuestionInitialData = {},
@ -134,19 +175,22 @@ function setupMocks({
FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, FunnelSubHeaderWidget: pageHeaderWidgetHeaderText,
VehicleMakeQuestion: vehicleMakeQuestionCmsContent, VehicleMakeQuestion: vehicleMakeQuestionCmsContent,
VehicleBannerWidget: VehicleBannerWidget:
{ {
GenericVehicleImage: GenericVehicleImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
}, },
FunnelHeaderWidget: FunnelHeaderWidget:
{ {
LogoImage: LogoImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
}, },
}, },
makeQuestionInitialData: makeQuestionInitialData, makeQuestionInitialData: makeQuestionInitialData,
}; };
const apiPromise = Promise.resolve(apiResponses); const apiPromise = Promise.resolve(apiResponses);
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
settleAllPromises.mockImplementation(() => apiPromise); settleAllPromises.mockImplementation(() => apiPromise);
//Mock make question methods //Mock make question methods
@ -163,6 +207,7 @@ function setupMocks({
funnelSubHeader.methods = { funnelSubHeader.methods = {
initializeComponent: jest.fn(), initializeComponent: jest.fn(),
}; };
const mountOptions = getMountOptions(mountOptionsMockData); const mountOptions = getMountOptions(mountOptionsMockData);
const wrapper = shallowMount(vehicleMake, mountOptions); const wrapper = shallowMount(vehicleMake, mountOptions);
const makeQuestionWrapper = wrapper.findComponent({ name: "makeQuestion" }); const makeQuestionWrapper = wrapper.findComponent({ name: "makeQuestion" });

View file

@ -5,10 +5,10 @@
<div class="select-car-form rounded text-center"> <div class="select-car-form rounded text-center">
<vehicleBanner ref="vehicleBanner" /> <vehicleBanner ref="vehicleBanner" />
<funnelSubHeader <funnelSubHeader
ref="funnelSubHeader" ref="funnelSubHeader"
:hasBackButton="true" :hasBackButton="true"
backButtonAccessibleText="Change Vehicle Year" backButtonAccessibleText="Change Vehicle Year"
@click-event="backButtonAction" @click-event="backButtonAction"
/> />
<makeQuestion v-model="selectedMake" ref="makeQuestion" /> <makeQuestion v-model="selectedMake" ref="makeQuestion" />
</div> </div>
@ -25,6 +25,7 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
// Supporting files // Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper"; import { settleAllPromises } from "@/helpers/layout-helper";
import store from "@/store";
export default { export default {
name: "vehicle-make", name: "vehicle-make",
data() { data() {
@ -53,7 +54,6 @@ export default {
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page. // Call the "next" function to complete the transition to this page.
next((vm) => { next((vm) => {
vm.$refs.funnelSubHeader.initializeComponent( vm.$refs.funnelSubHeader.initializeComponent(
@ -77,6 +77,10 @@ export default {
// route to move backwards // route to move backwards
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
}, },
arePagePrerequisitesValid() {
console.log(store.getters.vehicle);
return store.getters.vehicle.year !== null;
},
}, },
watch: { watch: {

View file

@ -25,6 +25,8 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
// Supporting files // Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper"; import { settleAllPromises } from "@/helpers/layout-helper";
import store from "@/store";
export default { export default {
name: "vehicle-model", name: "vehicle-model",
data() { data() {
@ -77,6 +79,9 @@ export default {
// route to move backwards // route to move backwards
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
}, },
arePagePrerequisitesValid() {
return store.getters.vehicle.make !== null;
},
}, },
watch: { watch: {

View file

@ -25,6 +25,8 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
// Supporting files // Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper"; import { settleAllPromises } from "@/helpers/layout-helper";
import store from "@/store";
export default { export default {
name: "vehicle-style", name: "vehicle-style",
data() { data() {
@ -78,17 +80,31 @@ export default {
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
}, },
setVehicle() { setVehicle() {
return this.dispatchNonBlockingStoreAction(this.storeActions.SET_VEHICLE, {year: this.$store.getters.vehicle.year, make: this.$store.getters.vehicle.make, model: this.$store.getters.vehicle.model, style: this.$store.getters.vehicle.style}); return this.dispatchNonBlockingStoreAction(
} this.storeActions.SET_VEHICLE,
{
year: this.$store.getters.vehicle.year,
make: this.$store.getters.vehicle.make,
model: this.$store.getters.vehicle.model,
style: this.$store.getters.vehicle.style,
}
);
},
arePagePrerequisitesValid() {
return store.getters.vehicle.model !== null;
},
}, },
watch: { watch: {
selectedStyle(style) { selectedStyle(style) {
this.$store.commit(this.storeMutations.UPDATE_STYLE, style); this.$store.commit(this.storeMutations.UPDATE_STYLE, style);
this.setVehicle().then(() => { this.setVehicle().then(() => {
this.$router.navigate(this.navigationScenarios.SELECTED_STYLE, this.$route); this.$router.navigate(
this.navigationScenarios.SELECTED_STYLE,
this.$route
);
}); });
} },
}, },
components: { components: {

View file

@ -4,9 +4,7 @@
<div class="select-car"> <div class="select-car">
<div class="select-car-form rounded text-center"> <div class="select-car-form rounded text-center">
<vehicleBanner ref="vehicleBanner" /> <vehicleBanner ref="vehicleBanner" />
<funnelSubHeader <funnelSubHeader ref="funnelSubHeader" />
ref="funnelSubHeader"
/>
<yearQuestion v-model="selectedYear" ref="yearQuestion" /> <yearQuestion v-model="selectedYear" ref="yearQuestion" />
</div> </div>
</div> </div>
@ -80,6 +78,11 @@ export default {
); );
}, },
}, },
methods: {
arePagePrerequisitesValid() {
return true;
},
},
components: { components: {
yearQuestion, yearQuestion,
funnelHeader, funnelHeader,

View file

@ -44,6 +44,13 @@ const routes = [
try { try {
// If we already have our route, go to it. // If we already have our route, go to it.
if (router.hasRoute(to.query.fmgPage)) { if (router.hasRoute(to.query.fmgPage)) {
// Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function.
let arePagePrerequisitesValid = router.getRoutes().filter(x => x.name === to.query.fmgPage)[0].components.default.methods.arePagePrerequisitesValid();
if (!arePagePrerequisitesValid) {
await GoToFunnelStartOn404(next);
}
return next({ name: to.query.fmgPage, query: to.query }); return next({ name: to.query.fmgPage, query: to.query });
} }
@ -57,6 +64,14 @@ const routes = [
component: routeData[0].component, component: routeData[0].component,
}); });
// Call the next components arePagePrerequisitesValid method before load.
// If it returns false, use the 404 logic.
let nextComponent = await router.getRoutes().filter(x => x.name === routeData[0].name)[0].components.default();
if (!nextComponent.default.methods.arePagePrerequisitesValid()) {
await GoToFunnelStartOn404(next);
}
// Assign current query string parameters, as well as our fmgPage one. // Assign current query string parameters, as well as our fmgPage one.
next({ name: routeData[0].name, query: Object.assign(to.query, { fmgPage: routeData[0].name }) }); next({ name: routeData[0].name, query: Object.assign(to.query, { fmgPage: routeData[0].name }) });
@ -79,6 +94,7 @@ const router = createRouter({
//---------------------------------------------------------- Router Functions ---------------------------------------------------------- //---------------------------------------------------------- Router Functions ----------------------------------------------------------
// Navigate to the next route, depending on the scenario. // Navigate to the next route, depending on the scenario.
router.navigate = ( router.navigate = (
scenario, scenario,