-
-
+
+
+
+
+
@@ -92,13 +94,14 @@ export default {
questionAlignment: String, // Left or center. Left is default.
cornerStyle: String, // Rounded or square. Square is default.
includeSearchIcon: Boolean,
- includeCameraIcon: Boolean,
+ includeImageQuestion: Boolean,
+ imageQuestionSubmitHandler: Function,
},
setup(props) {
const propsClone = Object.assign({}, props);
const modelValue = propsClone.modelValue;
let initialValue;
- let isLoading = ref(false);
+ let isImageProcessing = ref(false);
switch (typeof modelValue) {
case "number":
@@ -128,30 +131,35 @@ export default {
validate,
meta,
errors,
- isLoading,
+ isImageProcessing,
};
},
methods: {
- async submitImage(e) {
- this.isLoading = true;
- await this.dispatchStoreAction(storeActions.LOOKUP_VIN_BY_IMAGE, e.target.files[0])
- .then(async (response) => {
- if (response.data.length > 0) {
- this.value = response.data[0];
+ async imageChanged(e) {
+ let file = e.target.files[0];
- // Wait for DOM to update before firing change event to handle validation.
- await this.$nextTick();
+ if (!this.isImageValid(file)) {
+ this.$emit("imageValidityError");
+ return;
+ }
- document.getElementById(this.inputId).dispatchEvent(new Event("change"));
- } else {
- this.$emit("imageLookupError");
- }
- })
- .catch((error) => {
- this.$emit("imageLookupError");
- });
- this.isLoading = false;
- e.target.value = null;
+ this.isImageProcessing = true;
+
+ try {
+ this.value = await this.imageQuestionSubmitHandler(file);
+
+ await this.$nextTick();
+
+ document.getElementById(this.inputId).dispatchEvent(new Event("change"));
+ } catch (error) {
+ this.$emit("imageLookupError");
+ }
+
+ this.isImageProcessing = false;
+ },
+
+ isImageValid(imageFile) {
+ return imageFile && imageFile.size < this.maxFileSize;
},
},
computed: {
@@ -190,6 +198,12 @@ export default {
return questionText;
},
},
+
+ maxFileSize() {
+ let kiloBytes = 5140;
+
+ return kiloBytes * 1028;
+ },
},
watch: {
async value(newValue) {
diff --git a/src/layouts/vin-lookup/vin-lookup.spec.js b/src/layouts/vin-lookup/vin-lookup.spec.js
index 06174dafd..f47fdfb36 100644
--- a/src/layouts/vin-lookup/vin-lookup.spec.js
+++ b/src/layouts/vin-lookup/vin-lookup.spec.js
@@ -256,6 +256,84 @@ describe("vin-lookup.vue", () => {
expect(wrapper.findAllComponents({ name: "alert" }).length).toBe(1);
});
});
+
+ describe("getVinFromImage", () => {
+ test("GetVinFromImage resolves with first valid VIN when any vins are returned.", async () => {
+ // Arrange
+ const responseValue = "testResponse";
+ const lookup = jest.fn().mockImplementation(() => {
+ return new Promise((resolve, reject) => resolve([responseValue]));
+ });
+
+ const image = {};
+
+ const storeMixin = {
+ methods: {
+ dispatchStoreAction: lookup,
+ },
+ };
+
+ const { wrapper } = setupMocks({
+ mixins: [storeMixin],
+ });
+
+ // Act
+ const response = await wrapper.vm.getVinFromImage(image);
+
+ // Assert
+ expect(lookup).toHaveBeenCalled();
+ expect(response).toEqual(responseValue);
+ });
+
+ test("GetVinFromImage rejects if no vins are returned.", async () => {
+ // Arrange
+ const lookup = jest.fn().mockImplementation(() => {
+ return new Promise((resolve, reject) => resolve([]));
+ });
+
+ const image = {};
+
+ const storeMixin = {
+ methods: {
+ dispatchStoreAction: lookup,
+ },
+ };
+
+ const { wrapper } = setupMocks({
+ mixins: [storeMixin],
+ });
+
+ // Act
+ const promise = wrapper.vm.getVinFromImage(image);
+
+ // Assert
+ await expect(promise).rejects.toEqual("No VINs detected.");
+ });
+
+ test("GetVinFromImage rejects if an error occurs.", async () => {
+ const lookup = jest.fn().mockImplementation(() => {
+ return new Promise((resolve, reject) => reject());
+ });
+
+ const image = {};
+
+ const storeMixin = {
+ methods: {
+ dispatchStoreAction: lookup,
+ },
+ };
+
+ const { wrapper } = setupMocks({
+ mixins: [storeMixin],
+ });
+
+ // Act
+ const promise = wrapper.vm.getVinFromImage(image);
+
+ // Assert
+ await expect(promise).rejects.toEqual("An error occurred during the lookup.");
+ });
+ });
});
function setupMocks({ customMountOptions }) {
diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue
index 69dafd555..3c5549333 100644
--- a/src/layouts/vin-lookup/vin-lookup.vue
+++ b/src/layouts/vin-lookup/vin-lookup.vue
@@ -19,10 +19,13 @@
validationRules="vin-required|vin-format"
:isDisabled="vinPopulatedOnPageLoad"
maxLength="17"
- includeCameraIcon
:mask="vinMask"
+ includeImageQuestion
+ :imageQuestionSubmitHandler="getVinFromImage"
@image-lookup-error="displayVinScanAlert"
- data-test="vin-lookup-field" />
+ @image-validity-error="displayVinScanAlert"
+ data-test="vin-lookup-field"
+ ref="vinLookupQuestion" />
@@ -395,6 +398,21 @@ export default {
displayVinScanAlert() {
this.displayVinScanFailedAlert = true;
},
+ getVinFromImage(image) {
+ return new Promise((resolve, reject) => {
+ this.dispatchStoreAction(storeActions.LOOKUP_VIN_BY_IMAGE, image)
+ .then((response) => {
+ if (response.data.length > 0) {
+ resolve(response.data[0]);
+ } else {
+ reject("No VINs detected.");
+ }
+ })
+ .catch(() => {
+ reject("An error occurred during the lookup.");
+ });
+ });
+ },
resetAlerts() {
this.displayMatchedDifferentVehicleAlert = false;
this.displayNonServiceableZipAlert = false;