/* eslint-env jest */ import '@testing-library/jest-dom'; import { flushPromises } from '@vue/test-utils'; import { render, waitFor } from '@testing-library/vue'; import { createTestingPinia } from '@pinia/testing'; import userEvent from '@testing-library/user-event'; import errorMessages from '@/constants/error-messages'; import issPageValues from '@/router/router-constants/issPage-values'; import queryStrings from '@/constants/query-strings'; import { GaActions } from '@/constants/analytics'; import { navigationScenarios } from '@/router/router-constants/navigation-scenarios'; import { routerParams } from '@/router/router-params'; import { useMainStore } from '@/store'; import VinLookupComponent from '@/layouts/vin-lookup/vin-lookup.vue'; const continueButtonQuerySelector = '[data-test-id="site-footer-main-button"]'; const errorMessageWrapperElSelector = '#vin-question-wrapper .form-test-error'; const vinInputSelector = '#vin-question'; const mockValidVin = '12345678901234567'; const mockCarId = 'Mock Car Id'; const lookupVehicleByVin = { methodName: 'lookupVehicleByVin', mockResponse: { data: { carId: mockCarId } } }; const lookupVehicleByVinError = { methodName: 'lookupVehicleByVin', mockResponse: { data: { error: true } } }; const getPartsOrQuestions = { methodName: 'getPartsOrQuestions', mockResponse: null }; const vehicleWithPartQuestionsMockResponse = { data: { partsOrQuestions: [ { partQuestions: [ { id: 1 } ] } ] } }; const vehicleWithMultiplePartsMockResponse = { data: { partsOrQuestions: [ { parts: [ { id: 1 }, { id: 2 } ] } ] } }; const vehicleWithMoldingQuestionsMockResponse = { data: { partsOrQuestions: [ { parts: [ { childPartQuestions: [ { id: 1 } ] } ] } ] } }; const vehicleWithCapabilityQuestionsMockResponse = { data: { partsOrQuestions: [ { parts: [ { requiresCapabilityQuestions: true, partNumber: 1 } ] } ] } }; const vehicleWithNoAdditionalPartsOrQuestionsMockResponse = { data: { partsOrQuestions: [ { parts: [ { childPartQuestions: [], requiresCapabilityQuestions: false } ] } ], partQuestions: null } }; jest.mock('bootstrap', () => ({ getInstance: jest.fn(), getOrCreateInstance: jest.fn() })); const mockRoute = { query: { // Needed inside vehicle-questions-mixin issPage: issPageValues.VIN_LOOKUP } }; const mockRouter = { navigate: jest.fn() }; const maska = jest.fn(); jest.mock('@/helpers/damage-helper', () => ({ isGlassAvailableForCarId: jest.fn().mockResolvedValue(false) })); const mountOptions = { global: { directives: { maska }, mixins: [ { methods: { getCmsContent: jest.fn(() => ''), getFooterInfoBoxHeight: jest.fn(() => 80), cssClassNameForCmsWidget: jest.fn(() => 'widget-name-mock-class'), getPageNameByQueryString: jest.fn(() => ''), pushEventToGA: jest.fn() }, computed: { GaActions() { return GaActions; }, navigationScenarios() { return navigationScenarios; }, queryStrings() { return queryStrings; } } } ], mocks: { $route: mockRoute, $router: mockRouter }, plugins: [createTestingPinia({ initialState: { main: { order: { vehicle: { carId: mockCarId } } } }, stubActions: false })], stubs: { siteHeader: true, siteSubHeader: true, vehicleBanner: true, vinLocationInformation: true, vinLookupAlerts: true, vinQuestion: true } } }; beforeEach(() => { jest.clearAllMocks(); }); describe('vin-lookup.vue', () => { describe('Client-side validation', () => { test.each([ { vin: null }, { vin: '' } ])('VIN Required error messsage is displayed.', async ({ vin }) => { const user = userEvent.setup(); mountOptions.data = () => ({ vin }); mountOptions.global.stubs.vinQuestion = false; const { container } = render(VinLookupComponent, mountOptions); const continueButton = container.querySelector(continueButtonQuerySelector); user.click(continueButton); await flushPromises(); await waitFor(() => { const errorMessageWrapperEl = container.querySelector(errorMessageWrapperElSelector); expect(errorMessageWrapperEl).toBeVisible(); const errorMessageEl = errorMessageWrapperEl.querySelector('span'); expect(errorMessageEl.textContent).toBe(errorMessages.VIN_REQUIRED); }); }); test.each([ { vin: '11' }, { vin: 'I1234567890123456' }, { vin: 'O1234567890123456' }, { vin: 'Q1234567890123456' } ])('VIN Invalid Format error messsage is displayed.', async ({ vin }) => { const user = userEvent.setup(); mountOptions.data = () => ({ vin }); mountOptions.global.stubs.vinQuestion = false; const { container } = render(VinLookupComponent, mountOptions); const continueButton = container.querySelector(continueButtonQuerySelector); user.click(continueButton); await flushPromises(); await waitFor(() => { const errorMessageWrapperEl = container.querySelector(errorMessageWrapperElSelector); expect(errorMessageWrapperEl).toBeVisible(); const errorMessageEl = errorMessageWrapperEl.querySelector('span'); expect(errorMessageEl.textContent).toBe(errorMessages.VIN_FORMAT); }); }); test('No error message is displayed when the vin is in the accepted format.', async () => { const user = userEvent.setup(); mountOptions.data = () => ({ vin: '12345678901234567' }); mountOptions.global.stubs.vinQuestion = false; const { container } = render(VinLookupComponent, mountOptions); const continueButton = container.querySelector(continueButtonQuerySelector); user.click(continueButton); await flushPromises(); await waitFor(() => { const errorMessageWrapperEl = container.querySelector(errorMessageWrapperElSelector); expect(errorMessageWrapperEl).not.toBeVisible(); const errorMessageEl = errorMessageWrapperEl.querySelector('span'); expect(errorMessageEl.textContent).toBeFalsy(); }); }); }); describe('Navigation', () => { test('Click "Back", execute navigate with navigationScenario.CLICKED_BACK.', async () => { const user = userEvent.setup(); const { container } = render(VinLookupComponent, mountOptions); const backButton = container.querySelector('[data-test-id="site-footer-back-button"]'); await user.click(backButton); expect(mockRouter.navigate).toHaveBeenCalledTimes(1); expect(mockRouter.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, mockRoute); }); test('Different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page.', async () => { const user = userEvent.setup(); mountOptions.global.stubs.vinQuestion = false; getPartsOrQuestions.mockResponse = vehicleWithPartQuestionsMockResponse; lookupVehicleByVin.mockResponse.data.carId = 'Different Car Id'; jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName) .mockResolvedValue(lookupVehicleByVin.mockResponse); jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName) .mockResolvedValue(getPartsOrQuestions.mockResponse); mountOptions.data = () => ({ activeVehicleLookupAlertType: null, needToLookupVehicle: false, vehicleFromLookup: { carId: 'Different Car Id' }, vin: mockValidVin }); const { container } = render(VinLookupComponent, mountOptions); const continueButton = container.querySelector(continueButtonQuerySelector); await user.click(continueButton); await flushPromises(); await waitFor(() => { expect(mockRouter.navigate).toHaveBeenCalledTimes(1); expect(mockRouter.navigate).toHaveBeenCalledWith( navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS, mockRoute, {}, { [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true } ); }); }); describe('Succesful navigateForward', () => { test('Vehicle with Part Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_PART_QUESTIONS', async () => { const user = userEvent.setup(); mountOptions.global.stubs.vinQuestion = false; getPartsOrQuestions.mockResponse = vehicleWithPartQuestionsMockResponse; jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName) .mockResolvedValue(lookupVehicleByVin.mockResponse); jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName) .mockResolvedValue(getPartsOrQuestions.mockResponse); const { container } = render(VinLookupComponent, mountOptions); const vinInput = container.querySelector(vinInputSelector); await user.type(vinInput, mockValidVin); const continueButton = container.querySelector(continueButtonQuerySelector); await user.click(continueButton); await flushPromises(); await waitFor(() => { expect(mockRouter.navigate).toHaveBeenCalledTimes(1); expect(mockRouter.navigate) .toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS, mockRoute, {}, {}, { partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions } ); }); }); test('Vehicle with Multiple Parts. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE', async () => { const user = userEvent.setup(); mountOptions.global.stubs.vinQuestion = false; getPartsOrQuestions.mockResponse = vehicleWithMultiplePartsMockResponse; jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName) .mockResolvedValue(lookupVehicleByVin.mockResponse); jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName) .mockResolvedValue(getPartsOrQuestions.mockResponse); const { container } = render(VinLookupComponent, mountOptions); const vinInput = container.querySelector(vinInputSelector); await user.type(vinInput, mockValidVin); const continueButton = container.querySelector(continueButtonQuerySelector); await user.click(continueButton); await flushPromises(); await waitFor(() => { expect(mockRouter.navigate).toHaveBeenCalledTimes(1); expect(mockRouter.navigate) .toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE, mockRoute, {}, {}, { partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions } ); }); }); test('Vehicle with Molding Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS', async () => { const user = userEvent.setup(); mountOptions.global.stubs.vinQuestion = false; getPartsOrQuestions.mockResponse = vehicleWithMoldingQuestionsMockResponse; jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName) .mockResolvedValue(lookupVehicleByVin.mockResponse); jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName) .mockResolvedValue(getPartsOrQuestions.mockResponse); const { container } = render(VinLookupComponent, mountOptions); const vinInput = container.querySelector(vinInputSelector); await user.type(vinInput, mockValidVin); const continueButton = container.querySelector(continueButtonQuerySelector); await user.click(continueButton); await flushPromises(); await waitFor(() => { expect(mockRouter.navigate).toHaveBeenCalledTimes(1); expect(mockRouter.navigate) .toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS, mockRoute, {}, {}, { partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions } ); }); }); test('Vehicle with Capability Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS', async () => { const user = userEvent.setup(); const store = useMainStore(); mountOptions.global.stubs.vinQuestion = false; getPartsOrQuestions.mockResponse = vehicleWithCapabilityQuestionsMockResponse; jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName) .mockResolvedValue(lookupVehicleByVin.mockResponse); jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName) .mockResolvedValue(getPartsOrQuestions.mockResponse); store.getCapabilityQuestions.mockResolvedValueOnce({ data: [] }); const { container } = render(VinLookupComponent, mountOptions); const vinInput = container.querySelector(vinInputSelector); await user.type(vinInput, mockValidVin); const continueButton = container.querySelector(continueButtonQuerySelector); await user.click(continueButton); await flushPromises(); await waitFor(() => { expect(mockRouter.navigate).toHaveBeenCalledTimes(1); expect(mockRouter.navigate) .toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS, mockRoute, {}, {}, { partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions } ); }); }); test('Vehicle no additional Parts or Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS', async () => { const user = userEvent.setup(); mountOptions.global.stubs.vinQuestion = false; getPartsOrQuestions.mockResponse = vehicleWithNoAdditionalPartsOrQuestionsMockResponse; jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName) .mockResolvedValue(lookupVehicleByVin.mockResponse); jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName) .mockResolvedValue(getPartsOrQuestions.mockResponse); const { container } = render(VinLookupComponent, mountOptions); const vinInput = container.querySelector(vinInputSelector); await user.type(vinInput, mockValidVin); const continueButton = container.querySelector(continueButtonQuerySelector); await user.click(continueButton); await flushPromises(); await waitFor(() => { expect(mockRouter.navigate).toHaveBeenCalledTimes(1); expect(mockRouter.navigate) .toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, mockRoute ); }); }); test('Selected vehicle VIN do not match vehicles (CarIDs) in our system then navigate forward to bailout page.', async () => { //Arrange const user = userEvent.setup(); lookupVehicleByVin.mockResponse.data.error = true; jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName) .mockResolvedValue(lookupVehicleByVinError.mockResponse); mountOptions.data = () => ({ needToLookupVehicle: true, bailout: true, vin: mockValidVin }); const { container } = render(VinLookupComponent, mountOptions); const continueButton = container.querySelector(continueButtonQuerySelector); await user.click(continueButton); await flushPromises(); await waitFor(() => { expect(mockRouter.navigate).toHaveBeenCalledTimes(1); expect(mockRouter.navigate).toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, mockRoute ); }); }); }); }); });