diff --git a/src/app/modules/angular-slickgrid/editors/__tests__/autoCompleteEditor.spec.ts b/src/app/modules/angular-slickgrid/editors/__tests__/autoCompleteEditor.spec.ts
index c4f8a845d..35d842c74 100644
--- a/src/app/modules/angular-slickgrid/editors/__tests__/autoCompleteEditor.spec.ts
+++ b/src/app/modules/angular-slickgrid/editors/__tests__/autoCompleteEditor.spec.ts
@@ -1,7 +1,8 @@
 import { Editors } from '../index';
 import { AutoCompleteEditor } from '../autoCompleteEditor';
-import { AutocompleteOption, Column, FieldType, EditorArguments, GridOption, OperatorType, KeyCode } from '../../models';
+import { AutocompleteOption, Column, EditorArgs, EditorArguments, GridOption, KeyCode, FieldType } from '../../models';
 
+const KEY_CHAR_A = 97;
 const containerId = 'demo-container';
 
 // define a <div> container to simulate the grid container
@@ -12,13 +13,19 @@ const dataViewStub = {
 };
 
 const gridOptionMock = {
+  autoCommitEdit: false,
   enableeditoring: true,
   enableeditorTrimWhiteSpace: true,
 } as GridOption;
 
+const getEditorLockMock = {
+  commitCurrentEdit: jest.fn(),
+};
+
 const gridStub = {
   getOptions: () => gridOptionMock,
   getColumns: jest.fn(),
+  getEditorLock: () => getEditorLockMock,
   getHeaderRowColumn: jest.fn(),
   render: jest.fn(),
 };
@@ -193,5 +200,225 @@ describe('AutoCompleteEditor', () => {
 
       expect(editor.elementCollection).toEqual([{ value: 'male', label: 'Male' }, { value: 'female', label: 'Female' }]);
     });
+
+    it('should return True when calling "isValueChanged()" method with previously dispatched keyboard event being char "a"', () => {
+      const event = new (window.window as any).KeyboardEvent('keydown', { keyCode: KEY_CHAR_A, bubbles: true, cancelable: true });
+
+      editor = new AutoCompleteEditor(editorArguments);
+      const editorElm = divContainer.querySelector<HTMLInputElement>('input.editor-gender');
+
+      editorElm.focus();
+      editorElm.dispatchEvent(event);
+
+      expect(editor.isValueChanged()).toBe(true);
+    });
+
+    it('should return False when calling "isValueChanged()" method with previously dispatched keyboard event is same char as current value', () => {
+      const event = new (window.window as any).KeyboardEvent('keydown', { keyCode: KEY_CHAR_A, bubbles: true, cancelable: true });
+
+      editor = new AutoCompleteEditor(editorArguments);
+      const editorElm = divContainer.querySelector<HTMLInputElement>('input.editor-gender');
+
+      editor.loadValue({ id: 123, gender: 'a', isActive: true });
+      editorElm.focus();
+      editorElm.dispatchEvent(event);
+
+      expect(editor.isValueChanged()).toBe(false);
+    });
+
+    it('should return True when calling "isValueChanged()" method with previously dispatched keyboard event as ENTER and "alwaysSaveOnEnterKey" is enabled', () => {
+      const event = new (window.window as any).KeyboardEvent('keydown', { keyCode: KeyCode.ENTER, bubbles: true, cancelable: true });
+      mockColumn.internalColumnEditor.alwaysSaveOnEnterKey = true;
+
+      editor = new AutoCompleteEditor(editorArguments);
+      const editorElm = divContainer.querySelector<HTMLInputElement>('input.editor-gender');
+
+      editorElm.focus();
+      editorElm.dispatchEvent(event);
+
+      expect(editor.isValueChanged()).toBe(true);
+    });
+
+    it('should call "focus()" method and expect the DOM element to be focused and selected', async () => {
+      editor = new AutoCompleteEditor(editorArguments);
+      const editorElm = editor.editorDomElement;
+      const spy = jest.spyOn(editorElm, 'focus');
+      editor.focus();
+
+      expect(spy).toHaveBeenCalled();
+    });
+
+    it('should return override the item data as an object found from the collection when calling "applyValue" that passes validation', () => {
+      mockColumn.internalColumnEditor.validator = null;
+      mockItemData = { id: 123, gender: 'female', isActive: true };
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.applyValue(mockItemData, { value: 'female', label: 'female' });
+
+      expect(mockItemData).toEqual({ id: 123, gender: { value: 'female', label: 'female' }, isActive: true });
+    });
+
+    it('should return override the item data as a string found from the collection when calling "applyValue" that passes validation', () => {
+      mockColumn.internalColumnEditor.validator = null;
+      mockColumn.internalColumnEditor.collection = ['male', 'female'];
+      mockItemData = { id: 123, gender: 'female', isActive: true };
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.applyValue(mockItemData, 'female');
+
+      expect(mockItemData).toEqual({ id: 123, gender: 'female', isActive: true });
+    });
+
+    it('should return item data with an empty string in its value when calling "applyValue" which fails the custom validation', () => {
+      mockColumn.internalColumnEditor.validator = (value: any, args: EditorArgs) => {
+        if (value.label.length < 10) {
+          return { valid: false, msg: 'Must be at least 10 chars long.' };
+        }
+        return { valid: true, msg: '' };
+      };
+      mockItemData = { id: 123, gender: 'female', isActive: true };
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.applyValue(mockItemData, 'female');
+
+      expect(mockItemData).toEqual({ id: 123, gender: '', isActive: true });
+    });
+
+    it('should return DOM element value when "forceUserInput" is enabled and loaded value length is greater then minLength defined when calling "serializeValue"', () => {
+      mockColumn.internalColumnEditor.editorOptions = { forceUserInput: true, };
+      mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.loadValue(mockItemData);
+      editor.setValue('Female');
+      const output = editor.serializeValue();
+
+      expect(output).toBe('Female');
+    });
+
+    it('should return DOM element value when "forceUserInput" is enabled and loaded value length is greater then custom minLength defined when calling "serializeValue"', () => {
+      mockColumn.internalColumnEditor.editorOptions = { forceUserInput: true, minLength: 2 } as AutocompleteOption;
+      mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.loadValue(mockItemData);
+      editor.setValue('Female');
+      const output = editor.serializeValue();
+
+      expect(output).toBe('Female');
+    });
+
+    it('should return loaded value when "forceUserInput" is enabled and loaded value length is lower than minLength defined when calling "serializeValue"', () => {
+      mockColumn.internalColumnEditor.editorOptions = { forceUserInput: true, };
+      mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.loadValue(mockItemData);
+      editor.setValue('F');
+      const output = editor.serializeValue();
+
+      expect(output).toBe('Male');
+    });
+
+    it('should return correct object value even when defining a "customStructure" when calling "serializeValue"', () => {
+      mockColumn.internalColumnEditor.collection = [{ option: 'male', text: 'Male' }, { option: 'female', text: 'Female' }];
+      mockColumn.internalColumnEditor.customStructure = { value: 'option', label: 'text' };
+      mockItemData = { id: 123, gender: { option: 'female', text: 'Female' }, isActive: true };
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.loadValue(mockItemData);
+      const output = editor.serializeValue();
+
+      expect(output).toBe('Female');
+    });
+
+    it('should return an object output when calling "serializeValue" with its column definition set to "FieldType.object"', () => {
+      mockColumn.type = FieldType.object;
+      mockColumn.internalColumnEditor.collection = [{ value: 'm', label: 'Male' }, { value: 'f', label: 'Female' }];
+      mockItemData = { id: 123, gender: { value: 'f', label: 'Female' }, isActive: true };
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.loadValue(mockItemData);
+      const output = editor.serializeValue();
+
+      expect(output).toEqual({ value: 'f', label: 'Female' });
+    });
+
+    it('should call "getEditorLock" when "hasAutoCommitEdit" is enabled after calling "save()" method', async () => {
+      gridOptionMock.autoCommitEdit = true;
+      const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit');
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.save();
+
+      expect(spy).toHaveBeenCalled();
+    });
+
+    it('should call "commitChanges" when "hasAutoCommitEdit" is disabled after calling "save()" method', async () => {
+      gridOptionMock.autoCommitEdit = false;
+      const spy = jest.spyOn(editorArguments, 'commitChanges');
+
+      editor = new AutoCompleteEditor(editorArguments);
+      editor.save();
+
+      expect(spy).toHaveBeenCalled();
+    });
+
+    it('should validate and return False when field is required and field is an empty string', () => {
+      mockColumn.internalColumnEditor.required = true;
+      editor = new AutoCompleteEditor(editorArguments);
+      const validation = editor.validate('');
+
+      expect(validation).toEqual({ valid: false, msg: 'Field is required' });
+    });
+
+    it('should validate and return True when field is required and field a valid object', () => {
+      mockColumn.internalColumnEditor.required = true;
+      editor = new AutoCompleteEditor(editorArguments);
+      const validation = editor.validate(mockItemData);
+
+      expect(validation).toEqual({ valid: true, msg: null });
+    });
+
+    describe('onSelect method', () => {
+      it('should expect "setValue" and "autoCommitEdit" to have been called with a string when item provided is a string', (done) => {
+        const spyCommitEdit = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit');
+        gridOptionMock.autoCommitEdit = false;
+        mockColumn.internalColumnEditor.collection = ['male', 'female'];
+        mockItemData = { id: 123, gender: 'female', isActive: true };
+
+        editor = new AutoCompleteEditor(editorArguments);
+        const spySetValue = jest.spyOn(editor, 'setValue');
+        const output = editor.onSelect(null, { item: mockItemData.gender });
+
+        // HOW DO WE TRIGGER the jQuery UI autocomplete select event? The following works only on "autocompleteselect"
+        // but that doesn't trigger the "select" (onSelect) directly
+        // const editorElm = editor.editorDomElement;
+        // editorElm.on('autocompleteselect', (event, ui) => console.log(ui));
+        // editorElm[0].dispatchEvent(new (window.window as any).CustomEvent('autocompleteselect', { detail: { item: 'female' }, bubbles: true, cancelable: true }));
+
+        setTimeout(() => {
+          expect(output).toBe(false);
+          expect(spyCommitEdit).toHaveBeenCalled();
+          expect(spySetValue).toHaveBeenCalledWith('female');
+          done();
+        });
+      });
+
+      it('should expect "setValue" and "autoCommitEdit" to have been called with the string label when item provided is an object', () => {
+        const spyCommitEdit = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit');
+        gridOptionMock.autoCommitEdit = false;
+        mockColumn.internalColumnEditor.collection = [{ value: 'm', label: 'Male' }, { value: 'f', label: 'Female' }];
+        mockItemData = { id: 123, gender: { value: 'f', label: 'Female' }, isActive: true };
+
+        editor = new AutoCompleteEditor(editorArguments);
+        const spySetValue = jest.spyOn(editor, 'setValue');
+        const output = editor.onSelect(null, { item: mockItemData.gender });
+
+        expect(output).toBe(false);
+        expect(spyCommitEdit).toHaveBeenCalled();
+        expect(spySetValue).toHaveBeenCalledWith('Female');
+      });
+    });
   });
 });
diff --git a/src/app/modules/angular-slickgrid/editors/autoCompleteEditor.ts b/src/app/modules/angular-slickgrid/editors/autoCompleteEditor.ts
index cfd0eaa90..24956ffa5 100644
--- a/src/app/modules/angular-slickgrid/editors/autoCompleteEditor.ts
+++ b/src/app/modules/angular-slickgrid/editors/autoCompleteEditor.ts
@@ -16,6 +16,9 @@ import { findOrDefault } from '../services/utilities';
 // using external non-typed js libraries
 declare var $: any;
 
+// minimum length of chars to type before starting to start querying
+const MIN_LENGTH = 3;
+
 /*
  * An example of a 'detached' editor.
  * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter.
@@ -104,7 +107,7 @@ export class AutoCompleteEditor implements Editor {
   }
 
   focus() {
-    this._$editorElm.focus();
+    this._$editorElm.focus().select();
   }
 
   getValue() {
@@ -142,15 +145,16 @@ export class AutoCompleteEditor implements Editor {
     }
   }
 
-  serializeValue() {
-    // if user provided a custom structure, we will serialize the value returned from the object with custom structure
-    const minLength = typeof this.editorOptions.minLength !== 'undefined' ? this.editorOptions.minLength : 3;
+  serializeValue(): any {
+    // if you want to add the autocomplete functionality but want the user to be able to input a new option
     if (this.editorOptions.forceUserInput) {
-      this._currentValue = this._$editorElm.val().length >= minLength ? this._$editorElm.val() : this._currentValue;
+      const minLength = this.editorOptions && this.editorOptions.hasOwnProperty('minLength') ? this.editorOptions.minLength : MIN_LENGTH;
+      this._currentValue = this._$editorElm.val().length > minLength ? this._$editorElm.val() : this._currentValue;
     }
-    if (this.customStructure && this._currentValue.hasOwnProperty(this.labelName)) {
+    // if user provided a custom structure, we will serialize the value returned from the object with custom structure
+    if (this.customStructure && this._currentValue && this._currentValue.hasOwnProperty(this.labelName)) {
       return this._currentValue[this.labelName];
-    } else if (this._currentValue.label) {
+    } else if (this._currentValue && this._currentValue.label) {
       if (this.columnDef.type === FieldType.object) {
         return {
           [this.labelName]: this._currentValue.label,
@@ -165,15 +169,16 @@ export class AutoCompleteEditor implements Editor {
   applyValue(item: any, state: any) {
     let newValue = state;
     const fieldName = this.columnDef && this.columnDef.field;
+
     // if we have a collection defined, we will try to find the string within the collection and return it
     if (Array.isArray(this.editorCollection) && this.editorCollection.length > 0) {
       newValue = findOrDefault(this.editorCollection, (collectionItem: any) => {
         if (collectionItem && typeof state === 'object' && collectionItem.hasOwnProperty(this.labelName)) {
-          return collectionItem[this.labelName].toString() === state[this.labelName].toString();
+          return (collectionItem.hasOwnProperty(this.labelName) && collectionItem[this.labelName].toString()) === (state.hasOwnProperty(this.labelName) && state[this.labelName].toString());
         } else if (collectionItem && typeof state === 'string' && collectionItem.hasOwnProperty(this.labelName)) {
-          return collectionItem[this.labelName].toString() === state;
+          return (collectionItem.hasOwnProperty(this.labelName) && collectionItem[this.labelName].toString()) === state;
         }
-        return collectionItem.toString() === state;
+        return collectionItem && collectionItem.toString() === state;
       });
     }
 
@@ -183,7 +188,7 @@ export class AutoCompleteEditor implements Editor {
     item[fieldNameFromComplexObject || fieldName] = (validation && validation.valid) ? newValue : '';
   }
 
-  isValueChanged() {
+  isValueChanged(): boolean {
     const lastEvent = this._lastInputEvent && this._lastInputEvent.keyCode;
     if (this.columnEditor && this.columnEditor.alwaysSaveOnEnterKey && lastEvent === KeyCode.ENTER) {
       return true;
@@ -218,7 +223,9 @@ export class AutoCompleteEditor implements Editor {
   // private functions
   // ------------------
 
-  private onSelect(event: Event, ui: any) {
+  // this function should be PRIVATE but for unit tests purposes we'll make it public until a better solution is found
+  // a better solution would be to get the autocomplete DOM element to work with selection but I couldn't find how to do that in Jest
+  onSelect(event: Event, ui: any): boolean {
     if (ui && ui.item) {
       this._currentValue = ui && ui.item;
       const itemLabel = typeof ui.item === 'string' ? ui.item : ui.item.label;
@@ -282,8 +289,6 @@ export class AutoCompleteEditor implements Editor {
       });
     }
 
-    setTimeout(() => {
-      this._$editorElm.focus().select();
-    }, 50);
+    setTimeout(() => this.focus(), 50);
   }
 }
diff --git a/src/app/modules/angular-slickgrid/filters/__tests__/autoCompleteFilter.spec.ts b/src/app/modules/angular-slickgrid/filters/__tests__/autoCompleteFilter.spec.ts
index 4c85fb89a..ddaf91aae 100644
--- a/src/app/modules/angular-slickgrid/filters/__tests__/autoCompleteFilter.spec.ts
+++ b/src/app/modules/angular-slickgrid/filters/__tests__/autoCompleteFilter.spec.ts
@@ -446,4 +446,32 @@ describe('AutoCompleteFilter', () => {
     expect(filterCollection[1]).toEqual({ value: 'male', description: 'male' });
     expect(filterCollection[2]).toEqual({ value: 'other', description: 'other' });
   });
+
+  describe('onSelect method', () => {
+    it('should expect "setValue" and "autoCommitEdit" to have been called with a string when item provided is a string', () => {
+      const spyCallback = jest.spyOn(filterArguments, 'callback');
+      mockColumn.filter.collection = ['male', 'female'];
+
+      filter.init(filterArguments);
+      const spySetValue = jest.spyOn(filter, 'setValues');
+      const output = filter.onSelect(null, { item: 'female' });
+
+      expect(output).toBe(false);
+      expect(spySetValue).toHaveBeenCalledWith('female');
+      expect(spyCallback).toHaveBeenCalledWith(null, { columnDef: mockColumn, operator: 'EQ', searchTerms: ['female'], shouldTriggerQuery: true });
+    });
+
+    it('should expect "setValue" and "autoCommitEdit" to have been called with the string label when item provided is an object', () => {
+      const spyCallback = jest.spyOn(filterArguments, 'callback');
+      mockColumn.filter.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
+
+      filter.init(filterArguments);
+      const spySetValue = jest.spyOn(filter, 'setValues');
+      const output = filter.onSelect(null, { item: { value: 'f', label: 'Female' } });
+
+      expect(output).toBe(false);
+      expect(spySetValue).toHaveBeenCalledWith('Female');
+      expect(spyCallback).toHaveBeenCalledWith(null, { columnDef: mockColumn, operator: 'EQ', searchTerms: ['f'], shouldTriggerQuery: true });
+    });
+  });
 });
diff --git a/src/app/modules/angular-slickgrid/filters/autoCompleteFilter.ts b/src/app/modules/angular-slickgrid/filters/autoCompleteFilter.ts
index d15088463..a0c77d95a 100644
--- a/src/app/modules/angular-slickgrid/filters/autoCompleteFilter.ts
+++ b/src/app/modules/angular-slickgrid/filters/autoCompleteFilter.ts
@@ -138,7 +138,7 @@ export class AutoCompleteFilter implements Filter {
    */
   destroy() {
     if (this.$filterElm) {
-      this.$filterElm.off('keyup input change').remove();
+      this.$filterElm.off('keyup').remove();
     }
   }
 
@@ -264,7 +264,7 @@ export class AutoCompleteFilter implements Filter {
 
     // step 3, subscribe to the keyup event and run the callback when that happens
     // also add/remove "filled" class for styling purposes
-    this.$filterElm.on('keyup input change', (e: any) => {
+    this.$filterElm.on('keyup', (e: any) => {
       let value = e && e.target && e.target.value || '';
       const enableWhiteSpaceTrim = this.gridOptions.enableFilterTrimWhiteSpace || this.columnFilter.enableTrimWhiteSpace;
       if (typeof value === 'string' && enableWhiteSpaceTrim) {
@@ -354,15 +354,15 @@ export class AutoCompleteFilter implements Filter {
   // private functions
   // ------------------
 
-  private onSelect(event: Event, ui: any) {
+  // this function should be PRIVATE but for unit tests purposes we'll make it public until a better solution is found
+  // a better solution would be to get the autocomplete DOM element to work with selection but I couldn't find how to do that in Jest
+  onSelect(event: Event, ui: any): boolean {
     if (ui && ui.item) {
       const itemLabel = typeof ui.item === 'string' ? ui.item : ui.item.label;
       const itemValue = typeof ui.item === 'string' ? ui.item : ui.item.value;
-      this.$filterElm.val(itemLabel);
+      this.setValues(itemLabel);
+      itemValue === '' ? this.$filterElm.removeClass('filled') : this.$filterElm.addClass('filled');
       this.callback(event, { columnDef: this.columnDef, operator: this.operator, searchTerms: [itemValue], shouldTriggerQuery: this._shouldTriggerQuery });
-      // reset both flags for next use
-      this._clearFilterTriggered = false;
-      this._shouldTriggerQuery = true;
     }
     return false;
   }
diff --git a/src/app/modules/angular-slickgrid/models/editor.interface.ts b/src/app/modules/angular-slickgrid/models/editor.interface.ts
index 85f029043..059de973e 100644
--- a/src/app/modules/angular-slickgrid/models/editor.interface.ts
+++ b/src/app/modules/angular-slickgrid/models/editor.interface.ts
@@ -42,8 +42,8 @@ export interface Editor {
   focus: () => void;
 
   /**
-   * Deserialize the value(s) saved to "state" and apply them to the data item
-   * this method may get called after the editor itself has been destroyed
+   * Deserialize the value(s) saved to "state" and apply them to the data item.
+   * This method may get called after the editor itself has been destroyed,
    * treat it as an equivalent of a Java/C# "static" method - no instance variables should be accessed
    */
   applyValue: (item: any, state: any) => void;