Merge pull request #484 from Safelite/feature/digital/SSR-462

SSR-462 VIN passed or input doesn't match a CarID in our system
This commit is contained in:
Josh Dassinger 2023-10-18 09:53:56 -05:00 committed by GitHub
commit da8f2e2776
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 203 additions and 25 deletions

View file

@ -243,7 +243,7 @@ function mapStringToState(str) {
// Reset store state for each match. // Reset store state for each match.
const valueFromStore = getStoreValueFromString(match[2]); const valueFromStore = getStoreValueFromString(match[2]);
if (!valueFromStore) { if (!valueFromStore) {
console.warning('Unable to resolve global state data.'); console.warn('Unable to resolve global state data.');
return ''; // if we can't map our string to state data, return an empty string. return ''; // if we can't map our string to state data, return an empty string.
} }
const stringWithReplacement = str.replace(match[0], valueFromStore); const stringWithReplacement = str.replace(match[0], valueFromStore);

View file

@ -140,7 +140,7 @@ describe('policyEndorsements.vue', () => {
expect(wrapper.vm.$router.navigate) expect(wrapper.vm.$router.navigate)
.toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, undefined); .toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, undefined);
}); });
test('Forward button clicked triggers navigation', () => { test('Forward button clicked with valid car id triggers navigation', () => {
// Arrange // Arrange
const wrapper = shallowMount(policyEndorsements, getMountOptions({ const wrapper = shallowMount(policyEndorsements, getMountOptions({
router: { router: {
@ -148,6 +148,10 @@ describe('policyEndorsements.vue', () => {
} }
})); }));
wrapper.setData({
hasValidCarId: true
});
// Act // Act
wrapper.vm.forwardButtonAction(); wrapper.vm.forwardButtonAction();
wrapper.vm.navigateForward(); wrapper.vm.navigateForward();
@ -157,6 +161,27 @@ describe('policyEndorsements.vue', () => {
expect(wrapper.vm.$router.navigate) expect(wrapper.vm.$router.navigate)
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined); .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined);
}); });
test('Forward button clicked with invalid car id triggers navigation scenario CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND', () => {
// Arrange
const wrapper = shallowMount(policyEndorsements, getMountOptions({
router: {
navigate: jest.fn()
}
}));
wrapper.setData({
hasValidCarId: false,
});
// Act
wrapper.vm.forwardButtonAction();
wrapper.vm.navigateForward();
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
expect(wrapper.vm.$router.navigate)
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, undefined);
});
test('Forward button clicked saves endorsement question answers', () => { test('Forward button clicked saves endorsement question answers', () => {
// Arrange // Arrange
const mountOptions = getMountOptions({ const mountOptions = getMountOptions({

View file

@ -96,11 +96,17 @@ export default {
}); });
}, },
emits: ['update:modelValue'], emits: ['update:modelValue'],
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() { data() {
return { return {
questionAnswersArray: [], questionAnswersArray: [],
schoolPropertyAnswer: '', schoolPropertyAnswer: '',
parkingLotAnswer: '', parkingLotAnswer: '',
hasValidCarId: this.mainStore.vehicle.carId && this.mainStore.vehicle.carId !== '0',
rules: { rules: {
selectionRequired: globalRules.OPTION_REQUIRED selectionRequired: globalRules.OPTION_REQUIRED
} }
@ -158,10 +164,17 @@ export default {
return this.navigateForward(); return this.navigateForward();
}, },
navigateForward() { navigateForward() {
this.$router.navigate( if (!this.hasValidCarId) {
this.navigationScenarios.CLICKED_FORWARD, this.$router.navigate(
this.$route this.navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND,
); this.$route
);
} else {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD,
this.$route
);
}
} }
} }
}; };

View file

@ -100,7 +100,8 @@ describe('policy-vehicles.vue', () => {
policyVehicles: [ policyVehicles: [
{ vin } { vin }
], ],
bailout: false bailout: false,
policyVinFound: true
}); });
const year = getRandomInt(1998, 2023); const year = getRandomInt(1998, 2023);
@ -126,6 +127,7 @@ describe('policy-vehicles.vue', () => {
// Assert // Assert
expect(wrapper.vm.bailout).toBeFalsy(); expect(wrapper.vm.bailout).toBeFalsy();
expect(wrapper.vm.policyVinFound).toBeTruthy();
expect(store.updateVehicle).toHaveBeenCalledWith(expectedInput); expect(store.updateVehicle).toHaveBeenCalledWith(expectedInput);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE, navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE,
@ -190,6 +192,7 @@ describe('policy-vehicles.vue', () => {
async () => { async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue({ error: true, status: 500 });
const vin = getRandomString(17, 17); const vin = getRandomString(17, 17);
await wrapper.setData({ await wrapper.setData({
@ -197,9 +200,6 @@ describe('policy-vehicles.vue', () => {
bailout: false bailout: false
}); });
const store = useMainStore();
store.lookupVehicleByVin.mockReturnValue(Promise.reject());
// Act // Act
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
@ -214,6 +214,43 @@ describe('policy-vehicles.vue', () => {
} }
); );
test(
// eslint-disable-next-line max-len
'Vehicle not found in lookupVehicleByVin call => policyVinFound false and navigate forward with CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND scenario.',
async () => {
// Arrange
const { wrapper } = setupMocks({});
wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue({ error: true, status: 404 });
const vin = getRandomString(17, 17);
await wrapper.setData({
selectedVehicleVin: vin,
bailout: false,
policyVinFound: true,
policyVehicles: [{
vin,
vehicleYear: getRandomInt(2000, 2100),
vehicleMake: getRandomString(5, 10),
vehicleMode: getRandomString(5, 10),
vehicleStyle: getRandomString(5, 10)
}]
});
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(wrapper.vm.bailout).toBeFalsy();
expect(wrapper.vm.policyVinFound).toBeFalsy();
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND,
undefined,
{},
{}
);
}
);
test('vehicle not listed => navigate forward with CLICKED_FORWARD_NON_LISTED_VEHICLE scenario.', async () => { test('vehicle not listed => navigate forward with CLICKED_FORWARD_NON_LISTED_VEHICLE scenario.', async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});

View file

@ -77,6 +77,7 @@ export default {
policyVehicles, policyVehicles,
selectedVehicleVin: '', selectedVehicleVin: '',
displayGeneric: true, displayGeneric: true,
policyVinFound: true,
bailout: false, bailout: false,
rules: { rules: {
optionRequired: globalRules.OPTION_REQUIRED optionRequired: globalRules.OPTION_REQUIRED
@ -174,6 +175,26 @@ export default {
if (this.selectedVehicleVin !== vehicleSelectionOptions.VEHICLE_NOT_LISTED) { if (this.selectedVehicleVin !== vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin); const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin);
if (vehicleLookupResponse.error) { if (vehicleLookupResponse.error) {
if (vehicleLookupResponse.status === 404) {
const vehicle = this.policyVehicles.find((pv) => pv.vin === this.selectedVehicleVin);
this.mainStore.resetVehicleState();
useMainStore().updateVehicle({
carId: '0',
category: '',
year: vehicle.vehicleYear || '',
make: vehicle.vehicleMake || '',
model: vehicle.vehicleModel || '',
style: vehicle.vehicleStyle || '',
vin: vehicle.vin,
noCoverage: this.noCoverageForSelectedVehicle,
deductible: this.deductibleForSelectedVehicle,
repairWaived: this.repairWaivedForSelectedVehicle
});
this.policyVinFound = false;
return this.navigateForward();
}
this.bailout = true; this.bailout = true;
return this.navigateForward(); return this.navigateForward();
} }
@ -192,12 +213,13 @@ export default {
}, },
navigateForward() { navigateForward() {
if (this.bailout) { if (this.bailout) {
this.$router.navigate( this.$router
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, .navigate(
this.$route, this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
{}, this.$route,
{} {},
); {}
);
} else if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { } else if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
this.$router this.$router
.navigate( .navigate(
@ -211,6 +233,13 @@ export default {
this.navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS, this.navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS,
this.$route this.$route
); );
} else if (!this.policyVinFound) {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND,
this.$route,
{},
{}
);
} else { } else {
this.$router.navigate( this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE, this.navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE,
@ -225,7 +254,8 @@ export default {
return await useMainStore().lookupVehicleByVin(vin); return await useMainStore().lookupVehicleByVin(vin);
} catch (responseError) { } catch (responseError) {
return { return {
error: true error: true,
status: responseError.status
}; };
} }
} }

View file

@ -477,6 +477,43 @@ describe('vin-lookup.vue', () => {
); );
}); });
}); });
// eslint-disable-next-line max-len
test('Policy Vehicle with invalid vin corrected. Click "Continue", execute navigate with navigationScenario.CORRECTED_VIN_FROM_POLICY_VEHICLE', async () => {
const user = userEvent.setup();
mountOptions.global.stubs.vinQuestion = false;
mountOptions.data = () => ({
vinWithNonMatchingCarId: true,
isCarIdDifferentFromTheStore: false,
vin: mockValidVin
});
getPartsOrQuestions.mockResponse = vehicleWithNoAdditionalPartsOrQuestionsMockResponse;
jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(vehicleQuestionsMixin.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.CORRECTED_VIN_FROM_POLICY_VEHICLE,
mockRoute
);
});
});
}); });
}); });
}); });

View file

@ -15,7 +15,7 @@
<vehicleBanner <vehicleBanner
class="mt-2 mb-4" class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget" cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" /> :displayGenericVehicleImage="carIdIsValid" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" /> <siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<vinLookupAlerts <vinLookupAlerts
class="mt-5" class="mt-5"
@ -106,23 +106,28 @@ export default {
return { mainStore }; return { mainStore };
}, },
data() { data() {
const vin = this.getVinFromStore();
return { return {
activeVehicleLookupAlertType: null, activeVehicleLookupAlertType: vin?.length > 0 && !this.hasValidCarId() ? vehicleLookupAlertTypes.NOT_FOUND : null,
needToLookupVehicle: true, needToLookupVehicle: true,
vehicleFromLookup: null, vehicleFromLookup: null,
vin: this.getVinFromStore(), vinWithNonMatchingCarId: vin?.length > 0 && !this.hasValidCarId(),
vin,
forwardButtonCarStyle: '', forwardButtonCarStyle: '',
vinPopulatedOnPageLoad: this.getVinFromStore()?.length > 0 vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId()
}; };
}, },
computed: { computed: {
isCarIdDifferentFromTheStore() { isCarIdDifferentFromTheStore() {
return ( return (
this.vehicleFromLookup !== null this.vehicleFromLookup !== null && this.hasValidCarId()
&& this.vehicleFromLookup.carId !== this.mainStore.vehicle.carId && this.vehicleFromLookup.carId !== this.mainStore.vehicle.carId
); );
}, },
isTwoIdenticalYMMVehicleFound() { isTwoIdenticalYMMVehicleFound() {
if (this.vehicleFromLookup === null) {
return false;
}
const vinYmmFound = `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model}`; const vinYmmFound = `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model}`;
const vinYmmExpected = const vinYmmExpected =
`${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`; `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
@ -138,6 +143,9 @@ export default {
return `!X!X!X!X!X!X!X!X!X!X!X${lastSixChars}`; return `!X!X!X!X!X!X!X!X!X!X!X${lastSixChars}`;
} }
return 'XXXXXXXXXXXXXXXXX'; return 'XXXXXXXXXXXXXXXXX';
},
carIdIsValid() {
return this.hasValidCarId();
} }
}, },
watch: { watch: {
@ -155,10 +163,13 @@ export default {
getVinFromStore() { getVinFromStore() {
return this.mainStore.vehicle.vin; return this.mainStore.vehicle.vin;
}, },
hasValidCarId() {
return this.mainStore.vehicle.carId && this.mainStore.vehicle.carId !== '0';
},
backButtonAction() { backButtonAction() {
/** /**
* this.navigationScenarios comes from base-mixin * this.navigationScenarios comes from base-mixin
*/ */
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
}, },
// NOTE: If form is not valid, this method is not called when 'Continue' button is clicked // NOTE: If form is not valid, this method is not called when 'Continue' button is clicked
@ -228,6 +239,14 @@ export default {
this.mainStore.updateVehicle(this.vehicleFromLookup); this.mainStore.updateVehicle(this.vehicleFromLookup);
} }
if (this.vinWithNonMatchingCarId) {
this.$router.navigate(
this.navigationScenarios.CORRECTED_VIN_FROM_POLICY_VEHICLE,
this.$route
);
return null;
}
const partsOrQuestionsResponse = await this.getPartsOrQuestions(); const partsOrQuestionsResponse = await this.getPartsOrQuestions();
if (partsOrQuestionsResponse.error) { if (partsOrQuestionsResponse.error) {
// To Do: Need requirement on what to do here // To Do: Need requirement on what to do here

View file

@ -25,6 +25,7 @@ const navigationScenarios = Object.freeze({
// Policy Vehicle // Policy Vehicle
CLICKED_FORWARD_LISTED_VEHICLE: 'CLICKED_FORWARD_LISTED_VEHICLE', CLICKED_FORWARD_LISTED_VEHICLE: 'CLICKED_FORWARD_LISTED_VEHICLE',
CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND: 'CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND',
CLICKED_FORWARD_NON_LISTED_VEHICLE: 'CLICKED_FORWARD_NON_LISTED_VEHICLE', CLICKED_FORWARD_NON_LISTED_VEHICLE: 'CLICKED_FORWARD_NON_LISTED_VEHICLE',
CLICKED_FORWARD_WITH_ENDORSEMENTS: 'CLICKED_FORWARD_WITH_ENDORSEMENTS', CLICKED_FORWARD_WITH_ENDORSEMENTS: 'CLICKED_FORWARD_WITH_ENDORSEMENTS',
@ -36,6 +37,7 @@ const navigationScenarios = Object.freeze({
CLICKED_FORWARD_WITHOUT_VIN: 'CLICKED_FORWARD_WITHOUT_VIN', CLICKED_FORWARD_WITHOUT_VIN: 'CLICKED_FORWARD_WITHOUT_VIN',
CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES: 'CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES', CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES: 'CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES',
SELECTED_VIN_WITH_MISMATCHED_GLASS: 'SELECTED_VIN_WITH_MISMATCHED_GLASS', SELECTED_VIN_WITH_MISMATCHED_GLASS: 'SELECTED_VIN_WITH_MISMATCHED_GLASS',
CORRECTED_VIN_FROM_POLICY_VEHICLE: 'CORRECTED_VIN_FROM_POLICY_VEHICLE',
SELECTED_MANUAL_VIN: 'SELECTED_MANUAL_VIN', SELECTED_MANUAL_VIN: 'SELECTED_MANUAL_VIN',
SELECTED_LICENSE_PLATE: 'SELECTED_LICENSE_PLATE', SELECTED_LICENSE_PLATE: 'SELECTED_LICENSE_PLATE',
SELECTED_HOME_ADDRESS: 'SELECTED_HOME_ADDRESS', SELECTED_HOME_ADDRESS: 'SELECTED_HOME_ADDRESS',

View file

@ -86,6 +86,10 @@ const routingTable = () => [
scenario: navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS, scenario: navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
destinationIssPageValue: issPageValues.VEHICLE_DAMAGE destinationIssPageValue: issPageValues.VEHICLE_DAMAGE
}, },
{
scenario: navigationScenarios.CORRECTED_VIN_FROM_POLICY_VEHICLE,
destinationIssPageValue: issPageValues.VEHICLE_DAMAGE
},
{ {
scenario: navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS, scenario: navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
destinationIssPageValue: issPageValues.PART_QUESTIONS destinationIssPageValue: issPageValues.PART_QUESTIONS
@ -110,6 +114,10 @@ const routingTable = () => [
scenario: issPageValues.VEHICLE_LOOKUP, scenario: issPageValues.VEHICLE_LOOKUP,
destinationIssPageValue: issPageValues.VEHICLE_LOOKUP destinationIssPageValue: issPageValues.VEHICLE_LOOKUP
}, },
{
scenario: issPageValues.BAILOUT_PAGE,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
},
{ {
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE destinationIssPageValue: issPageValues.BAILOUT_PAGE
@ -447,6 +455,10 @@ const routingTable = () => [
scenario: navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE, scenario: navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE,
destinationIssPageValue: issPageValues.VEHICLE_SELECTION destinationIssPageValue: issPageValues.VEHICLE_SELECTION
}, },
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND,
destinationIssPageValue: issPageValues.VIN_LOOKUP
},
{ {
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE destinationIssPageValue: issPageValues.BAILOUT_PAGE
@ -455,7 +467,6 @@ const routingTable = () => [
scenario: navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS, scenario: navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS,
destinationIssPageValue: issPageValues.POLICY_ENDORSEMENTS destinationIssPageValue: issPageValues.POLICY_ENDORSEMENTS
} }
] ]
}, },
{ {
@ -681,6 +692,10 @@ const routingTable = () => [
{ {
scenario: navigationScenarios.CLICKED_FORWARD, scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.VEHICLE_DAMAGE destinationIssPageValue: issPageValues.VEHICLE_DAMAGE
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND,
destinationIssPageValue: issPageValues.VIN_LOOKUP
} }
] ]
} }