CSR-319: move validation rules and errors into centralized files; fix and set up validation unit tests
This commit is contained in:
parent
ca0b266efa
commit
312ba851bf
12 changed files with 167 additions and 96 deletions
12
src/constants/error-messages.js
Normal file
12
src/constants/error-messages.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
const errorMessages = {
|
||||
DAMAGE_LOCATION_REQUIRED: "Please select damage location",
|
||||
DAMAGE_SIDE_REQUIRED: "Please select vehicle side",
|
||||
DRIVER_SIDE_OPTIONS_REQUIRED: "Please select window",
|
||||
PASSENGER_SIDE_OPTIONS_REQUIRED: "Please select window",
|
||||
WINDSHIELD_DAMAGE_TYPE_REQUIRED: "Please select windshield damage",
|
||||
WINDSHIELD_CHIP_COUNT_REQUIRED: "Please select chip(s)",
|
||||
WINSHIELD_REPLACE_OPTIONS_REQUIRED: "Please select windshield part",
|
||||
REPLACE_OPTIONS_REQUIRED: "Please select rear window type",
|
||||
};
|
||||
|
||||
export default { errorMessages };
|
||||
8
src/helpers/validation-rules.js
Normal file
8
src/helpers/validation-rules.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export function required(errorMessage) {
|
||||
return (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return errorMessage;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
29
src/helpers/validation-rules.spec.js
Normal file
29
src/helpers/validation-rules.spec.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { required } from "@/helpers/validation-rules";
|
||||
|
||||
describe("validation-rules.vue", () => {
|
||||
test("required rules should return error if value missing", () => {
|
||||
|
||||
//Arrange
|
||||
const testFn = required("an error");
|
||||
|
||||
//Act
|
||||
const testResponse = testFn();
|
||||
|
||||
//Assert
|
||||
expect(testResponse).toBe("an error");
|
||||
});
|
||||
});
|
||||
|
||||
describe("validation-rules.vue", () => {
|
||||
test("required rules should return true if value present", () => {
|
||||
|
||||
//Arrange
|
||||
const testFn = required("an error");
|
||||
|
||||
//Act
|
||||
const testResponse = testFn('some value');
|
||||
|
||||
//Assert
|
||||
expect(testResponse).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -16,14 +16,11 @@
|
|||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
import store from "@/store";
|
||||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import errorMessages from "@/constants/error-messages";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("damage-location-required", (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select damage location";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
defineRule("damage-location-required", required(errorMessages.DAMAGE_LOCATION_REQUIRED));
|
||||
|
||||
export default ({
|
||||
name: "damageLocationQuestion",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div v-if="isAvailable && this.answersToDisplay.length > 0" class="replace-options-question" :class="this.answersToDisplay.length < 2 ? 'd-noneXXXXconsole.logXXXXXXXXXXXXX' : ''" aria-live="polite">
|
||||
answersToDisplay.length: {{answersToDisplay.length}}
|
||||
<buttonQuestion
|
||||
isWide
|
||||
:questionText="questionText"
|
||||
|
|
|
|||
|
|
@ -39,28 +39,13 @@ import buttonQuestion from "@/common-components/button-question/button-question"
|
|||
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||
import store from "@/store";
|
||||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import errorMessages from "@/constants/error-messages";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("damage-side-required", (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select vehicle side";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
defineRule("driver-side-options-required", (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select window";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
defineRule("passenger-side-options-required", (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select window";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
defineRule("damage-side-required", required(errorMessages.DAMAGE_SIDE_REQUIRED));
|
||||
defineRule("driver-side-options-required", required(errorMessages.DRIVER_SIDE_OPTIONS_REQUIRED));
|
||||
defineRule("passenger-side-options-required", required(errorMessages.PASSENGER_SIDE_OPTIONS_REQUIRED));
|
||||
|
||||
export default ({
|
||||
name: "sideDoorOptions",
|
||||
|
|
|
|||
|
|
@ -13,11 +13,12 @@ import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-que
|
|||
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import { nextTick } from "vue";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import store from "@/store";
|
||||
import { validate } from "vee-validate";
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||
|
|
@ -36,7 +37,9 @@ jest.mock("@/store", () => ({
|
|||
getters: {
|
||||
vehicle: {
|
||||
carId: "C00000000",
|
||||
image: "test.jpg",
|
||||
},
|
||||
eventBusItem: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
@ -211,12 +214,84 @@ describe("vehicle-damage.vue", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test("when onInvalidSubmit is triggered with errors focus will be put on the first element with an error", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
const mockedValidationPayload = {
|
||||
values: {},
|
||||
errors: {
|
||||
driverSideOptions: 'Please select window',
|
||||
passengerSideOptions: 'Please select window'
|
||||
},
|
||||
results: {},
|
||||
}
|
||||
const newObj = document.createElement('input');
|
||||
newObj.setAttribute("id", "testInput");
|
||||
newObj.setAttribute("data-focus-target", "driverSideOptions");
|
||||
document.body.appendChild(newObj);
|
||||
const testInputElement = document.getElementById("testInput");
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
wrapper.vm.onInvalidSubmit(mockedValidationPayload);
|
||||
await nextTick();
|
||||
const focusedEl = document.activeElement;
|
||||
|
||||
//Assert
|
||||
expect(testInputElement).toBe(focusedEl);
|
||||
});
|
||||
});
|
||||
|
||||
// THE FOLLOWING TEST IS NOT NECESSARILY REQUIRED FOR COVERAGE
|
||||
// BUT KEEP FOR AN EXAMPLE OF A VALIDATION TEST
|
||||
//
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test("when validation rules are set they should validate correctly", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
const testNull = validate( "", "replace-options-required");
|
||||
const testString = validate( "sldfj", "replace-options-required");
|
||||
await flushPromises();
|
||||
|
||||
//Assert
|
||||
testNull.then(function(data) {
|
||||
expect(data.valid).toEqual(false);
|
||||
});
|
||||
testString.then(function(data) {
|
||||
expect(data.valid).toEqual(true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function setupMocks({
|
||||
pageHeaderWidgetHeaderText = {},
|
||||
mountOptionsMockData = {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
store: {
|
||||
getters: {
|
||||
vehicle: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}) {
|
||||
//Mock api responses
|
||||
|
|
@ -281,6 +356,7 @@ function setupMocks({
|
|||
|
||||
replaceOptionsQuestion.methods = {
|
||||
initializeComponent: jest.fn(),
|
||||
updateSelectedValues: jest.fn(),
|
||||
};
|
||||
|
||||
funnelFooter.methods = {
|
||||
|
|
@ -288,7 +364,9 @@ function setupMocks({
|
|||
}
|
||||
|
||||
const mountOptions = getMountOptions(mountOptionsMockData);
|
||||
const wrapper = shallowMount(vehicleDamage, mountOptions);
|
||||
mountOptions['attachTo'] = document.body; // append wrapper to document.body to test DOM methods
|
||||
|
||||
const wrapper = mount(vehicleDamage, mountOptions);
|
||||
|
||||
const funnelHeaderWrapper = wrapper.findComponent({ name: "funnelHeader" });
|
||||
funnelHeaderWrapper.vm.initializeComponent =
|
||||
|
|
@ -300,38 +378,27 @@ function setupMocks({
|
|||
|
||||
const sideDoorOptionsWrapper = wrapper.findComponent({ name: "sideDoorOptions" });
|
||||
sideDoorOptionsWrapper.vm.initializeComponent =
|
||||
sideDoorOptions.methods.initializeComponent;
|
||||
sideDoorOptions.methods.initializeComponent;
|
||||
|
||||
const windshieldOptionsWrapper = wrapper.findComponent({
|
||||
name: "windshieldOptions",
|
||||
});
|
||||
|
||||
const windshieldOptionsWrapper = wrapper.findComponent({ name: "windshieldOptions" });
|
||||
windshieldOptionsWrapper.vm.initializeComponent =
|
||||
windshieldOptions.methods.initializeComponent;
|
||||
windshieldOptions.methods.initializeComponent;
|
||||
|
||||
const funnelSubHeaderWrapper = wrapper.findComponent({
|
||||
name: "funnelSubHeader",
|
||||
});
|
||||
const funnelSubHeaderWrapper = wrapper.findComponent({ name: "funnelSubHeader" });
|
||||
funnelSubHeaderWrapper.vm.initializeComponent =
|
||||
funnelSubHeader.methods.initializeComponent;
|
||||
|
||||
const backGlassOptionsWrapper = wrapper.findComponent({
|
||||
name: "replaceOptionsQuestion",
|
||||
});
|
||||
const backGlassOptionsWrapper = wrapper.findComponent({ name: "replaceOptionsQuestion" });
|
||||
backGlassOptionsWrapper.vm.initializeComponent =
|
||||
replaceOptionsQuestion.methods.initializeComponent;
|
||||
replaceOptionsQuestion.methods.initializeComponent;
|
||||
|
||||
const damageLocationQuestionWrapper = wrapper.findComponent({
|
||||
name: "damageLocationQuestion",
|
||||
});
|
||||
const damageLocationQuestionWrapper = wrapper.findComponent({ name: "damageLocationQuestion" });
|
||||
damageLocationQuestionWrapper.vm.initializeComponent =
|
||||
damageLocationQuestion.methods.initializeComponent;
|
||||
damageLocationQuestion.methods.initializeComponent;
|
||||
|
||||
const funnelFooterWrapper = wrapper.findComponent({
|
||||
name: "funnelFooter",
|
||||
});
|
||||
|
||||
funnelFooterWrapper.vm.initializeComponent = funnelFooter.methods.initializeComponent;
|
||||
const funnelFooterWrapper = wrapper.findComponent({ name: "funnelFooter" });
|
||||
funnelFooterWrapper.vm.initializeComponent =
|
||||
funnelFooter.methods.initializeComponent;
|
||||
|
||||
return { wrapper, apiPromise };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
<funnelHeader ref="funnelHeader" />
|
||||
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false />
|
||||
<funnelSubHeader ref="funnelSubHeader" />
|
||||
selectedDamageLocations {{selectedDamageLocations}}
|
||||
isRearWindowDamageLocation {{ isRearWindowDamageLocation }}
|
||||
<Form
|
||||
@submit="onSubmit"
|
||||
@invalid-submit="onInvalidSubmit"
|
||||
|
|
@ -80,15 +82,11 @@ import { storeActions } from "@/constants/store-actions";
|
|||
import store from "@/store";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import errorMessages from "@/constants/error-messages";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("replace-options-required", (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select rear window type";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
defineRule("replace-options-required", required(errorMessages.REPLACE_OPTIONS_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "vehicle-damage",
|
||||
|
|
@ -180,21 +178,20 @@ export default {
|
|||
);
|
||||
},
|
||||
onSubmit(values) {
|
||||
window.alert('Success! Submitted values: ' + JSON.stringify(values, null ,2))
|
||||
console.log('submitted: ', JSON.stringify(values, null ,2));
|
||||
// window.alert('Success! Submitted values: ' + JSON.stringify(values, null ,2))
|
||||
// TODO - ADD IN SAVING OF DATA
|
||||
// TODO - ADD IN ADVANCING TO NEXT PAGE
|
||||
console.log('submitted: ', JSON.stringify(values, null, 2));
|
||||
},
|
||||
onInvalidSubmit({ values, errors, results }) {
|
||||
// identify the first error field and put focus on it
|
||||
console.log("values: ", values);
|
||||
console.log("errors: ", errors);
|
||||
console.log("results: ", results);
|
||||
// get error names array
|
||||
const errorNames = errors ? Object.keys(errors) : [];
|
||||
const firstErrorEl = errorNames[0];
|
||||
if (firstErrorEl) {
|
||||
console.log('firstErrorEl: ', firstErrorEl);
|
||||
const qsString = "[data-focus-target='" + firstErrorEl + "']";
|
||||
document.querySelector(qsString).focus();
|
||||
const el = document.querySelector(qsString);
|
||||
el && el.focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div class="windshield-options">
|
||||
selectedDamageLocations: {{selectedDamageLocations}}
|
||||
<windshieldDamageTypeQuestion ref="windshieldDamageTypeQuestion"
|
||||
:isAvailable=isWindshieldDamageLocation
|
||||
:suppressError="suppressError"
|
||||
|
|
@ -29,15 +28,14 @@ import windshieldDamageTypeQuestion from"@/layouts/vehicle-damage/windshield-opt
|
|||
import windshieldChipCountQuestion from"@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question";
|
||||
import replaceOptionsQuestion from"@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||
import { defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import errorMessages from "@/constants/error-messages";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("windshield-damage-type-required", (value) => {
|
||||
console.log('windshield-damage-type-required, value: ', value);
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select windshield damage";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
defineRule("windshield-damage-type-required", required(errorMessages.WINDSHIELD_DAMAGE_TYPE_REQUIRED));
|
||||
defineRule("windshield-chip-count-required", required(errorMessages.WINDSHIELD_CHIP_COUNT_REQUIRED));
|
||||
defineRule("windshield-replace-options-required", required(errorMessages.WINSHIELD_REPLACE_OPTIONS_REQUIRED));
|
||||
|
||||
defineRule("checkForRepairAndReplace", (value, [other]) => {
|
||||
console.log('checkForRepairAndReplace, value: ', value, ' / other: ', other);
|
||||
if (value.toString().toUpperCase().includes("REPAIR") && other.toString().toUpperCase().includes("WINDSHIELD") && other.length > 1) {
|
||||
|
|
@ -45,18 +43,6 @@ defineRule("checkForRepairAndReplace", (value, [other]) => {
|
|||
}
|
||||
return true;
|
||||
});
|
||||
defineRule("windshield-chip-count-required", (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select chip(s)";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
defineRule("windshield-replace-options-required", (value) => {
|
||||
if (!value || value.length < 1) {
|
||||
return "Please select windshield part";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
export default ({
|
||||
name: "windshieldOptions",
|
||||
|
|
|
|||
|
|
@ -186,12 +186,7 @@ describe("vehicle-style.vue", () => {
|
|||
store: {
|
||||
commit: jest.fn(),
|
||||
getters: {
|
||||
vehicle: {
|
||||
year: 2020,
|
||||
make: "Honda",
|
||||
model: "Civic",
|
||||
style: "2 Door",
|
||||
},
|
||||
vehicle: {},
|
||||
},
|
||||
},
|
||||
actionList: [
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ describe("list-card.vue", () => {
|
|||
});
|
||||
wrapper.vm.handleCheckChange();
|
||||
// Assert
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean}]);
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: [Boolean, String]}]);
|
||||
});
|
||||
|
||||
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ export default {
|
|||
created(){
|
||||
if(Array.isArray(this.selectedValues)){
|
||||
this.checkValue = this.isMultiSelect ? this.selectedValues.includes(this.value) : this.selectedValues[0];
|
||||
console.log('=================== this.checkValue: ', this.checkValue)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -101,9 +100,6 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
handleCheckChange(newValue, oldValue){
|
||||
// console.log('LC handleCheckChange... oldValue: ', oldValue);
|
||||
// console.log('LC handleCheckChange... newValue: ', newValue);
|
||||
|
||||
const isInitialization = typeof(oldValue) === 'function';
|
||||
if (!isInitialization) {
|
||||
this.$emit('isCheckedChanged', { checkValue: this.checkValue, value: this.value.toString() });
|
||||
|
|
|
|||
Loading…
Reference in a new issue