Home Reference Source

src/components/textfield/TextField.unit.js

import assert from 'power-assert';
import _ from 'lodash';
import Harness from '../../../test/harness';
import TextFieldComponent from './TextField';
import { Formio } from './../../Formio';
import 'flatpickr';

import {
  comp1,
  comp2,
  comp4,
  comp5,
  comp6,
  withDisplayAndInputMasks,
  comp7,
} from './fixtures';

describe('TextField Component', () => {
  it('Should create a new TextField', () => {
    const textField = new TextFieldComponent({
      label: 'First Name',
      key: 'firstName',
      input: true,
      type: 'textfield'
    });

    assert.equal(textField.component.key, 'firstName');
  });

  it('Should build a TextField component', () => {
    return Harness.testCreate(TextFieldComponent, comp1).then((component) => {
      Harness.testElements(component, 'input[type="text"]', 1);
    });
  });

  it('Should disable multiple mask selector if component is disabled', (done) => {
    Harness.testCreate(TextFieldComponent, comp4).then((component) => {
      Harness.testElements(component, '[disabled]', 2);
      done();
    });
  });

  it('Should provide required validation', () => {
    return Harness.testCreate(TextFieldComponent, _.merge({}, comp2, {
      validate: { required: true }
    })).then((component) => {
      return Harness.testInvalid(component, '', 'firstName', 'First Name is required').then(() => component);
    }).then((component) => {
      return Harness.testValid(component, 'te').then(() => component);
    });
  });

  it('Should provide minWords validation', () => {
    return Harness.testCreate(TextFieldComponent, _.merge({}, comp2, {
      validate: { minWords: 2 }
    })).then((component) => {
      return Harness.testInvalid(component, 'test', 'firstName', 'First Name must have at least 2 words.').then(() => component);
    }).then((component) => {
      return Harness.testValid(component, 'te st').then(() => component);
    });
  });

  it('Should correctly calculate remaining words', (done) => {
    Harness.testCreate(TextFieldComponent, comp5).then((component) => {
      const inputEvent = new Event('input', { bubbles: true, cancelable: true });
      const element = component.refs.input[0];

      element.value = 'paper format A4';
      element.dispatchEvent(inputEvent);

      setTimeout(()=>{
        assert.equal(component.refs.wordcount[0].textContent, '2 words remaining.');

        element.value = 'Hey, guys! We are here!!';
        element.dispatchEvent(inputEvent);

        setTimeout(()=>{
          assert.equal(component.refs.wordcount[0].textContent, '0 words remaining.');

          element.value = ' Some   test   text  111 ';
          element.dispatchEvent(inputEvent);

          setTimeout(()=>{
            assert.equal(component.refs.wordcount[0].textContent, '1 words remaining.');

            done();
          }, 300);
        }, 275);
      }, 250);
    });
  });

  it('Should provide maxWords validation', () => {
    return Harness.testCreate(TextFieldComponent, _.merge({}, comp2, {
      validate: { maxWords: 2 }
    })).then((component) => {
      return Harness.testInvalid(component, 'test test test', 'firstName', 'First Name must have no more than 2 words.').then(() => component);
    }).then((component) => {
      return Harness.testValid(component, 'te st').then(() => component);
    });
  });

  it('Should provide minLength validation', () => {
    return Harness.testCreate(TextFieldComponent, _.merge({}, comp2, {
      validate: { minLength: 2 }
    })).then((component) => {
      return Harness.testInvalid(component, 't', 'firstName', 'First Name must have at least 2 characters.').then(() => component);
    }).then((component) => {
      return Harness.testValid(component, 'te').then(() => component);
    });
  });

  it('Should provide maxLength validation', () => {
    return Harness.testCreate(TextFieldComponent, _.merge({}, comp2, {
      validate: { maxLength: 5 }
    })).then(component => {
      return Harness.testInvalid(component, 'testte', 'firstName', 'First Name must have no more than 5 characters.').then(() => component);
    }).then((component) => {
      return Harness.testValid(component, 'te').then(() => component);
    });
  });

  it('Should provide custom validation', () => {
    return Harness.testCreate(TextFieldComponent, _.merge({}, comp2, {
      validate: {
        custom: 'valid = (input !== "Joe") ? true : "You cannot be Joe"'
      }
    })).then((component) => {
      return Harness.testInvalid(component, 'Joe', 'firstName', 'You cannot be Joe').then(() => component);
    }).then((component) => {
      return Harness.testValid(component, 'Tom').then(() => component);
    });
  });

  it('Should provide one custom error message', (done) => {
    const formJson =  {
      components: [{
          label: 'Text Field',
          tableView: true,
          validate: {
            pattern: '^[0-9]*$]',
            customMessage: 'Custom Error Message',
            minWords: 10
          },
          key: 'textField',
          type: 'textfield',
          input: true
       }]
    };
    const element = document.createElement('div');
    Formio.createForm(element, formJson)
      .then(form => {
        form.submission = {
          data: {
            textField: 'textField'
          }
        };
        const textField = form.getComponent('textField');
        setTimeout(() => {
          assert.equal(textField.refs.messageContainer.children.length, 1);
          assert.equal(textField.refs.messageContainer.children[0].innerHTML, 'Custom Error Message');
          done();
        }, 300);
      })
      .catch(done);
  });

  it('Should provide json validation', () => {
    return Harness.testCreate(TextFieldComponent, _.merge({}, comp2, {
      validate: {
        json: {
          'if': [
            {
              '===': [
                { var: 'data.firstName' },
                'Joe'
              ]
            },
            true,
            'You must be Joe'
          ]
        }
      }
    })).then((component) => {
      return Harness.testInvalid(component, 'Tom', 'firstName', 'You must be Joe').then(() => component);
    }).then((component) => {
      return Harness.testValid(component, 'Joe').then(() => component);
    });
  });

  it('Should provide number input mask only after blur event if applyMaskOn setting on blur', (done) => {
    const form = _.cloneDeep(comp7);
    const element = document.createElement('div');
    form.components[0].inputMask = '99-99';
    const value = 999;

    Formio.createForm(element, form).then(form => {
      const component = form.getComponent('textField');
      const changed = component.setValue(value);

      if (value) {
        assert.equal(changed, true, 'Should set value');
        assert.equal(component.getValue(), value);
      }

      setTimeout(() => {
        const textFieldInput = component.element.querySelector('.form-control');
        const event = new Event('blur');
        textFieldInput.dispatchEvent(event);

        setTimeout(() => {
          assert.equal(component.getValue(), '99-9_');
          done();
        }, 200);
      }, 200);
    }).catch(done);
  });

  it('Should provide validation of number input mask only after blur event if applyMaskOn setting on blur', (done) => {
    const form = _.cloneDeep(comp7);
    const element = document.createElement('div');
    form.components[0].inputMask = '99-99';
    let value = 999;

    Formio.createForm(element, form).then(form => {
      const component = form.getComponent('textField');
      let changed = component.setValue(value);
      const error = 'Text Field does not match the mask.';

      if (value) {
        assert.equal(changed, true, 'Should set value');
      }

      setTimeout(() => {
        assert.equal(!!component.error, false, 'Should not contain error');

        const textFieldInput = component.element.querySelector('.form-control');
        const event = new Event('blur');
        textFieldInput.dispatchEvent(event);

        setTimeout(() => {
          assert.equal(!!component.error, true, 'Should contain error');
          assert.equal(component.error.message, error, 'Should contain error message');
          assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class');
          assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error');

          value = 9999;
          changed = component.setValue(value);

          setTimeout(() => {
            assert.equal(!!component.error, true, 'Should contain error');
            assert.equal(component.error.message, error, 'Should contain error message');
            assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class');
            assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error');

            textFieldInput.dispatchEvent(event);

            setTimeout(() => {
              assert.equal(!!component.error, false, 'Should not contain error');
              done();
            }, 300);
          }, 300);
        }, 300);
      }, 300);
    }).catch(done);
  });

  it('Should provide validation of number input mask after setting value', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = '99/99-99.99:99,99';

    const validValues = [
      '',
      '99/99-99.99:99,99',
    ];

    const invalidValues = [
      '99/99-99.99:99,,99',
      '99/99-99.99:99,9',
      '9999-99.99:99,,99',
      '99/99-99.99:9(9,9)9',
      '99999999#999999999',
      'fffffff()f99/99-99.99:99,99',
      '77ff7777ff7777ff7777',
      '9/99-99.99999,99',
      '9/99-9/9.9/9:99,9/9',
      '99/99-a9.99:99,99',
      '99/99---.99:99,99',
      'ddddddddddddddd',
      '9/99-9/9.9/9:99,9/9ddd',
      '9/99-99.99999,fffffff',
      '99/_9-99.9f9:99,9g9',
      'A8/99-99.99:99,99',
    ];

    const testValidity = (values, valid, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const changed = component.setValue(value);
          const error = 'Text Field does not match the mask.';

          if (value) {
            assert.equal(changed, true, 'Should set value');
          }

          setTimeout(() => {
            if (valid) {
              assert.equal(!!component.error, false, 'Should not contain error');
            }
            else {
              assert.equal(!!component.error, true, 'Should contain error');
              assert.equal(component.error.message, error, 'Should contain error message');
              assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class');
              assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error');
            }

            if (_.isEqual(value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testValidity(validValues, true);
    testValidity(invalidValues, false, invalidValues[invalidValues.length-1]);
  });

  it('Should allow inputing only numbers and format input according to input mask', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = '99/99-99.99:99,99';

    const values = [
      '99/99-99.99:99,,99',
      '9999-99.99:999,,99',
      '99/99-99.99:9(9,9)9',
      '99999999999999999',
      'ffffffff99/99-99.99:99,99',
      '99ff999999ff999ff9',
      '9/99-99.99999,999',
      '9/99-9/9.9/9:999,9/9',
      '99.99-a9.99:999,99',
      '99/99---.99:9999,99',
      '999999999999',
      '99999-9/9.9/9:99,9/9ddd',
      '9----99999-99.99999,fffffff',
      '999-9kkkk9.99999f9:99,9g9',
      'A9/99-99.999:99,99',
    ];

    const testFormatting = (values, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const input = component.refs.input[0];
          const inputEvent = new Event('input');
          input.value = value;
          input.dispatchEvent(inputEvent);

          setTimeout(() => {
            assert.equal(!!component.error, false, 'Should not contain error');
            assert.equal(component.getValue(), '99/99-99.99:99,99', 'Should set and format value');

            if (_.isEqual(value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testFormatting(values, values[values.length-1]);
  });

  it('Should provide validation for alphabetic input mask after setting value', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = 'a/A/a-a:a.a,aa';

    const validValues = [
      '',
      'b/V/r-y:d.d,as',
      'b/b/r-y:d.d,as',
    ];

    const invalidValues = [
      'b/b/r-y:d.d',
      'b/v/r-yCC:d.d,as',
      'rD/F/R-y:d.d,DE',
      'bv/Sr-y:d.d,as',
      '555555555555555',
      'ssssEsssssssssssss',
      'b/v/Rr-y:d$.d,a',
      '3/3/#r-y:d.d,as',
      '3/3/6-6&&:d...d,as',
      '5/5/5ee-55.5,5'
    ];

    const testValidity = (values, valid, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const changed = component.setValue(value);
          const error = 'Text Field does not match the mask.';

          if (value) {
            assert.equal(changed, true, 'Should set value');
          }

          setTimeout(() => {
            if (valid) {
              assert.equal(!!component.error, false, 'Should not contain error');
            }
            else {
              assert.equal(!!component.error, true, 'Should contain error');
              assert.equal(component.error.message, error, 'Should contain error message');
              assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class');
              assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error');
            }

            if (_.isEqual(value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testValidity(validValues, true);
    testValidity(invalidValues, false, invalidValues[invalidValues.length-1]);
  });

  it('Should allow inputing only letters and format input according to input mask', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = 'a/a/a-a:a.a,aa';

    const values = [
        'ssssSSSSSSS/sss-ss.s,ss',
        'ss/sSss-sSSs.s,ss',
        'ssSssssssSSSssssss',
        's/sS/sss-s5555:sss.--s,s',
        '3/s3/Ss-s:ss.s,ssSsss',
        'ssSs3/3s/s6-s6:s...s,s',
        's5/5sSS/5s-5:sS---5.s5,s5sss'
    ];

    const testFormatting = (values, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const input = component.refs.input[0];
          const inputEvent = new Event('input');
          input.value = value;
          input.dispatchEvent(inputEvent);

          setTimeout(() => {
            assert.equal(!!component.error, false, 'Should not contain error');
            assert.equal(component.getValue(), 's/s/s-s:s.s,ss', 'Should set and format value');

            if (_.isEqual(value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testFormatting(values, values[values.length-1]);
  });

  it('Should provide validation for alphanumeric input mask after setting value', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = '**/***.*-*,**';

    const validValues = [
      '',
      'f4/D34.3-S,dd',
      'gg/ggg.g-g,gg',
      'DD/DDD.D-D,DD',
      '55/555.5-5,55',
    ];

    const invalidValues = [
      'er432ff',
      'rD5/F/R-y:d',
      '_=+dsds4',
      'sFFFFF--------2',
      'sd',
      'sf__df',
      'gg/ggg.g-g',
    ];

    const testValidity = (values, valid, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const changed = component.setValue(value);
          const error = 'Text Field does not match the mask.';

          if (value) {
            assert.equal(changed, true, 'Should set value');
          }

          setTimeout(() => {
            if (valid) {
              assert.equal(!!component.error, false, 'Should not contain error');
            }
            else {
              assert.equal(!!component.error, true, 'Should contain error');
              assert.equal(component.error.message, error, 'Should contain error message');
              assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class');
              assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error');
            }

            if (_.isEqual(value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testValidity(validValues, true);
    testValidity(invalidValues, false, invalidValues[invalidValues.length-1]);
  });

  it('Should allow inputing only letters and digits and format input according to input mask', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = '**/***.*-*,**';

    const values = [
      { value:'ssssSSSSSSS/sss-ss.s,ss', expected: 'ss/ssS.S-S,SS' },
      { value:'ss/sSss-sSSs.s,ss', expected: 'ss/sSs.s-s,SS' },
      { value:'ssS666ssssssSSSssssss', expected: 'ss/S66.6-s,ss' },
      { value:'s/sS/sss-s5555:sss.--s,s', expected: 'ss/Sss.s-s,55' },
      { value:'3/s3/Ss-s:ss.s,ssSsss', expected: '3s/3Ss.s-s,ss' },
      { value:'ssSs3/3s/s6-s6:s...s,s', expected: 'ss/Ss3.3-s,s6' },
      { value:'s5/5sSS/5s-5:sS---5.s5,s5sss', expected: 's5/5sS.S-5,s5' },
    ];

    const testFormatting = (values, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const input = component.refs.input[0];
          const inputEvent = new Event('input');
          input.value = value.value;
          input.dispatchEvent(inputEvent);

          setTimeout(() => {
            assert.equal(!!component.error, false, 'Should not contain error');
            assert.equal(component.getValue(), value.expected, 'Should set and format value');

            if (_.isEqual(value.value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testFormatting(values, values[values.length-1].value);
  });

  it('Should provide validation for mixed input mask after setting value', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = '**/99-aa';

    const validValues = [
      '',
      '4r/34-fg',
      '46/34-yy',
      'ye/56-op',
      'We/56-op',
    ];

    const invalidValues = [
      'te/56-Dp',
      'te/E6-pp',
      'tdddde/E6-pp',
      'te/E6',
      'te/E6-p',
      'gdfgdfgdf',
      '43543',
      'W'
    ];

    const testValidity = (values, valid, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const changed = component.setValue(value);
          const error = 'Text Field does not match the mask.';

          if (value) {
            assert.equal(changed, true, 'Should set value');
          }

          setTimeout(() => {
            if (valid) {
              assert.equal(!!component.error, false, 'Should not contain error');
            }
            else {
              assert.equal(!!component.error, true, 'Should contain error');
              assert.equal(component.error.message, error, 'Should contain error message');
              assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class');
              assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error');
            }

            if (_.isEqual(value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testValidity(validValues, true);
    testValidity(invalidValues, false, invalidValues[invalidValues.length-1]);
  });

  it('Should allow inputing only letters and digits and format input according to mixed input mask', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = '**/99-aa';

    const values = [
      { value:'S67gf-+f34cfd', expected: 'S6/73-cf' },
      { value:'56DDDfdsf23,DDdsf', expected: '56/23-ds' },
      { value:'--fs344d.g234df', expected: 'fs/34-dg' },
      { value:'000000000g234df', expected: '00/00-gd' },
    ];

    const testFormatting = (values, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const input = component.refs.input[0];
          const inputEvent = new Event('input');
          input.value = value.value;
          input.dispatchEvent(inputEvent);

          setTimeout(() => {
            assert.equal(!!component.error, false, 'Should not contain error');
            assert.equal(component.getValue(), value.expected, 'Should set and format value');

            if (_.isEqual(value.value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testFormatting(values, values[values.length-1].value);
  });

  it('Should allow multiple masks', (done) => {
    const form = _.cloneDeep(comp6);
    const tf = form.components[0];
    tf.allowMultipleMasks = true;
    tf.inputMasks = [
      { label: 'number', mask: '99-99' },
      { label: 'letter', mask: 'aa.aa' },
      { label: 'any', mask: '**/**' }
    ];

    const masks = [
      { index: 0, mask: 'number', valueValid:['33-33'], valueInvalid: ['Bd'] },
      { index: 1, mask: 'letter', valueValid:['rr.dd'], valueInvalid: ['Nr-22'] },
      { index: 2, mask: 'any', valueValid:['Dv/33'], valueInvalid: ['4/4'] },
    ];

    const testMask = (mask, valid, lastValue) => {
      const values = valid ? mask.valueValid : mask.valueInvalid;

      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);
          const component = form.getComponent('textField');
          const changed = component.setValue({ value: value, maskName: mask.mask });
          const error = 'Text Field does not match the mask.';

          if (value) {
            assert.equal(changed, true, 'Should set value');
          }

          setTimeout(() => {
            assert.equal(component.refs.select[0].options[mask.index].selected, true, 'Should select correct mask');
            assert.equal(component.getValue().maskName, mask.mask, 'Should apply correct mask');

            if (valid) {
              assert.equal(!!component.error, false, 'Should not contain error');
            }
            else {
              assert.equal(!!component.error, true, 'Should contain error');
              assert.equal(component.error.message, error, 'Should contain error message');
              assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class');
              assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error');
            }

            if (_.isEqual(value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    _.each(masks, (mask, index) => {
      testMask(mask, true);
      testMask(mask, false,  (index === masks.length - 1) ? mask.valueInvalid[mask.valueInvalid.length-1] : undefined);
    });
  });

  it('Should provide validation of number input mask with low dash and placeholder char after setting value', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = '99_99/99';
    form.components[0].inputMaskPlaceholderChar = '.';

    const validValues = [
      '',
      '55_44/88',
    ];

    const invalidValues = [
      '99 99 99',
      '44_44_55',
      '55555555',
    ];

    const testValidity = (values, valid, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const input = component.refs.input[0];

          assert.equal(input.placeholder, '.._../..', 'Should set placeholder using the char setting');

          const changed = component.setValue(value);
          const error = 'Text Field does not match the mask.';

          if (value) {
            assert.equal(changed, true, 'Should set value');
          }

          setTimeout(() => {
            if (valid) {
              assert.equal(!!component.error, false, 'Should not contain error');
            }
            else {
              assert.equal(!!component.error, true, 'Should contain error');
              assert.equal(component.error.message, error, 'Should contain error message');
              assert.equal(component.element.classList.contains('has-error'), true, 'Should contain error class');
              assert.equal(component.refs.messageContainer.textContent.trim(), error, 'Should show error');
            }

            if (_.isEqual(value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testValidity(validValues, true);
    testValidity(invalidValues, false, invalidValues[invalidValues.length-1]);
  });

  it('Should format input according to input mask with low dash when placeholder char is set', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputMask = '99_99/99';
    form.components[0].inputMaskPlaceholderChar = '.';

    const values = [
      { value:'4444444', expected: '44_44/44' },
    ];

    const testFormatting = (values, lastValue) => {
      _.each(values, (value) => {
        const element = document.createElement('div');

        Formio.createForm(element, form).then(form => {
          form.setPristine(false);

          const component = form.getComponent('textField');
          const input = component.refs.input[0];
          const inputEvent = new Event('input');
          input.value = value.value;
          input.dispatchEvent(inputEvent);

          setTimeout(() => {
            assert.equal(!!component.error, false, 'Should not contain error');
            assert.equal(component.getValue(), value.expected, 'Should set and format value');

            if (_.isEqual(value.value, lastValue)) {
              done();
            }
          }, 300);
        }).catch(done);
      });
    };

    testFormatting(values, values[values.length-1].value);
  });

  it('Should correctly count characters if character counter is enabled', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].showCharCount = true;
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const component = form.getComponent('textField');
      const inputValue = (value) => {
        const input = component.refs.input[0];
        const inputEvent = new Event('input');
        input.value = value;
        input.dispatchEvent(inputEvent);
      };

      const checkValue = (value) => {
        assert.equal(component.dataValue, value, 'Should set value');
        assert.equal(parseInt(component.refs.charcount[0].textContent), value.length, 'Should show correct chars number');
        assert.equal(component.refs.charcount[0].textContent, `${value.length} characters`, 'Should show correct message');
      };

      let value = 'test Value (@#!-"]) _ 23.,5}/*&&';
      inputValue(value);
      setTimeout(() => {
        checkValue(value);
        value = '';
        inputValue(value);

        setTimeout(() => {
          checkValue(value);
          value = '  ';
          inputValue(value);

          setTimeout(() => {
            checkValue(value);

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

  it('Should format value to uppercase', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].case = 'uppercase';
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const component = form.getComponent('textField');
      const inputValue = (value) => {
        const input = component.refs.input[0];
        const inputEvent = new Event('input');
        input.value = value;
        input.dispatchEvent(inputEvent);
      };

      const checkValue = (value) => {
        assert.equal(component.dataValue, value.toUpperCase(), 'Should format value to uppercase');
        assert.equal(component.getValue(), value.toUpperCase(), 'Should format value to uppercase');
      };

      let value = 'SoMe Value';
      inputValue(value);
      setTimeout(() => {
        checkValue(value);
        value = 'test 1 value 1';
        inputValue(value);

        setTimeout(() => {
          checkValue(value);
          value = '';
          inputValue(value);

          setTimeout(() => {
            checkValue(value);

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

  it('Should format value to lowercase', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].case = 'lowercase';
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const component = form.getComponent('textField');
      const inputValue = (value) => {
        const input = component.refs.input[0];
        const inputEvent = new Event('input');
        input.value = value;
        input.dispatchEvent(inputEvent);
      };

      const checkValue = (value) => {
        assert.equal(component.dataValue, value.toLowerCase(), 'Should format value to lowercase (1)');
        assert.equal(component.getValue(), value.toLowerCase(), 'Should format value to lowercase (2)');
      };

      let value = 'SoMe Value';
      inputValue(value);
      setTimeout(() => {
        checkValue(value);
        value = 'TEST 1 VALUE (1)';
        inputValue(value);

        setTimeout(() => {
          checkValue(value);
          value = '';
          inputValue(value);

          setTimeout(() => {
            checkValue(value);

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

  it('Should render and open/close calendar on click', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].widget = {
      allowInput: true,
      altInput: true,
      clickOpens: true,
      dateFormat: 'dd-MM-yyyy',
      enableDate: true,
      enableTime: true,
      format: 'dd-MM-yyyy',
      hourIncrement: 1,
      minuteIncrement: 5,
      mode: 'single',
      noCalendar: false,
      saveAs: 'date',
      'time_24hr': false,
      type: 'calendar',
      useLocaleSettings: false,
    };
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const component = form.getComponent('textField');
      const clickElem = (path) => {
        const elem = _.get(component, path);
        const clickEvent = new Event('click');
        elem.dispatchEvent(clickEvent);
      };
      const checkCalendarState = (open) => {
        const calendar = document.querySelector('.flatpickr-calendar');
        assert.equal(calendar.classList.contains('open'), open, `${open ? 'Should open calendar' : 'Should close calendar'}`);
      };

      assert.equal(component.widget.settings.type, 'calendar', 'Should create calendar widget');
      clickElem('refs.suffix[0]');

       setTimeout(() => {
        checkCalendarState(true);
        clickElem('refs.suffix[0]');

        setTimeout(() => {
          checkCalendarState(false);
          clickElem('element.children[1].children[0].children[1]');

          setTimeout(() => {
            checkCalendarState(true);
            clickElem('refs.suffix[0]');

            setTimeout(() => {
              checkCalendarState(false);
              document.body.innerHTML = '';
              done();
            }, 300);
          }, 300);
        }, 300);
      }, 300);
    }).catch(done);
  });

  it('Should set value into calendar', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].widget = {
      allowInput: true,
      altInput: true,
      clickOpens: true,
      dateFormat: 'dd-MM-yyyy',
      enableDate: true,
      enableTime: true,
      format: 'dd-MM-yyyy',
      hourIncrement: 1,
      minuteIncrement: 5,
      mode: 'single',
      noCalendar: false,
      saveAs: 'date',
     'time_24hr': false,
      type: 'calendar',
      useLocaleSettings: false,
    };
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const component = form.getComponent('textField');
      const clickElem = (path) => {
        const elem = _.get(component, path);
        const clickEvent = new Event('click');
        elem.dispatchEvent(clickEvent);
      };
      const checkCalendarState = (open, selectedDay) => {
        const calendar = document.querySelector('.flatpickr-calendar');
        assert.equal(calendar.classList.contains('open'), open, `${open ? 'Should open calendar' : 'Should close calendar'}`);
        if (selectedDay) {
          const day = calendar.querySelector('.flatpickr-day.selected').textContent;
          assert.equal(day, selectedDay, 'Should select correct day');
        }
      };

      const date = '16-03-2031';

      component.setValue(date);

       setTimeout(() => {
        checkCalendarState(false);
        const widget = component.element.querySelector('.flatpickr-input').widget;

        assert.equal(component.getValue(), date, 'Should set text field value');
        assert.equal(widget.calendar.input.value, date, 'Should set flatpickr value');
        assert.equal(widget.calendar.currentMonth, 2, 'Should set correct month');
        assert.equal(widget.calendar.currentYear, 2031, 'Should set correct year');

        clickElem('refs.suffix[0]');

        setTimeout(() => {
          checkCalendarState(true);
          clickElem('refs.suffix[0]');

          setTimeout(() => {
            checkCalendarState(false);
            document.body.innerHTML = '';
            done();
          }, 300);
        }, 300);
      }, 300);
    }).catch(done);
  });

  it('Should allow manual input and set value on blur if calendar widget is enabled with allowed input', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].widget = {
      allowInput: true,
      altInput: true,
      clickOpens: true,
      dateFormat: 'dd-MM-yyyy',
      enableDate: true,
      enableTime: true,
      format: 'dd-MM-yyyy',
      hourIncrement: 1,
      minuteIncrement: 5,
      mode: 'single',
      noCalendar: false,
      saveAs: 'date',
     'time_24hr': false,
      type: 'calendar',
      useLocaleSettings: false,
    };
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const component = form.getComponent('textField');
      const clickElem = (path, element) => {
        const elem = element || _.get(component, path);
        const clickEvent = new Event('click');
        elem.dispatchEvent(clickEvent);
      };
      const checkCalendarState = (open, selectedDay) => {
        const calendar = document.querySelector('.flatpickr-calendar');
        assert.equal(calendar.classList.contains('open'), open, `${open ? 'Should open calendar' : 'Should close calendar'}`);
        if (selectedDay) {
          const day = calendar.querySelector('.flatpickr-day.selected').textContent;
          assert.equal(day, selectedDay, 'Should select correct day');
        }
      };

      const triggerDateInputEvent = (eventName, value) => {
        const dateInput = component.element.querySelector('.form-control.input');
        const event = new Event(eventName);
        if (eventName === 'input') {
          dateInput.value = value;
        }
        dateInput.dispatchEvent(event);
      };

      triggerDateInputEvent('focus');

       setTimeout(() => {
        const date = '21-01-2001';
        checkCalendarState(true);
        triggerDateInputEvent('input', date);

        setTimeout(() => {
          checkCalendarState(true);
          triggerDateInputEvent('blur');

          setTimeout(() => {
            checkCalendarState(true, 21);

            assert.equal(component.getValue(), date, 'Should set text field value');
            const widget = component.element.querySelector('.flatpickr-input').widget;
            assert.equal(widget.calendar.input.value, date, 'Should set flatpickr value');
            assert.equal(widget.calendar.currentMonth, 0, 'Should set correct month');
            assert.equal(widget.calendar.currentYear, 2001, 'Should set correct year');

            clickElem('refs.suffix[0]');

            setTimeout(() => {
              checkCalendarState(false);
              assert.equal(component.getValue(), date, 'Should save text field value');

              document.body.innerHTML = '';
              done();
            }, 300);
          }, 300);
        }, 300);
      }, 300);
    }).catch(done);
  });

  it('Should allow removing date value if calendar widget is enabled with allowed input', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].widget = {
      allowInput: true,
      altInput: true,
      clickOpens: true,
      dateFormat: 'dd-MM-yyyy',
      enableDate: true,
      enableTime: true,
      format: 'dd-MM-yyyy',
      hourIncrement: 1,
      minuteIncrement: 5,
      mode: 'single',
      noCalendar: false,
      saveAs: 'date',
     'time_24hr': false,
      type: 'calendar',
      useLocaleSettings: false,
    };
    const element = document.createElement('div');

    Formio.createForm(element, form).then(form => {
      const component = form.getComponent('textField');
      const clickElem = (path, element) => {
        const elem = element || _.get(component, path);
        const clickEvent = new Event('click');
        elem.dispatchEvent(clickEvent);
      };

      const checkCalendarState = (open, selectedDay, noSelectedDay) => {
        const calendar = document.querySelector('.flatpickr-calendar');
        assert.equal(calendar.classList.contains('open'), open, `${open ? 'Should open calendar' : 'Should close calendar'}`);
        if (selectedDay) {
          const day = calendar.querySelector('.flatpickr-day.selected').textContent;
          assert.equal(day, selectedDay, 'Should select correct day');
        }
        if (noSelectedDay) {
          const day = calendar.querySelector('.flatpickr-day.selected');
          assert.equal(!!day, false, 'Should not contain selected day');
        }
      };

      const triggerDateInputEvent = (eventName, value) => {
        const dateInput = component.element.querySelector('.form-control.input');
        const event = new Event(eventName);
        if (eventName === 'input') {
          dateInput.value = value;
        }
        dateInput.dispatchEvent(event);
      };

      let date = '12-03-2009';
      component.setValue(date);
      triggerDateInputEvent('focus');

       setTimeout(() => {
        assert.equal(component.getValue(), date, 'Should set text field value');
        date = '';
        checkCalendarState(true);
        triggerDateInputEvent('input', date);

        setTimeout(() => {
          checkCalendarState(true);
          triggerDateInputEvent('blur');

          setTimeout(() => {
            checkCalendarState(true, '', true);

            assert.equal(component.getValue(), date, 'Should set text field value');
            const widget = component.element.querySelector('.flatpickr-input').widget;
            assert.equal(widget.calendar.input.value, date, 'Should set flatpickr value');

            clickElem('refs.suffix[0]');

            setTimeout(() => {
              checkCalendarState(false);
              assert.equal(component.getValue(), date, 'Should save text field value');
              document.body.innerHTML = '';
              done();
            }, 300);
          }, 300);
        }, 300);
      }, 300);
    }).catch(done);
  });

  it('Test Display mask', (done) => {
    const element = document.createElement('div');

    Formio.createForm(element, withDisplayAndInputMasks).then(form => {
      const textField = form.getComponent(['textField']);
      const textFieldDisplayMask = form.getComponent(['textFieldDisplayMask']);
      const textFieldDisplayAndInputMasks = form.getComponent(['textFieldDisplayAndInputMasks']);
      const textFieldDisplayAndInputMasksReverse = form.getComponent(['textFieldDisplayAndInputMasksReverse']);

      Harness.dispatchEvent(
        'input',
        form.element,
        '[name="data[textField]"',
        (input) => input.value = '123123',
      );
      Harness.dispatchEvent(
        'input',
        form.element,
        '[name="data[textFieldDisplayMask]"',
        (input) => input.value = '123123',
      );
      Harness.dispatchEvent(
        'input',
        form.element,
        '[name="data[textFieldDisplayAndInputMasks]"',
        (input) => input.value = '123123',
      );
      Harness.dispatchEvent(
        'input',
        form.element,
        '[name="data[textFieldDisplayAndInputMasksReverse]"',
        (input) => input.value = '123123',
      );

      setTimeout(() => {
        Harness.getInputValue(textField, 'data[textField]', '123-123');
        Harness.getInputValue(textFieldDisplayMask, 'data[textFieldDisplayMask]', '123-123');
        Harness.getInputValue(textFieldDisplayAndInputMasks, 'data[textFieldDisplayAndInputMasks]', '+1(23)-123');
        Harness.getInputValue(textFieldDisplayAndInputMasksReverse, 'data[textFieldDisplayAndInputMasksReverse]', '123-123');

        assert.equal(
          textField.dataValue,
          '123-123',
          'If only Input mask is set, it should affect both value and view',
        );
        assert.equal(
          textFieldDisplayMask.dataValue,
          '123123',
          'If only Display mask is set, it should affect only view',
        );
        assert.equal(
          textFieldDisplayAndInputMasks.dataValue,
          '123-123',
          'If both Input and Display masks are set, the Input mask should be applied to value',
        );
        assert.equal(
          textFieldDisplayAndInputMasksReverse.dataValue,
          '+1(23)-123',
          'If both Input and Display masks are set, the Input mask should be applied to value',
        );
        done();
      }, 200);
    }).catch(done);
  });

  it('Should render HTML', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputFormat = 'html';
    const element = document.createElement('div');

    Formio.createForm(element, form, {
      readOnly: true
    }).then(form => {
      form.setSubmission({
        data: {
          textField: '<b>HTML!</b>'
        }
      });
      setTimeout(() => {
        const textField = form.getComponent('textField');
        textField.loadRefs(element, {
          value: 'multiple'
        });
        assert.equal(textField.refs.value[0].innerHTML, '<b>HTML!</b>');
        done();
      }, 300);
    }).catch(done);
  });

  it('Should render plain text', (done) => {
    const form = _.cloneDeep(comp6);
    form.components[0].inputFormat = 'plain';
    const element = document.createElement('div');

    Formio.createForm(element, form, {
      readOnly: true
    }).then(form => {
      form.setSubmission({
        data: {
          textField: '<b>Plain!</b>'
        }
      });
      setTimeout(() => {
        const textField = form.getComponent('textField');
        assert.equal(textField.refs.input[0].value, '<b>Plain!</b>');
        done();
      }, 300);
    }).catch(done);
  });
});