diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index f6aa10c0f..08f429b5e 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -54,6 +54,10 @@ const endpoints = { url: "/vehicle/api/v1/vehicle/lookup-vin-by-address", method: "POST", }, + LookupVinByImage: { + url: "/vehicle/api/v1/vehicle/vins-by-image", + method: "POST", + }, IsVinByAddressPermissible: { url: "/vehicle/api/v1/vehicle/is-vin-by-address-permissible", method: "GET", diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 65ce02493..db15e9854 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -20,6 +20,7 @@ const storeActions = { LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin", LOOKUP_VIN_BY_PLATE: "lookupVinByPlate", LOOKUP_VIN_BY_ADDRESS: "lookupVinByAddress", + LOOKUP_VIN_BY_IMAGE: "lookupVinByImage", GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions", GET_PARTS: "getParts", GET_WIPERS: "getWipers", diff --git a/src/digital-components/textbox-question/textbox-question.spec.js b/src/digital-components/textbox-question/textbox-question.spec.js index dba54d544..aea563624 100644 --- a/src/digital-components/textbox-question/textbox-question.spec.js +++ b/src/digital-components/textbox-question/textbox-question.spec.js @@ -161,4 +161,252 @@ describe("textboxQuestion.vue", () => { // Assert expect(wrapper.vm.handleChange).toHaveBeenCalled; }); + + it("Should not display camera or spinner icon when disabled", () => { + // Arrange + const inputId = "test"; + + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + modelValue: "", + includeImageQuestion: true, + inputId: inputId, + isDisabled: true, + }, + mixins: [mockMixin], + attachTo: document.body, + }); + + // Act + const cameraIcon = wrapper.find('[data-test="image-upload"]'); + const loadingIcon = wrapper.findComponent("loader"); + + // Assert + expect(cameraIcon.exists()).toBe(false); + expect(loadingIcon.exists()).toBe(false); + }); + + describe("Image Uploading", () => { + it("Should call this.handleChange & emit update when fully valid image is submitted.", async () => { + // Arrange + const inputId = "test"; + const responseValue = "testResponse"; + + const imageHandler = jest.fn().mockImplementation( + () => + new Promise((resolve, reject) => { + resolve({ + data: [responseValue], + }); + }) + ); + + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + modelValue: "", + "onUpdate:modelValue": (v) => wrapper.setProps({ modelValue: v }), + includeImageQuestion: true, + inputId: inputId, + imageQuestionSubmitHandler: imageHandler, + }, + mixins: [mockMixin], + attachTo: document.body, + }); + + wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); + wrapper.vm.isImageValid = jest.fn().mockImplementation(() => true); + + // Act + const imageUploadField = wrapper.find('[data-test="image-upload"]'); + + await imageUploadField.trigger("change"); + + await wrapper.vm.$nextTick(); + + // Assert + + expect(wrapper.vm.imageQuestionSubmitHandler).toHaveBeenCalled(); + expect(wrapper.vm.handleChange).toHaveBeenCalled(); + expect(wrapper.emitted()).toHaveProperty("update:modelValue"); + }); + + it("Should not call handler and emit image-lookup-error when image is invalid.", async () => { + // Arrange + const inputId = "test"; + const responseValue = "testResponse"; + + const imageHandler = jest.fn().mockImplementation( + () => + new Promise((resolve, reject) => { + resolve({ + data: [responseValue], + }); + }) + ); + + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + modelValue: "", + "onUpdate:modelValue": (v) => wrapper.setProps({ modelValue: v }), + includeImageQuestion: true, + inputId: inputId, + imageQuestionSubmitHandler: imageHandler, + }, + mixins: [mockMixin], + attachTo: document.body, + }); + + wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); + wrapper.vm.isImageValid = jest.fn().mockImplementation(() => false); + + // Act + const imageUploadField = wrapper.find('[data-test="image-upload"]'); + + await imageUploadField.trigger("change"); + + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.imageQuestionSubmitHandler).not.toHaveBeenCalled(); + expect(wrapper.vm.handleChange).not.toHaveBeenCalled(); + expect(wrapper.emitted()).toHaveProperty("imageValidityError"); + }); + + it("Should emit image-lookup-error when image lookup responds with an error.", async () => { + // Arrange + const inputId = "test"; + + const imageHandler = jest.fn().mockImplementation( + () => + new Promise((resolve, reject) => { + reject({}); + }) + ); + + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + modelValue: "", + "onUpdate:modelValue": (v) => wrapper.setProps({ modelValue: v }), + includeImageQuestion: true, + inputId: inputId, + imageQuestionSubmitHandler: imageHandler, + }, + mixins: [mockMixin], + attachTo: document.body, + }); + + wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); + wrapper.vm.isImageValid = jest.fn().mockImplementation(() => true); + + // Act + const imageUploadField = wrapper.find('[data-test="image-upload"]'); + + await imageUploadField.trigger("change"); + + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.imageQuestionSubmitHandler).toHaveBeenCalled(); + expect(wrapper.vm.handleChange).not.toHaveBeenCalled(); + expect(wrapper.emitted()).toHaveProperty("imageLookupError"); + }); + }); + + describe("Image Validation", () => { + it("Should return true if a valid image is tested.", () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + inputId: "input ID", + maxFileSize: 5140 * 1028, + }, + mixins: [mockMixin], + }); + + const image = { + size: 100 * 1028, + }; + + // Act + const result = wrapper.vm.isImageValid(image); + + // Assert + expect(result).toBeTruthy(); + }); + + it("Should return false if null image is tested.", () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + inputId: "input ID", + maxFileSize: 5140 * 1028, + }, + mixins: [mockMixin], + }); + + const image = null; + + // Act + const result = wrapper.vm.isImageValid(image); + + // Assert + expect(result).toBeFalsy(); + }); + + it("Should return false if oversized image is tested.", () => { + // Arrange + const wrapper = shallowMount(textboxQuestion, { + global: { + directives: { + maska: maska, + }, + }, + propsData: { + inputId: "input ID", + maxFileSize: 5140 * 1028, + }, + mixins: [mockMixin], + }); + + const image = { + size: 10000000 * 1028, + }; + + // Act + const result = wrapper.vm.isImageValid(image); + + // Assert + expect(result).toBeFalsy(); + }); + }); }); diff --git a/src/digital-components/textbox-question/textbox-question.vue b/src/digital-components/textbox-question/textbox-question.vue index 335c47a60..a4de11254 100644 --- a/src/digital-components/textbox-question/textbox-question.vue +++ b/src/digital-components/textbox-question/textbox-question.vue @@ -15,7 +15,12 @@ hideInput ? 'hide-input' : '', ]" v-html="questionText"> -