Merge pull request #26 from Safelite/feature/Digital/SSR-93
Feature/digital/ssr 93
This commit is contained in:
commit
458c8e2125
21 changed files with 576 additions and 51 deletions
44
package-lock.json
generated
44
package-lock.json
generated
|
|
@ -2548,6 +2548,29 @@
|
||||||
"webpack-merge": "^5.7.3",
|
"webpack-merge": "^5.7.3",
|
||||||
"webpack-virtual-modules": "^0.4.2",
|
"webpack-virtual-modules": "^0.4.2",
|
||||||
"whatwg-fetch": "^3.6.2"
|
"whatwg-fetch": "^3.6.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/vue-loader-v15": {
|
||||||
|
"version": "npm:vue-loader@15.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz",
|
||||||
|
"integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@vue/component-compiler-utils": "^3.1.0",
|
||||||
|
"hash-sum": "^1.0.2",
|
||||||
|
"loader-utils": "^1.1.0",
|
||||||
|
"vue-hot-reload-api": "^2.3.0",
|
||||||
|
"vue-style-loader": "^4.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hash-sum": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@vue/cli-shared-utils": {
|
"@vue/cli-shared-utils": {
|
||||||
|
|
@ -2797,27 +2820,6 @@
|
||||||
"integrity": "sha512-E2P4oXSaWDqTZNbmKZFVLrNN/siVN78YkEqs7pHryWerrlZR9bBFLWdJwRoguX45Ru6HxIflzKl4vQvwRMwm5g==",
|
"integrity": "sha512-E2P4oXSaWDqTZNbmKZFVLrNN/siVN78YkEqs7pHryWerrlZR9bBFLWdJwRoguX45Ru6HxIflzKl4vQvwRMwm5g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@vue/vue-loader-v15": {
|
|
||||||
"version": "npm:vue-loader@15.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz",
|
|
||||||
"integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@vue/component-compiler-utils": "^3.1.0",
|
|
||||||
"hash-sum": "^1.0.2",
|
|
||||||
"loader-utils": "^1.1.0",
|
|
||||||
"vue-hot-reload-api": "^2.3.0",
|
|
||||||
"vue-style-loader": "^4.1.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"hash-sum": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vue/vue3-jest": {
|
"@vue/vue3-jest": {
|
||||||
"version": "27.0.0",
|
"version": "27.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/vue3-jest/-/vue3-jest-27.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/vue3-jest/-/vue3-jest-27.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,16 @@
|
||||||
<head>
|
<head>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = [{}];
|
window.dataLayer = [{}];
|
||||||
</script>
|
</script>
|
||||||
<script><%= process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY %></script>
|
<script><%= process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY %></script>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 1.625rem;
|
line-height: 1.5rem;
|
||||||
|
|
||||||
& > span {
|
& > span {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import buttonBack from "./button-back";
|
||||||
|
|
||||||
|
describe("back button", () => {
|
||||||
|
test("renders a button", () => {
|
||||||
|
// Arrange
|
||||||
|
const myFunction = () => {};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(buttonBack, {
|
||||||
|
propsData: {
|
||||||
|
backButtonAction: myFunction,
|
||||||
|
backButtonAccessibleText: "something",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.find("button").exists()).toBe(true);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
@click="handleClick"
|
||||||
|
class="back-button-wrapper"
|
||||||
|
:aria-label="backButtonAccessibleText"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="34px"
|
||||||
|
height="30px"
|
||||||
|
viewBox="-2.2 -6 30 30"
|
||||||
|
>
|
||||||
|
<g id="Layer_3">
|
||||||
|
<path
|
||||||
|
class="outer"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
d="M19.934-1.434h-8.486c-1.683,0-2.999,0.528-4.021,1.614L1.685,6.146C0.78,7.079,0.338,8.027,0.338,9.041
|
||||||
|
c0,1.028,0.444,1.979,1.359,2.908l5.753,5.938c1.054,1.093,2.403,1.647,4.007,1.647l8.475,0.01c2.861,0,4.568-1.692,4.568-4.528
|
||||||
|
V3.094C24.5,0.259,22.793-1.434,19.934-1.434L19.934-1.434z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<g id="Layer_2">
|
||||||
|
<path
|
||||||
|
class="inner"
|
||||||
|
fill="#727676"
|
||||||
|
d="M19.934,17.989c2.041,0,3.066-1.006,3.066-3.028V3.039c0-2.022-1.025-3.028-3.066-3.028h-8.486
|
||||||
|
c-1.143,0-2.129,0.292-2.929,1.143L2.766,7.13C2.131,7.784,1.838,8.37,1.838,8.985c0,0.605,0.283,1.201,0.928,1.855l5.762,5.947
|
||||||
|
c0.811,0.84,1.787,1.191,2.93,1.191L19.934,17.989z M10.773,13.253c-0.439,0-0.801-0.371-0.801-0.82
|
||||||
|
c0-0.215,0.078-0.42,0.235-0.566l2.861-2.861l-2.861-2.862c-0.156-0.156-0.234-0.352-0.234-0.566c0-0.458,0.362-0.801,0.801-0.801
|
||||||
|
c0.224,0,0.41,0.069,0.556,0.225l2.881,2.872l2.891-2.881c0.166-0.166,0.353-0.234,0.566-0.234c0.439,0,0.801,0.352,0.801,0.801
|
||||||
|
c0,0.225-0.078,0.4-0.234,0.576l-2.871,2.871l2.861,2.852c0.146,0.156,0.234,0.352,0.234,0.576c0,0.449-0.361,0.82-0.811,0.82
|
||||||
|
c-0.225,0-0.43-0.098-0.586-0.244l-2.852-2.871l-2.852,2.871C11.203,13.165,10.999,13.253,10.773,13.253z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "buttonBack",
|
||||||
|
props: {
|
||||||
|
backButtonAccessibleText: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClick() {
|
||||||
|
this.$emit("click-event");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.back-button-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
margin-left: 2px;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
svg .outer {
|
||||||
|
fill: $white;
|
||||||
|
stroke-width: 2.5px;
|
||||||
|
stroke: $blue;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
transform: translate(-2.7px, -1.8px) scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -5,23 +5,39 @@
|
||||||
<span>
|
<span>
|
||||||
{{ content }}
|
{{ content }}
|
||||||
</span>
|
</span>
|
||||||
|
<buttonBack
|
||||||
|
v-if="hasBackButton"
|
||||||
|
:backButtonAccessibleText="backButtonAccessibleText"
|
||||||
|
@click-event="clickEvent"
|
||||||
|
/>
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import buttonBack from "@/common-components/site-sub-header/button-back/button-back";
|
||||||
|
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
name: "siteSubHeader",
|
name: "siteSubHeader",
|
||||||
props: {
|
props: {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
|
hasBackButton: Boolean,
|
||||||
|
backButtonAccessibleText: String,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
content()
|
content()
|
||||||
{
|
{
|
||||||
return this.getCmsContent(this.cmsWidgetName, "SubHeaderText")
|
return this.getCmsContent(this.cmsWidgetName, "SubHeaderText")
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
methods: {
|
||||||
|
clickEvent() {
|
||||||
|
this.$emit("click-event");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: { buttonBack },
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useMainStore } from "@/store";
|
||||||
export default {
|
export default {
|
||||||
name: "vehicle-banner",
|
name: "vehicle-banner",
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -25,7 +25,7 @@ export default {
|
||||||
return this.genericVehicleImage;
|
return this.genericVehicleImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageUrl = this.mainStore.order.vehicle.imageUrl;
|
const imageUrl = useMainStore().order.vehicle.imageUrl;
|
||||||
if (!imageUrl || imageUrl === "NULL"){
|
if (!imageUrl || imageUrl === "NULL"){
|
||||||
return this.getUnmatchedVehicleIcon();
|
return this.getUnmatchedVehicleIcon();
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getUnmatchedVehicleIcon(){
|
getUnmatchedVehicleIcon(){
|
||||||
switch(this.mainStore.order.vehicle.category){
|
switch(useMainStore().order.vehicle.category){
|
||||||
case this.vehicleCategories.CAR:
|
case this.vehicleCategories.CAR:
|
||||||
return this.carUnmatchedVehicleIcon;
|
return this.carUnmatchedVehicleIcon;
|
||||||
case this.vehicleCategories.SUV:
|
case this.vehicleCategories.SUV:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ const endpoints = {
|
||||||
url: "/vehicle/api/v1/vehicle/years",
|
url: "/vehicle/api/v1/vehicle/years",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
},
|
},
|
||||||
|
GetVehicleMakes: {
|
||||||
|
url: "/vehicle/api/v1/vehicle/makes/",
|
||||||
|
method: "GET",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export { endpoints };
|
export { endpoints };
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ function mapStringToState(str) {
|
||||||
|
|
||||||
for (const match of globalStateMatches) {
|
for (const match of globalStateMatches) {
|
||||||
// Reset store state for each match.
|
// Reset store state for each match.
|
||||||
let storeState = useMainStore().state;
|
let storeState = useMainStore();
|
||||||
|
|
||||||
for (const s of match[2].split(".")) {
|
for (const s of match[2].split(".")) {
|
||||||
if (storeState[s] != undefined) {
|
if (storeState[s] != undefined) {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export function getMountOptions(mockData) {
|
||||||
mocks.vehicleCategories = vehicleCategories;
|
mocks.vehicleCategories = vehicleCategories;
|
||||||
mocks.issPageValues = issPageValues;
|
mocks.issPageValues = issPageValues;
|
||||||
mocks.queryStrings = queryStrings;
|
mocks.queryStrings = queryStrings;
|
||||||
|
mocks.$router = mockData?.router;
|
||||||
|
|
||||||
const global = {
|
const global = {
|
||||||
mocks: mocks,
|
mocks: mocks,
|
||||||
|
|
|
||||||
70
src/layouts/vehicle-make/make-question/make-question.spec.js
Normal file
70
src/layouts/vehicle-make/make-question/make-question.spec.js
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import makeQuestion from "@/layouts/vehicle-make/make-question/make-question";
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
import { useMainStore } from "@/store";
|
||||||
|
|
||||||
|
|
||||||
|
describe("make-question.vue", () => {
|
||||||
|
|
||||||
|
test("Data from store api are used as radio question answers.", async () => {
|
||||||
|
//Arrange
|
||||||
|
const { wrapper, cmsContent } = setupMocks({
|
||||||
|
dataFromStoreApi: ["honda", "ford", "dodge"],
|
||||||
|
});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
const initialData = makeQuestion.methods.loadInitialData.call(wrapper.vm);
|
||||||
|
makeQuestion.methods.initializeComponent.call(
|
||||||
|
wrapper.vm,
|
||||||
|
initialData
|
||||||
|
);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
const buttonQuestionComponent = await wrapper.findComponent({
|
||||||
|
name: "buttonQuestion",
|
||||||
|
});
|
||||||
|
expect(buttonQuestionComponent.attributes("answers")).toBe(
|
||||||
|
"honda,ford,dodge"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Selected make is emitted upon selection.", async () => {
|
||||||
|
//Arrange
|
||||||
|
const { wrapper } = setupMocks({ modelValueProp: "honda" });
|
||||||
|
const makeToSelect = "ford";
|
||||||
|
|
||||||
|
//Act
|
||||||
|
wrapper.setValue({ selectedMake: makeToSelect });
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([
|
||||||
|
{ selectedMake: "ford" },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function setupMocks({
|
||||||
|
modelValueProp = "1900",
|
||||||
|
cmsQuestionText = "CMS text goes here",
|
||||||
|
dataFromStoreApi = [],
|
||||||
|
}) {
|
||||||
|
//Mock store
|
||||||
|
const mountOptions = getMountOptions();
|
||||||
|
|
||||||
|
const store = useMainStore();
|
||||||
|
store.getVehicleMakes = jest.fn(() => dataFromStoreApi);
|
||||||
|
|
||||||
|
|
||||||
|
mountOptions.propsData = {
|
||||||
|
modelValue: modelValueProp,
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount(makeQuestion, mountOptions);
|
||||||
|
|
||||||
|
//Mock CMS content
|
||||||
|
const cmsContent = {
|
||||||
|
QuestionText: cmsQuestionText,
|
||||||
|
};
|
||||||
|
return { wrapper, cmsContent };
|
||||||
|
}
|
||||||
56
src/layouts/vehicle-make/make-question/make-question.vue
Normal file
56
src/layouts/vehicle-make/make-question/make-question.vue
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<buttonQuestion
|
||||||
|
class="radioQuestion"
|
||||||
|
isOverflowScrollable
|
||||||
|
selectingInitiatesLoad
|
||||||
|
:questionText="questionText"
|
||||||
|
:answers="makes"
|
||||||
|
groupName="ChooseVehicleMake"
|
||||||
|
textPosition="text-start"
|
||||||
|
v-model="selectedValue"
|
||||||
|
isRequired
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||||
|
import { useMainStore } from '@/store';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "make-question",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
makes: Array,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: String,
|
||||||
|
cmsWidgetName: String,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
buttonQuestion,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
questionText(){
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
|
||||||
|
},
|
||||||
|
selectedValue: {
|
||||||
|
get: function() {
|
||||||
|
return this.modelValue
|
||||||
|
},
|
||||||
|
set: function(newValue) {
|
||||||
|
this.$emit("update:modelValue", newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadInitialData() {
|
||||||
|
return useMainStore().getVehicleMakes();
|
||||||
|
},
|
||||||
|
initializeComponent(initialData) {
|
||||||
|
this.makes = initialData;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -1 +1,140 @@
|
||||||
test.todo("some test to be written in the future");
|
// Supporting Files
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||||
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
import { nextTick } from "vue";
|
||||||
|
import { useMainStore } from "@/store";
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import vehicleMake from "@/layouts/vehicle-make/vehicle-make.vue";
|
||||||
|
import makeQuestion from "@/layouts/vehicle-make/make-question/make-question";
|
||||||
|
|
||||||
|
// Mock fetchCmsContentForPage
|
||||||
|
jest.mock("@/helpers/cms-content-helper", () => ({
|
||||||
|
fetchCmsContentForPage: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock our module for promises.
|
||||||
|
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||||
|
settleAllPromises: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("vehicle-make.vue", () => {
|
||||||
|
|
||||||
|
test("Make question component is initized with api data", async () => {
|
||||||
|
//Arrange
|
||||||
|
const makeQuestionInitialData = ["honda", "ford", "dodge"];
|
||||||
|
const { wrapper, apiPromise } = setupMocks({
|
||||||
|
makeQuestionInitialData: makeQuestionInitialData,
|
||||||
|
});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
vehicleMake.beforeRouteEnter.call(
|
||||||
|
wrapper.vm,
|
||||||
|
{ query: { issPage: "vehicle-make" } },
|
||||||
|
undefined,
|
||||||
|
(c) => c(wrapper.vm)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
apiPromise.finally(() => {
|
||||||
|
expect(makeQuestion.methods.initializeComponent).toHaveBeenCalledWith(
|
||||||
|
makeQuestionInitialData
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("BackButtonAction triggers a router.navigate change", async () => {
|
||||||
|
//Arrange
|
||||||
|
const { wrapper, apiPromise } = setupMocks({
|
||||||
|
pageHeaderWidgetHeaderText: "Select a make to get started",
|
||||||
|
mountOptionsMockData: {
|
||||||
|
router: {
|
||||||
|
navigate: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
//Act
|
||||||
|
vehicleMake.beforeRouteEnter.call(
|
||||||
|
wrapper.vm,
|
||||||
|
{ query: { issPage: "vehicle-make" } },
|
||||||
|
undefined,
|
||||||
|
(c) => c(wrapper.vm)
|
||||||
|
);
|
||||||
|
wrapper.vm.backButtonAction();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
apiPromise.finally(() => {
|
||||||
|
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Year set, arePagePrerequisitesValid should be true ", async () => {
|
||||||
|
//Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
useMainStore().order.vehicle.year = 2001;
|
||||||
|
|
||||||
|
//Act
|
||||||
|
vehicleMake.beforeRouteEnter.call(
|
||||||
|
wrapper.vm,
|
||||||
|
{ query: { issPage: "vehicle-make" } },
|
||||||
|
undefined,
|
||||||
|
(c) => c(wrapper.vm)
|
||||||
|
);
|
||||||
|
|
||||||
|
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(arePagePrerequisitesValid).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function setupMocks({
|
||||||
|
vehicleMakeQuestionCmsContent = {},
|
||||||
|
makeQuestionInitialData = {},
|
||||||
|
pageHeaderWidgetHeaderText = {},
|
||||||
|
mountOptionsMockData = {},
|
||||||
|
}) {
|
||||||
|
//Mock api responses
|
||||||
|
const apiResponses = {
|
||||||
|
cmsContent: {
|
||||||
|
SiteSubHeaderWidget: pageHeaderWidgetHeaderText,
|
||||||
|
VehicleMakeQuestion: vehicleMakeQuestionCmsContent,
|
||||||
|
VehicleBannerWidget: {
|
||||||
|
GenericVehicleImage:
|
||||||
|
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
|
||||||
|
},
|
||||||
|
SiteHeaderWidget: {
|
||||||
|
LogoImage:
|
||||||
|
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
makeQuestionInitialData: makeQuestionInitialData,
|
||||||
|
};
|
||||||
|
|
||||||
|
const apiPromise = Promise.resolve(apiResponses);
|
||||||
|
|
||||||
|
settleAllPromises.mockImplementation(() => apiPromise);
|
||||||
|
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
|
||||||
|
|
||||||
|
//Mock make question methods
|
||||||
|
makeQuestion.methods = {
|
||||||
|
loadInitialData: jest.fn(),
|
||||||
|
initializeComponent: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mountOptions = getMountOptions(mountOptionsMockData);
|
||||||
|
const wrapper = shallowMount(vehicleMake, mountOptions);
|
||||||
|
|
||||||
|
const makeQuestionWrapper = wrapper.findComponent({ name: "makeQuestion" });
|
||||||
|
makeQuestionWrapper.vm.initializeComponent =
|
||||||
|
makeQuestion.methods.initializeComponent;
|
||||||
|
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
||||||
|
|
||||||
|
return { wrapper, apiPromise };
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,102 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="page-container-grouped-styles">
|
||||||
<input type="text" v-model="year"/>
|
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||||
<span> Vehicle Make Placeholder </span>
|
<div class="select-car">
|
||||||
<span>{{returnVehicleYear}}</span>
|
<div class="select-car-form rounded text-center">
|
||||||
<button @click="updateVehicleYear(year)">Add</button>
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="true"/>
|
||||||
|
<siteSubHeader
|
||||||
|
cmsWidgetName="SiteSubHeaderWidget"
|
||||||
|
:hasBackButton="true"
|
||||||
|
@click-event="backButtonAction"
|
||||||
|
backButtonAccessibleText = "Change vehicle year"
|
||||||
|
/>
|
||||||
|
<div class="fade-on-route-transition">
|
||||||
|
<makeQuestion
|
||||||
|
class="fade-on-route-transition"
|
||||||
|
v-model="selectedMake"
|
||||||
|
ref="makeQuestion"
|
||||||
|
cmsWidgetName="VehicleMakeQuestion"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Components
|
||||||
|
import makeQuestion from "@/layouts/vehicle-make/make-question/make-question";
|
||||||
|
import siteHeader from "@/common-components/site-header/site-header";
|
||||||
|
import siteSubHeader from "@/common-components/site-sub-header/site-sub-header";
|
||||||
|
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||||
|
|
||||||
|
// Supporting files
|
||||||
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'vehicle-make',
|
name: 'vehicle-make',
|
||||||
methods:
|
data() {
|
||||||
{
|
return {
|
||||||
updateVehicleYear(item)
|
selectedMake: null,
|
||||||
{
|
};
|
||||||
this.mainStore.updateVehicleYear(item);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed:
|
|
||||||
{
|
async beforeRouteEnter(to, from, next) {
|
||||||
returnVehicleYear()
|
// Call APIs
|
||||||
|
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||||
|
const makeQuestionInitialDataPromise = makeQuestion.methods.loadInitialData();
|
||||||
|
|
||||||
|
// Settle promises and get results
|
||||||
|
const promiseResultMap = [
|
||||||
{
|
{
|
||||||
return this.mainStore.order.vehicle.year;
|
resultKey: "cmsContent",
|
||||||
|
promise: cmsContentPromise,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resultKey: "makeQuestionInitialData",
|
||||||
|
promise: makeQuestionInitialDataPromise,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let resultMap = await settleAllPromises(promiseResultMap);
|
||||||
|
|
||||||
|
// Call the "next" function to complete the transition to this page.
|
||||||
|
next((vm) => {
|
||||||
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
|
vm.$refs.makeQuestion.initializeComponent(
|
||||||
|
resultMap.makeQuestionInitialData
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
backButtonAction() {
|
||||||
|
this.$router.navigate(
|
||||||
|
this.navigationScenarios.CLICKED_BACK,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
},
|
||||||
|
arePagePrerequisitesValid() {
|
||||||
|
if (this.mainStore.order.vehicle.year){
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
makeQuestion,
|
||||||
|
siteHeader,
|
||||||
|
siteSubHeader,
|
||||||
|
vehicleBanner,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selectedMake(make) {
|
||||||
|
this.mainStore.updateVehicleMake(make);
|
||||||
|
this.$router.navigate(
|
||||||
|
this.navigationScenarios.SELECTED_MAKE,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||||
<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 cmsWidgetName="VehicleBannerWidget" displayGenericVehicleImage="true"/>
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="true"/>
|
||||||
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
|
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
|
||||||
<div class="fade-on-route-transition">
|
<div class="fade-on-route-transition">
|
||||||
<yearQuestion
|
<yearQuestion
|
||||||
|
|
@ -65,6 +65,22 @@
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
selectedYear(year) {
|
||||||
|
const parsedYear = parseInt(year);
|
||||||
|
this.mainStore.updateVehicleYear(parsedYear);
|
||||||
|
this.$router.navigate(
|
||||||
|
this.navigationScenarios.SELECTED_YEAR,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
yearQuestion,
|
yearQuestion,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ const navigationScenarios = {
|
||||||
|
|
||||||
// YMMS
|
// YMMS
|
||||||
SELECTED_YEAR: "SELECTED_YEAR",
|
SELECTED_YEAR: "SELECTED_YEAR",
|
||||||
|
SELECTED_MAKE: "SELECTED_MAKE",
|
||||||
// TESTING
|
// TESTING
|
||||||
CLICKED_TEST: "CLICKED_TEST"
|
CLICKED_TEST: "CLICKED_TEST"
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,23 @@ const routingTable = function(store) {
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
destinationIssPageValue: issPageValues.WELCOME_PAGE
|
destinationIssPageValue: issPageValues.WELCOME_PAGE
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.SELECTED_YEAR,
|
||||||
|
destinationIssPageValue: issPageValues.VEHICLE_MAKE
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
issPageValue: issPageValues.VEHICLE_MAKE,
|
||||||
|
maps: [
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
|
destinationIssPageValue: issPageValues.VEHICLE_YEAR
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.SELECTED_MAKE,
|
||||||
|
destinationIssPageValue: issPageValues.WELCOME_PAGE
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -26,6 +43,7 @@ const routingTable = function(store) {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,14 @@ export const useMainStore = defineStore({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getVehicleMakes() {
|
||||||
|
return globalMethods.callHttpClient({
|
||||||
|
method: endpoints.GetVehicleMakes.method,
|
||||||
|
endpoint: endpoints.GetVehicleMakes.url + this.order.vehicle.year,
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// Vehicle API Actions
|
// Vehicle API Actions
|
||||||
updateVehicleYear(year)
|
updateVehicleYear(year)
|
||||||
{
|
{
|
||||||
|
|
@ -91,6 +99,13 @@ export const useMainStore = defineStore({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateVehicleMake(make)
|
||||||
|
{
|
||||||
|
if (this.order.vehicle.make !== make) {
|
||||||
|
this.order.vehicle.make = make;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// populate initial state
|
// populate initial state
|
||||||
populateInitialState()
|
populateInitialState()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
6
src/styles/mixins/customMixins.scss
Normal file
6
src/styles/mixins/customMixins.scss
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
//Custom Mixins
|
||||||
|
|
||||||
|
//Blue gradient background mixin
|
||||||
|
@mixin blue-gradient {
|
||||||
|
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ module.exports = {
|
||||||
@import "@/styles/ux-variables.scss";
|
@import "@/styles/ux-variables.scss";
|
||||||
@import "./node_modules/bootstrap/scss/variables";
|
@import "./node_modules/bootstrap/scss/variables";
|
||||||
@import "./node_modules/bootstrap/scss/mixins";
|
@import "./node_modules/bootstrap/scss/mixins";
|
||||||
|
@import "@/styles/mixins/customMixins";
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,11 @@ module.exports = {
|
||||||
@import "@/styles/ux-variables.scss";
|
@import "@/styles/ux-variables.scss";
|
||||||
@import "./node_modules/bootstrap/scss/variables";
|
@import "./node_modules/bootstrap/scss/variables";
|
||||||
@import "./node_modules/bootstrap/scss/mixins";
|
@import "./node_modules/bootstrap/scss/mixins";
|
||||||
|
@import "@/styles/mixins/customMixins";
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
devtool: 'source-map'
|
devtool: 'source-map'
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue