Home Reference Source

src/components/select/Select.unit.js

/* eslint-disable max-statements */
import assert from 'power-assert';
import cloneDeep from 'lodash/cloneDeep';
import sinon from 'sinon';
import Harness from '../../../test/harness';
import SelectComponent from './Select';
import { expect } from 'chai';
import { Formio } from './../../Formio';
import _ from 'lodash';

import {
  comp1,
  comp2,
  multiSelect,
  multiSelectOptions,
  comp4,
  comp5,
  comp6,
  comp7,
  comp8,
  comp9,
  comp10,
  comp11,
  comp12,
  comp13,
  comp14,
  comp15,
  comp16,
  comp17,
  comp18,
  comp19,
} from './fixtures';

// eslint-disable-next-line max-statements
describe('Select Component', () => {
  it('should not stringify select option value', function(done) {
    Harness.testCreate(SelectComponent, comp6).then((component) => {
      component.setValue({ value:'a', label:'A' });
      setTimeout(()=> {
        assert.equal(component.choices._currentState.items[0].value.value, 'a');
        assert.equal(typeof component.choices._currentState.items[0].value , 'object');
        assert.equal(component.dataValue.value, 'a');
        assert.equal(typeof component.dataValue , 'object');
        done();
      }, 100);
    });
  });

  it('should return string value for different value types', function(done) {
    Harness.testCreate(SelectComponent, comp4).then((component) => {
      const stringValue = component.asString(true);
      const stringValue1 = component.asString(11);
      const stringValue2 = component.asString('test');
      const stringValue3 = component.asString(12);
      assert.equal(stringValue, '<span>true</span>');
      assert.equal(stringValue1, '<span>11</span>');
      assert.equal(stringValue2, '<span>test</span>');
      assert.equal(stringValue3, '<span>1.2</span>');
      done();
    });
  });

  it('should correctly determine storage type when dataType is auto', function(done) {
    Harness.testCreate(SelectComponent, comp4).then((component) => {
      const value = component.normalizeSingleValue('true');
      const value1 = component.normalizeSingleValue('11');
      const value2 = component.normalizeSingleValue('test');
      const value3 = component.normalizeSingleValue('11test11test');
      const value4 = component.normalizeSingleValue('test11');
      const value5 = component.normalizeSingleValue('0');
      const value6 = component.normalizeSingleValue('');
      assert.equal(typeof value, 'boolean');
      assert.equal(typeof value1, 'number');
      assert.equal(typeof value2, 'string');
      assert.equal(typeof value3, 'string');
      assert.equal(typeof value4, 'string');
      assert.equal(typeof value5, 'number');
      assert.equal(typeof value6, 'string');
      done();
    });
  });

  it('should not stringify default empty values', function(done) {
    Harness.testCreate(SelectComponent, comp4).then((component) => {
      const value = component.normalizeSingleValue({});
      const value1 = component.normalizeSingleValue([]);
      assert.equal(typeof value, 'object');
      assert.equal(typeof value1, 'object');
      done();
    });
  });

  it('should not change value letter case', function(done) {
    Harness.testCreate(SelectComponent, comp4).then((component) => {
      const value = component.normalizeSingleValue('data.textArea');
      const value1 = component.normalizeSingleValue('ECMAScript');
      const value2 = component.normalizeSingleValue('JS');
      assert.equal(value, 'data.textArea');
      assert.equal(value1, 'ECMAScript');
      assert.equal(value2, 'JS');
      done();
    });
  });

  it('should define boolean value', function(done) {
    Harness.testCreate(SelectComponent, comp4).then((component) => {
      const value = component.normalizeSingleValue('TRUE');
      const value1 = component.normalizeSingleValue('False');
      const value2 = component.normalizeSingleValue('true');
      assert.equal(value, true);
      assert.equal(value1, false);
      assert.equal(value2, true);
      done();
    });
  });

  it('1/2 should not display empty choice options if property value is not defined', function(done) {
    Harness.testCreate(SelectComponent, comp5).then((component) => {
      component.setItems([{
        'label': '111',
        'value': '111'
      }, {
        'label': '222',
        'value': '222'
      }, {
        'label': '333',
        'value': '333'
      }], false);
      assert.equal(component.selectOptions.length, 0);
      done();
    });
  });

  it('2/2 should display choice option if property value is set', function(done) {
    comp5.template = '<span>{{ item.label }}</span>';
    Harness.testCreate(SelectComponent, comp5).then((component) => {
      component.setItems([{
        'label': '111',
        'value': '111'
      }, {
        'label': '222',
        'value': '222'
      }, {
        'label': '333',
        'value': '333'
      }], false);
      assert.equal(component.selectOptions.length, 3);
      done();
    });
  });

  it('should have only unique dropdown options', function(done) {
    comp5.template = '<span>{{ item.label }}</span>';
    comp5.uniqueOptions = true;
    Harness.testCreate(SelectComponent, comp5).then((component) => {
      component.setItems([{
        'label': 'Label 1',
        'value': 'value1'
      }, {
        'label': 'Label 2',
        'value': 'value2'
      }, {
        'label': 'Label 3',
        'value': 'value3'
      }, {
        'label': 'Label 4',
        'value': 'value3'
      }], false);

      assert.equal(component.selectOptions.length, 3);
      done();
    });
  });

  it('should format unlisted values', function(done) {
    comp5.template = '<span>{{ item.label }}</span>';
    Harness.testCreate(SelectComponent, comp5).then((component) => {
      const formattedValue1 = component.getView('Unlisted value');
      const formattedValue2 = component.getView(0);

      assert.equal(formattedValue1, '<span>Unlisted value</span>');
      assert.equal(formattedValue2, '<span>0</span>');
      done();
    });
  });

  it('should set multiple selected values not repeating them', function(done) {
    Harness.testCreate(SelectComponent, multiSelect).then((component) => {
      component.setItems(multiSelectOptions, false);
      component.setChoicesValue(['Cheers']);
      component.setChoicesValue(['Cheers', 'Cyberdyne Systems'], 1);
      component.setChoicesValue(['Cheers', 'Cyberdyne Systems', 'Massive Dynamic'], 2);
      const choices = component.element.querySelector('.choices__list--multiple').children;
      assert.equal(choices.length, 3);
      done();
    });
  });

  it('should not show selected values in dropdown when searching', function(done) {
    Harness.testCreate(SelectComponent, multiSelect).then((component) => {
      component.setItems(multiSelectOptions, false);
      component.setChoicesValue(['Cheers']);
      component.setChoicesValue(['Cheers', 'Cyberdyne Systems'], 1);
      component.setItems([], true);
      const itemsInDropdown = component.element.querySelectorAll('.choices__item--choice');
      const choices = component.element.querySelector('.choices__list--multiple').children;
      assert.equal(choices.length, 2);
      assert.equal(itemsInDropdown.length, 1);
      done();
    });
  });

  it('Should build a Select component', () => {
    return Harness.testCreate(SelectComponent, comp1).then((component) => {
      Harness.testElements(component, 'select', 1);
    });
  });

  it('Should preserve the tabindex', () => {
    return Harness.testCreate(SelectComponent, comp2).then((component) => {
      const element = component.element.getElementsByClassName('choices__list choices__list--single')[0];
      Harness.testElementAttribute(element, 'tabindex', '10');
    });
  });

  it('Should default to 0 when tabindex is not specified', () => {
    return Harness.testCreate(SelectComponent, comp1).then((component) => {
      const element = component.element.getElementsByClassName('choices__list choices__list--single')[0];
      Harness.testElementAttribute(element, 'tabindex', '0');
    });
  });

  it('Should allow to override threshold option of fuzzy search', () => {
    try {
      const c1 = Object.assign(cloneDeep(comp1), { selectThreshold: 0.2 });
      const c2 = Object.assign(cloneDeep(comp1), { selectThreshold: 0.4 });
      const c3 = Object.assign(cloneDeep(comp1), { selectThreshold: 0.8 });
      const comps = [
        Harness.testCreate(SelectComponent, c1),
        Harness.testCreate(SelectComponent, c2),
        Harness.testCreate(SelectComponent, c3),
      ];

      return Promise
        .all(comps)
        .then(([a, b, c]) => {
          expect(a.choices.config.fuseOptions.threshold).to.equal(0.2);
          expect(b.choices.config.fuseOptions.threshold).to.equal(0.4);
          expect(c.choices.config.fuseOptions.threshold).to.equal(0.8);
        });
    }
    catch (error) {
      return Promise.reject(error);
    }
  });

  it('should set component value', () => {
    return Harness.testCreate(SelectComponent, comp1).then((component) => {
      assert.deepEqual(component.dataValue, '');
      component.setValue('red');
      assert.equal(component.dataValue, 'red');
    });
  });

  it('should remove selected item', () => {
    return Harness.testCreate(SelectComponent, comp1).then((component) => {
      assert.deepEqual(component.dataValue, '');
      component.setValue('red');
      assert.equal(component.dataValue, 'red');

      const element = component.element.getElementsByClassName('choices__button')[0];
      component.choices._handleButtonAction(component.choices._store.activeItems, element);

      assert.equal(component.dataValue, '');
    });
  });

  it('should open dropdown after item has been removed', () => {
    global.requestAnimationFrame = cb => cb();
    window.matchMedia = window.matchMedia || function() {
      return {
        matches : false,
        addListener : function() {},
        removeListener: function() {}
      };
    };

    return Harness.testCreate(SelectComponent, comp1).then((component) => {
      component.setValue('red');

      const element = component.element.getElementsByClassName('choices__button')[0];
      component.choices._handleButtonAction(component.choices._store.activeItems, element);

      component.choices.showDropdown(true);

      assert.equal(component.choices.dropdown.isActive, true);
    });
  });

  it('should keep dropdown closed after item has been removed by keypress', () => {
    return Harness.testCreate(SelectComponent, comp1).then((component) => {
      component.setValue('red');

      const element = component.element.querySelector('.choices__button');
      const ke = new KeyboardEvent('keydown', {
        bubbles: true, cancelable: true, keyCode: 13
      });

      element.dispatchEvent(ke);

      assert.equal(component.dataValue, '');
      assert.equal(component.choices.dropdown.isActive, false);
    });
  });

  it('Should render and set values in selects with different widget types', (done) => {
    const form = _.cloneDeep(comp7);
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const selectHTML = form.getComponent('selectHtml');
      const selectChoices = form.getComponent('selectChoices');
      assert.equal(!!selectHTML.choices, false);
      assert.equal(!!selectChoices.choices, true);

      setTimeout(() => {
        assert.equal(selectChoices.element.querySelectorAll('.choices__item--choice').length, 3);
        const value = 'b';
        selectHTML.setValue(value);
        selectChoices.setValue(value);

        setTimeout(() => {
          assert.equal(selectHTML.dataValue, value);
          assert.equal(selectChoices.dataValue, value);
          assert.equal(selectHTML.getValue(), value);
          assert.equal(selectChoices.getValue(), value);

          done();
        }, 200);
      }, 200);
    }).catch(done);
  });

  it('Should clear select value when "clear value on refresh options" and "refresh options on" is enable and number component is changed   ', (done) => {
    const form = _.cloneDeep(comp8);
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const numberComp = form.getComponent('number');
      const value = 'b';
      select.setValue(value);

      setTimeout(() => {
        assert.equal(select.dataValue, value);
        assert.equal(select.getValue(), value);
        const numberInput = numberComp.refs.input[0];
        const numberValue = 5;
        const inputEvent = new Event('input');
        numberInput.value = numberValue;
        numberInput.dispatchEvent(inputEvent);

        setTimeout(() => {
          assert.equal(numberComp.dataValue, numberValue);
          assert.equal(numberComp.getValue(), numberValue);
          assert.equal(select.dataValue, '');
          assert.equal(select.getValue(), '');

          done();
        }, 400);
      }, 200);
    }).catch(done);
  });

  it('Should update select items when "refresh options on" is enable and number component is changed', (done) => {
    const form = _.cloneDeep(comp9);
    const element = document.createElement('div');
    const originalMakeRequest = Formio.makeRequest;
    Formio.makeRequest = function(formio, type, url) {
      return new Promise(resolve => {
        let values =[{ name: 'Ivan' }, { name: 'Mike' }];

        if (url.endsWith('5')) {
          values = [{ name: 'Kate' }, { name: 'Ann' }, { name: 'Lana' }];
        }
         resolve(values);
      });
    };

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const numberComp = form.getComponent('number');
      setTimeout(() => {
        assert.equal(select.selectOptions.length, 2);
        assert.deepEqual(select.selectOptions[0].value, { name: 'Ivan' });

        const numberValue = 5;
        const inputEvent = new Event('input');
        const numberInput = numberComp.refs.input[0];

        numberInput.value = numberValue;
        numberInput.dispatchEvent(inputEvent);

        setTimeout(() => {
          assert.equal(numberComp.dataValue, numberValue);
          assert.equal(numberComp.getValue(), numberValue);
          assert.equal(select.selectOptions.length, 3);
          assert.deepEqual(select.selectOptions[0].value, { name: 'Kate' });

          Formio.makeRequest = originalMakeRequest;
          done();
        }, 500);
      }, 200);
    }).catch(done);
  });

  it('Should update select items when "refresh options on blur" is enable and number component is changed', (done) => {
    const form = _.cloneDeep(comp9);
    form.components[1].refreshOn = null;
    form.components[1].refreshOnBlur = 'number';

    const element = document.createElement('div');
    const originalMakeRequest = Formio.makeRequest;
    Formio.makeRequest = function(formio, type, url) {
      return new Promise(resolve => {
        let values =[{ name: 'Ivan' }, { name: 'Mike' }];

        if (url.endsWith('5')) {
          values = [{ name: 'Kate' }, { name: 'Ann' }, { name: 'Lana' }];
        }
         resolve(values);
      });
    };

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const numberComp = form.getComponent('number');
      setTimeout(() => {
        assert.equal(select.selectOptions.length, 2);
        assert.deepEqual(select.selectOptions[0].value, { name: 'Ivan' });

        const numberValue = 5;
        const inputEvent = new Event('input');
        const focusEvent = new Event('focus');
        const blurEvent = new Event('blur');
        const numberInput = numberComp.refs.input[0];
        numberInput.dispatchEvent(focusEvent);
        numberInput.value = numberValue;
        numberInput.dispatchEvent(inputEvent);
        numberInput.dispatchEvent(blurEvent);

        setTimeout(() => {
          assert.equal(numberComp.dataValue, numberValue);
          assert.equal(numberComp.getValue(), numberValue);
          assert.equal(select.selectOptions.length, 3);
          assert.deepEqual(select.selectOptions[0].value, { name: 'Kate' });

          Formio.makeRequest = originalMakeRequest;
          done();
        }, 500);
      }, 200);
    }).catch(done);
  });

  it('Should be able to search if static search is enable', (done) => {
    const form = _.cloneDeep(comp10);
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');

      const searchField = select.element.querySelector('.choices__input.choices__input--cloned');
      const focusEvent = new Event('focus');
      searchField.dispatchEvent(focusEvent);

      setTimeout(() => {
        assert.equal(select.choices.dropdown.isActive, true);
        const items = select.choices.choiceList.element.children;
        assert.equal(items.length, 5);

        const keyupEvent = new Event('keyup');
        const searchField = select.element.querySelector('.choices__input.choices__input--cloned');
        searchField.value = 'par';
        searchField.dispatchEvent(keyupEvent);

        setTimeout(() => {
          const items = select.choices.choiceList.element.children;
          assert.equal(items.length, 1);

          done();
        }, 400);
      }, 200);
    }).catch(done);
  });

  it('Should not be able to search if static search is disable', (done) => {
    const form = _.cloneDeep(comp10);
    form.components[0].searchEnabled = false;
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const searchField = select.element.querySelector('.choices__input.choices__input--cloned');
      assert.equal(searchField, null);

      done();
    }).catch(done);
  });

  it('Should save correct value if value property and item template property are different', (done) => {
    const form = _.cloneDeep(comp9);
    form.components[1].refreshOn = null;
    form.components[1].valueProperty = 'age';
    form.components[1].lazyLoad = true;

    const element = document.createElement('div');
    const originalMakeRequest = Formio.makeRequest;

    Formio.makeRequest = function() {
      return new Promise(resolve => {
        const values =[{ name: 'Ivan', age: 35 }, { name: 'Mike', age: 41 }];
        resolve(values);
      });
    };

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      assert.equal(select.selectOptions.length, 0);
      select.choices.showDropdown();

      setTimeout(() => {
        assert.equal(select.selectOptions.length, 2);
        assert.deepEqual(select.selectOptions[0].value, 35);
        assert.deepEqual(select.selectOptions[0].label, '<span>Ivan</span>');

        const items = select.choices.choiceList.element.children;
        assert.equal(items.length, 2);
        assert.equal(items[0].textContent.trim(), 'Ivan');

        select.setValue(41);

        setTimeout(() => {
          assert.equal(select.getValue(), 41);
          assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'Mike');

          Formio.makeRequest = originalMakeRequest;

          done();
        }, 400);
      }, 200);
    }).catch(done);
  });

  it('Should set custom header when sending request in select url', (done) => {
    const form = _.cloneDeep(comp9);
    form.components[1].refreshOn = null;
    form.components[1].lazyLoad = true;
    form.components[1].data.headers = [{ key:'testHeader', value:'test' }];

    const element = document.createElement('div');
    const originalMakeRequest = Formio.makeRequest;

    Formio.makeRequest = function(formio, type, url, method, data, opts) {
      assert.equal(opts.header.get('testHeader'), 'test');
      return new Promise(resolve => {
        const values = [{ name: 'Ivan', age: 35 }, { name: 'Mike', age: 41 }];
        resolve(values);
      });
    };

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      assert.equal(select.selectOptions.length, 0);
      select.choices.showDropdown();

      setTimeout(() => {
        Formio.makeRequest = originalMakeRequest;
        done();
      }, 200);
    }).catch(done);
  });

  it('Should set value in select url with lazy load option', (done) => {
    const form = _.cloneDeep(comp9);
    form.components[1].refreshOn = null;
    form.components[1].lazyLoad = true;

    const element = document.createElement('div');
    const originalMakeRequest = Formio.makeRequest;

    Formio.makeRequest = function() {
      return new Promise(resolve => {
        const values = [{ name: 'Ivan' }, { name: 'Mike' }];
        resolve(values);
      });
    };

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      select.setValue({ name: 'Ivan' });
      setTimeout(() => {
        assert.deepEqual(select.getValue(), { name: 'Ivan' });
        assert.deepEqual(select.dataValue, { name: 'Ivan' });
        assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'Ivan');

        Formio.makeRequest = originalMakeRequest;

        done();
      }, 200);
    }).catch(done);
  });

  it('Should set value in select url with lazy load option when value property is defined', (done) => {
    const form = _.cloneDeep(comp9);
    form.components[1].refreshOn = null;
    form.components[1].lazyLoad = true;
    form.components[1].valueProperty = 'name';
    const element = document.createElement('div');
    const originalMakeRequest = Formio.makeRequest;

    Formio.makeRequest = function() {
      return new Promise(resolve => {
        const values = [{ name: 'Ivan' }, { name: 'Mike' }];
        resolve(values);
      });
    };

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      select.setValue('Ivan');
      setTimeout(() => {
        assert.equal(select.getValue(), 'Ivan');
        assert.equal(select.dataValue, 'Ivan');
        assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'Ivan');

        Formio.makeRequest = originalMakeRequest;

        done();
      }, 200);
    }).catch(done);
  });

  it('Should be able to search if static search is enable', (done) => {
    const form = _.cloneDeep(comp10);
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');

      const searchField = select.element.querySelector('.choices__input.choices__input--cloned');
      const focusEvent = new Event('focus');
      searchField.dispatchEvent(focusEvent);

      setTimeout(() => {
        assert.equal(select.choices.dropdown.isActive, true);
        const items = select.choices.choiceList.element.children;
        assert.equal(items.length, 5);

        const keyupEvent = new Event('keyup');
        const searchField = select.element.querySelector('.choices__input.choices__input--cloned');
        searchField.value = 'par';
        searchField.dispatchEvent(keyupEvent);

        setTimeout(() => {
          const items = select.choices.choiceList.element.children;
          assert.equal(items.length, 1);

          done();
        }, 400);
      }, 200);
    }).catch(done);
  });

  it('Server side search is debounced with the correct timeout', (done) => {
    const form = _.cloneDeep(comp9);
    form.components[1].lazyLoad = false;
    form.components[1].searchDebounce = 0.7;
    form.components[1].disableLimit = false;
    form.components[1].searchField = 'name';
    const element = document.createElement('div');

    const originalMakeRequest = Formio.makeRequest;
    Formio.makeRequest = function() {
      return new Promise(resolve => {
        resolve([]);
      });
    };

    var searchHasBeenDebounced = false;
    var originalDebounce = _.debounce;
    _.debounce = (fn, timeout, opts) => {
      searchHasBeenDebounced = timeout === 700;
      return originalDebounce(fn, 0, opts);
    };

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const searchField = select.element.querySelector('.choices__input.choices__input--cloned');
      const focusEvent = new Event('focus');
      searchField.dispatchEvent(focusEvent);

      setTimeout(() => {
        const keyupEvent = new Event('keyup');
        searchField.value = 'the_name';
        searchField.dispatchEvent(keyupEvent);

        setTimeout(() => {
          _.debounce = originalDebounce;
          Formio.makeRequest = originalMakeRequest;

          assert.equal(searchHasBeenDebounced, true);
          done();
        }, 50);
      }, 200);
    }).catch(done);
  });

  it('Should provide "Allow only available values" validation', (done) => {
    const form = _.cloneDeep(comp10);
    form.components[0].validate.onlyAvailableItems = true;
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const value = 'Dallas';
      select.setValue(value);

      setTimeout(() => {
        assert.equal(select.getValue(), value);
        assert.equal(select.dataValue, value);
        const submit = form.getComponent('submit');
        const clickEvent = new Event('click');
        const submitBtn = submit.refs.button;
        submitBtn.dispatchEvent(clickEvent);

        setTimeout(() => {
          assert.equal(form.errors.length, 1);
          assert.equal(select.error.message, 'Select is an invalid value.');
          document.innerHTML = '';
          done();
        }, 400);
      }, 200);
    }).catch(done);
  });

  it('Should render and set value in select json', (done) => {
    const formObj = _.cloneDeep(comp11);
    const element = document.createElement('div');

    Formio.createForm(element, formObj).then(form => {
      const select = form.getComponent('select');
      assert.equal(select.choices.containerInner.element.children[1].children[0].dataset.value, '');
      select.choices.showDropdown();

      setTimeout(() => {
        const items = select.choices.choiceList.element.children;
        assert.equal(items.length, 4);

        const value = { value: 'a', label:'A' };
        select.setValue(value);

        setTimeout(() => {
          assert.deepEqual(select.getValue(), value);
          assert.deepEqual(select.dataValue, value);
          assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'A');

          done();
        }, 400);
      }, 200);
    }).catch(done);
  });

  it('Should load and set items in select resource and set value', (done) => {
    const form = _.cloneDeep(comp12);
    const element = document.createElement('div');
    const originalMakeRequest = Formio.makeRequest;

    Formio.makeRequest = function(formio, type, url) {
      return new Promise(resolve => {
        let values = [{ data: { name: 'Ivan' } }, { data: { name: 'Mike' } }];

        if (url.endsWith('Ivan')) {
          assert.equal(url.endsWith('/form/60114dd32cab36ad94ac4f94/submission?limit=100&skip=0&data.name__regex=Ivan'), true);
          values = [{ data: { name: 'Ivan' } }];
        }
        else {
          assert.equal(url.endsWith('/form/60114dd32cab36ad94ac4f94/submission?limit=100&skip=0'), true);
        }

        resolve(values);
      });
    };

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const items = select.choices.choiceList.element.children;
      assert.equal(items.length, 1);
      select.setValue('Ivan');

      setTimeout(() => {
        assert.equal(select.getValue(), 'Ivan');
        assert.equal(select.dataValue, 'Ivan');
        assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'Ivan');
        select.choices.showDropdown();

        setTimeout(() => {
          const items = select.choices.choiceList.element.children;

          assert.equal(items.length, 2);
          assert.equal(items[0].textContent, 'Ivan');

          Formio.makeRequest = originalMakeRequest;
          done();
        }, 400);
      }, 200);
    }).catch(done);
  });

  it('Should not have "limit" and "skip" query params when "Disable limit" option checked', (done) => {
    const form = _.cloneDeep(comp9);
    const element = document.createElement('div');
    const originalMakeRequest = Formio.makeRequest;
    Formio.makeRequest = (_, __, url) => {
      assert.equal(url, 'https://test.com/');
      return Promise.resolve({});
    };

    Formio.createForm(element, form).then(() => {
      setTimeout(() => {
        Formio.makeRequest = originalMakeRequest;
        done();
      }, 200);
    }).catch(done);
  });

  it('The empty option in html5 shouldn\'t have the [Object Object] value', () => {
    return Harness.testCreate(SelectComponent, comp13).then((component) => {
     const emptyOption = component.element.querySelectorAll('option')[0];
      assert.notEqual(emptyOption.value, '[object Object]');
      assert.equal(emptyOption.value, '');
    });
  });

  it('Should not have default values in schema', (done) => {
    const form = _.cloneDeep(comp14);
    const element = document.createElement('div');

    const requiredSchema = {
      label: 'Select',
      tableView: true,
      key: 'select',
      type: 'select',
      input: true
    };

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      assert.deepEqual(requiredSchema, select.schema);
      done();
    }).catch(done);
  });

  it('Should show async custom values and be able to set submission', (done) => {
    const formObj = _.cloneDeep(comp16);
    const element = document.createElement('div');

    Formio.createForm(element, formObj).then(form => {
      const select = form.getComponent('select');
     select.choices.showDropdown();

      setTimeout(() => {
        const items = select.choices.choiceList.element.children;
        assert.equal(items.length, 3);
        const value = 'bb';
        form.submission = { data: { select: value } };

        setTimeout(() => {
          assert.deepEqual(select.getValue(), value);
          assert.deepEqual(select.dataValue, value);
          assert.equal(select.choices.containerInner.element.children[1].children[0].children[0].textContent, 'B');

          done();
        }, 400);
      }, 200);
    }).catch(done);
  });

  it('Should provide metadata.selectData for Select component pointed to a resource where value property is set to a field', (done) => {
    const form = _.cloneDeep(comp17);
    const testItems = [
      { textField: 'John' },
      { textField: 'Mary' },
      { textField: 'Sally' }
    ];
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      select.setItems(testItems.map(item => ({ data: item })));
      const value = 'John';
      select.setValue(value);

      setTimeout(() => {
        assert.equal(select.dataValue, value);
        const submit = form.getComponent('submit');
        const clickEvent = new Event('click');
        const submitBtn = submit.refs.button;
        submitBtn.dispatchEvent(clickEvent);

        setTimeout(() => {
          assert.equal(_.isEqual(form.submission.metadata.selectData.select.data, testItems[0]), true);
          done();
        }, 200);
      }, 200);
    }).catch(done);
  });

  it('OnBlur validation should work properly with Select component', function(done) {
    this.timeout(0);
    const element = document.createElement('div');

    Formio.createForm(element, comp19).then(form => {
      const select = form.components[0];
      select.setValue('banana');
      select.focusableElement.focus();
      select.pristine = false;

      setTimeout(() => {
        assert(!select.error, 'Select should be valid while changing');
        select.focusableElement.dispatchEvent(new Event('blur'));

        setTimeout(() => {
          assert(select.error, 'Should set error after Select component was blurred');
          done();
        }, 500);
      }, 200);
    }).catch(done);
  });

  it('Should escape special characters in regex search field', done => {
    const form = _.cloneDeep(comp17);
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const searchField = select.element.querySelector('.choices__input.choices__input--cloned');
      const focusEvent = new Event('focus');
      searchField.dispatchEvent(focusEvent);

      setTimeout(() => {
        const keyupEvent = new Event('keyup');
        searchField.value = '^$.*+?()[]{}|';
        searchField.dispatchEvent(keyupEvent);

        const spy = sinon.spy(Formio, 'makeRequest');

        setTimeout(() => {
          assert.equal(spy.callCount, 1);

          const urlArg = spy.args[0][2];

          assert.ok(urlArg && typeof urlArg === 'string' && urlArg.startsWith('http'), 'A URL should be passed as the third argument to "Formio.makeRequest()"');

          assert.ok(urlArg.includes('__regex=%5C%5E%5C%24%5C.%5C*%5C%2B%5C%3F%5C(%5C)%5C%5B%5C%5D%5C%7B%5C%7D%5C%7C'), 'The URL should contain escaped and encoded search value regex');
          done();
        }, 500);
      }, 200);
    }).catch(done);
  });

  // it('should reset input value when called with empty value', () => {
  //   const comp = Object.assign({}, comp1);
  //   delete comp.placeholder;
  //
  //   return Harness.testCreate(SelectComponent, comp).then((component) => {
  //     assert.deepEqual(component.dataValue, '');
  //     assert.equal(component.refs.input[0].value, '');
  //     component.setValue('red');
  //     assert.equal(component.dataValue, 'red');
  //     assert.equal(component.refs.input[0].value, 'red');
  //     component.setValue('');
  //     assert.equal(component.dataValue, '');
  //     assert.equal(component.refs.input[0].value, '');
  //   });
  // });
});

describe('Select Component', () => {
  it('Select Component should work correctly with the values in the form of an array', (done) => {
    const form = _.cloneDeep(comp18);
    const testItems = [
      { textField: ['one','two'] },
      { textField: ['three','four'] },
      { textField: ['five','six'] },
    ];
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      select.setItems(testItems.map(item => ({ data: item })));
      const value = ['three','four'];
      select.setValue(value);
      assert.equal(select.selectOptions.length, 3);
      setTimeout(() => {
        assert.deepEqual(select.getValue(), value);
        assert.deepEqual(select.dataValue, value);
        const submit = form.getComponent('submit');
        const clickEvent = new Event('click');
        const submitBtn = submit.refs.button;
        submitBtn.dispatchEvent(clickEvent);

        setTimeout(() => {
          assert.equal(select.dataValue, value);
          done();
        }, 200);
      }, 200);
    }).catch(done);
  });
});

describe('Select Component with Entire Object Value Property', () => {
  it('Should provide correct value', (done) => {
    const form = _.cloneDeep(comp15);
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const value = { 'textField':'rgd','submit':true,'number':11 };
      select.setValue(value);

      setTimeout(() => {
        assert.equal(select.getValue(), value);
        assert.equal(select.dataValue, value);
        const submit = form.getComponent('submit');
        const clickEvent = new Event('click');
        const submitBtn = submit.refs.button;
        submitBtn.dispatchEvent(clickEvent);

        setTimeout(() => {
          assert.equal(select.dataValue, value);
          done();
        }, 200);
      }, 200);
    }).catch(done);
  });

  it('Should provide correct items for Resource DataSrc Type and Entire Object Value Property', (done) => {
    const form = _.cloneDeep(comp15);
    const testItems = [
      { textField: 'Jone', number: 1 },
      { textField: 'Mary', number: 2 },
      { textField: 'Sally', number: 3 }
    ];
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      select.setItems(testItems.map(item => ({ data: item })));
      const value = { textField: 'Jone', number: 1 };
      select.setValue(value);
      assert.equal(select.selectOptions.length, 3);

      setTimeout(() => {
        assert.equal(select.dataValue, value);
        const submit = form.getComponent('submit');
        const clickEvent = new Event('click');
        const submitBtn = submit.refs.button;
        submitBtn.dispatchEvent(clickEvent);

        setTimeout(() => {
          assert.equal(typeof select.dataValue, 'object');
          done();
        }, 200);
      }, 200);
    }).catch(done);
  });

  it('Should provide correct html value for Resource DataSrc Type and Entire Object Value Property', (done) => {
    const form = _.cloneDeep(comp15);
    const testItems = [
      { textField: 'Jone', number: 1 },
      { textField: 'Mary', number: 2 },
      { textField: 'Sally', number: 3 }
    ];
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      select.setItems(testItems.map(item => ({ data: item })));
      const selectContainer = element.querySelector('[ref="selectContainer"]');
      assert.notEqual(selectContainer, null);
      const options = selectContainer.childNodes;
      assert.equal(options.length, 4);
      options.forEach((option) => {
        assert.notEqual(option.value, '[object Object]');
      });
      const value = { textField: 'Jone', number: 1 };
      select.setValue(value);
      assert.equal(select.selectOptions.length, 3);

      setTimeout(() => {
        assert.equal(select.dataValue, value);
        const submit = form.getComponent('submit');
        const clickEvent = new Event('click');
        const submitBtn = submit.refs.button;
        submitBtn.dispatchEvent(clickEvent);

        setTimeout(() => {
          assert.equal(typeof select.dataValue, 'object');
          done();
        }, 200);
      }, 200);
    }).catch(done);
  });

  it('Should set submission value for Resource DataSrc Type and Entire Object Value Property', (done) => {
    const form = _.cloneDeep(comp15);
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const select = form.getComponent('select');
      const value = { textField: 'Jone', nubmer: 1 };
      form.submission = {
        data: {
          select: value
        }
      };

      setTimeout(() => {
        assert.equal(typeof select.dataValue,  'object');
        const selectContainer = element.querySelector('[ref="selectContainer"]');
        assert.notEqual(selectContainer, null);
        assert.notEqual(selectContainer.value, '');
        const options = selectContainer.childNodes;
        assert.equal(options.length, 2);
        done();
      }, 1000);
    }).catch(done);
  });
});