diff --git a/src/constants/error-messages.js b/src/constants/error-messages.js
index 415647879..ef5459395 100644
--- a/src/constants/error-messages.js
+++ b/src/constants/error-messages.js
@@ -29,6 +29,10 @@ const errorMessages = {
DATE_REQUIRED: "Please select a date",
PHONE_REQUIRED: "Please enter your phone number",
PHONE_FORMAT: "Phone number must be 10 digits",
+ YEAR_REQUIRED: "Please select your vehicle year",
+ MAKE_REQUIRED: "Please select your vehicle make",
+ MODEL_REQUIRED: "Please select your vehicle model",
+ STYLE_REQUIRED: "Please select your vehicle style",
};
export { errorMessages };
diff --git a/src/digital-components/dropdown-question/dropdown-question.vue b/src/digital-components/dropdown-question/dropdown-question.vue
index 0fc4a977c..1d8fe22cd 100644
--- a/src/digital-components/dropdown-question/dropdown-question.vue
+++ b/src/digital-components/dropdown-question/dropdown-question.vue
@@ -44,6 +44,7 @@ export default {
isDisabled: Boolean,
isRequired: Boolean,
validationRules: String,
+ placeHolderText: String,
cmsWidgetName: String,
hasError: Boolean,
},
diff --git a/src/fmg-components/funnel-footer/funnel-footer.vue b/src/fmg-components/funnel-footer/funnel-footer.vue
index c036f8d0a..e093e43f9 100644
--- a/src/fmg-components/funnel-footer/funnel-footer.vue
+++ b/src/fmg-components/funnel-footer/funnel-footer.vue
@@ -13,6 +13,7 @@
:isDisabled="isForwardActionDisabled"
@click-event="buttonClick"
data-bs-target="#footerModal"
+ data-test-id="funnel-footer-main-button"
data-bs-dismiss="modal" />
diff --git a/src/layouts/vehicle/vehicle-question/vehicle-question.spec.js b/src/layouts/vehicle/vehicle-question/vehicle-question.spec.js
new file mode 100644
index 000000000..abdb3f994
--- /dev/null
+++ b/src/layouts/vehicle/vehicle-question/vehicle-question.spec.js
@@ -0,0 +1,56 @@
+import vehicleQuestion from "@/layouts/vehicle/vehicle-question/vehicle-question";
+import { shallowMount } from "@vue/test-utils";
+import { getMountOptions } from "@/helpers/unit-test-helper.js";
+import store from "@/store";
+
+jest.mock(
+ "@/store",
+ () => {
+ return {};
+ },
+ { virtual: true }
+);
+
+describe("vehicle-question.vue", () => {
+ test("Selected value is emitted upon selection.", async () => {
+ //Arrange
+ const { wrapper } = setupMocks({ modelValueProp: "value" });
+ const valueToSelect = "newvalue";
+
+ //Act
+ wrapper.setValue({ selectedValue: valueToSelect });
+ await wrapper.vm.$nextTick();
+
+ //Assert
+ expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ selectedValue: "newvalue" }]);
+ });
+});
+
+function setupMocks({ modelValueProp = "", dataFromStoreApi = [] }) {
+ //Mock store
+ store.dispatch = jest.fn(() => dataFromStoreApi);
+ store.getters = { vehicle: { year: 2019, make: "honda", model: "civic", style: "4 Door" } };
+
+ const mountOptions = getMountOptions({
+ store: {
+ dispatch: store.dispatch,
+ getters: store.getters,
+ },
+ });
+
+ //Mock props
+ const mockMixin = {
+ methods: {
+ getCmsContent: jest.fn(),
+ },
+ };
+
+ mountOptions.propsData = {
+ modelValue: modelValueProp,
+ };
+
+ mountOptions.mixins = [mockMixin];
+ const wrapper = shallowMount(vehicleQuestion, mountOptions);
+
+ return { wrapper };
+}
diff --git a/src/layouts/vehicle/vehicle-question/vehicle-question.vue b/src/layouts/vehicle/vehicle-question/vehicle-question.vue
new file mode 100644
index 000000000..283f26811
--- /dev/null
+++ b/src/layouts/vehicle/vehicle-question/vehicle-question.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
diff --git a/src/layouts/vehicle/vehicle.spec.js b/src/layouts/vehicle/vehicle.spec.js
new file mode 100644
index 000000000..210281423
--- /dev/null
+++ b/src/layouts/vehicle/vehicle.spec.js
@@ -0,0 +1,91 @@
+// Components
+import vehicle from "@/layouts/vehicle/vehicle.vue";
+
+// Supporting files
+import { nextTick } from "vue";
+import { mount } from "@vue/test-utils";
+
+describe("vehicle.vue", () => {
+ test('"Continue" button is enabled after YMMS is selected.', async () => {
+ const { wrapper } = setupMocks();
+
+ const continueButton = wrapper.get('[data-test-id="funnel-footer-main-button"]');
+
+ expect(continueButton.attributes()["aria-disabled"]).toBe("false");
+ });
+
+ test("arePagePrerequisitesValid should be true ", async () => {
+ //Arrange
+ const { wrapper } = setupMocks();
+
+ //Act
+ let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
+ await nextTick();
+
+ //Assert
+ expect(arePagePrerequisitesValid).toBe(true);
+ });
+});
+
+describe("navigation", () => {
+ test("forwardButtonAction should trigger navigateForward", async () => {
+ // Arrange
+ const { wrapper } = setupMocks();
+ wrapper.vm.navigateForward = jest.fn();
+
+ // Act
+ await wrapper.vm.forwardButtonAction();
+
+ //Assert
+ expect(wrapper.vm.navigateForward).toHaveBeenCalled();
+ });
+});
+
+const FunnelFooterWidgetMockData = {
+ ForwardButtonText: "Continue",
+};
+
+function setupMocks() {
+ const mockRoute = {
+ query: {
+ fmgPage: "vehicle",
+ },
+ };
+
+ const mockRouter = {
+ navigate: jest.fn(),
+ };
+
+ const wrapper = mount(vehicle, {
+ global: {
+ mixins: [
+ {
+ methods: {
+ getCmsContent: jest.fn((cmsWidgetName, fieldName) => {
+ if (cmsWidgetName === "FunnelFooterWidget") {
+ if (fieldName === "ForwardButtonText") {
+ return FunnelFooterWidgetMockData.ForwardButtonText;
+ }
+ }
+ return "";
+ }),
+ getFooterInfoBoxHeight: jest.fn(() => 80),
+ },
+ },
+ ],
+
+ mocks: {
+ $route: mockRoute,
+ $router: mockRouter,
+ },
+
+ stubs: {
+ FunnelHeader: true,
+ FunnelSubHeader: true,
+ VehicleBanner: true,
+ },
+ },
+ });
+
+ return { mockRoute, mockRouter, wrapper };
+}
diff --git a/src/layouts/vehicle/vehicle.vue b/src/layouts/vehicle/vehicle.vue
new file mode 100644
index 000000000..bea1fdd8e
--- /dev/null
+++ b/src/layouts/vehicle/vehicle.vue
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
diff --git a/src/router/router-constants/fmgPage-values.js b/src/router/router-constants/fmgPage-values.js
index a83d03ecb..1ce76d5e8 100644
--- a/src/router/router-constants/fmgPage-values.js
+++ b/src/router/router-constants/fmgPage-values.js
@@ -3,6 +3,7 @@ const fmgPageValues = {
VEHICLE_MAKE: "vehicle-make",
VEHICLE_MODEL: "vehicle-model",
VEHICLE_STYLE: "vehicle-style",
+ VEHICLE: "vehicle",
VEHICLE_DAMAGE: "vehicle-damage",
ADDRESS_LOOKUP: "address-lookup",
VIN_LOOKUP: "vin-lookup",
diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js
index 73a534d61..0c2beffee 100644
--- a/src/router/router-constants/routing-table.js
+++ b/src/router/router-constants/routing-table.js
@@ -4,6 +4,15 @@ import { navigationScenarios } from "@/router/router-constants/navigation-scenar
// Get store from router/index.js instead of importing it here to get updated values
const routingTable = function (store) {
return [
+ {
+ fmgPageValue: fmgPageValues.VEHICLE,
+ maps: [
+ {
+ scenario: navigationScenarios.CLICKED_FORWARD,
+ destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
+ },
+ ],
+ },
{
fmgPageValue: fmgPageValues.VEHICLE_YEAR,
maps: [
@@ -57,7 +66,7 @@ const routingTable = function (store) {
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
- destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
+ destinationFmgPageValue: fmgPageValues.VEHICLE,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_VIN,
@@ -457,7 +466,7 @@ const routingTable = function (store) {
maps: [
{
scenario: navigationScenarios.CLICKED_VEHICLE_EDIT,
- destinationFmgPageValue: fmgPageValues.VEHICLE_YEAR,
+ destinationFmgPageValue: fmgPageValues.VEHICLE,
},
{
scenario: navigationScenarios.CLICKED_DAMAGE_EDIT,