Home Reference Source

src/Webform.unit.js

import assert from 'power-assert';
import { expect } from 'chai';
import sinon from 'sinon';
import _ from 'lodash';
import Harness from '../test/harness';
import FormTests from '../test/forms';
import Webform from './Webform';
import 'flatpickr';
import AllComponents from './components';
import { Formio } from './Formio';
import {
  settingErrors,
  clearOnHide,
  manualOverride,
  validationOnBlur,
  calculateValueWithManualOverride,
  calculateValueWithSubmissionMetadata,
  formWithAdvancedLogic,
  formWithPatternValidation,
  calculatedSelectboxes,
  calculateZeroValue,
  formWithConditionalLogic,
  formWithCalculatedValueWithoutOverriding,
  formWithTimeComponent,
  formWithEditGridModalDrafts,
  formWithBlurValidationInsidePanel,
  modalEditComponents,
  calculatedNotPersistentValue,
  calculateValueInEditingMode,
  initiallyCollapsedPanel,
  multipleTextareaInsideConditionalComponent,
  disabledNestedForm,
  formWithEditGridAndNestedDraftModalRow,
  formWithDateTimeComponents,
  formWithCollapsedPanel,
  formWithCustomFormatDate,
  tooltipActivateCheckbox,
} from '../test/formtest';
import UpdateErrorClassesWidgets from '../test/forms/updateErrorClasses-widgets';
import nestedModalWizard from '../test/forms/nestedModalWizard';
import disableSubmitButton from '../test/forms/disableSubmitButton';
import formWithAddressComponent from '../test/forms/formWithAddressComponent';
import formWithDataGridInitEmpty from '../test/forms/dataGridWithInitEmpty';
import nestedFormInsideDataGrid from '../test/forms/dataGrid-nestedForm';
import formWithDataGrid from '../test/forms/formWithDataGrid';
import translationTestForm from '../test/forms/translationTestForm';
import formWithDataGridWithCondColumn from '../test/forms/dataGridWithConditionalColumn';
import { nestedFormInWizard } from '../test/fixtures';
import { fastCloneDeep } from './utils/utils';
import dataGridOnBlurValidation from '../test/forms/dataGridOnBlurValidation';
import checkBlurFocusEventForm from '../test/forms/checkBlurFocusEventForm';
import truncateMultipleSpaces from '../test/forms/truncateMultipleSpaces';
import calculatedValue from '../test/forms/calculatedValue';
import conditionalDataGridWithTableAndRadio from '../test/forms/conditionalDataGridWithTableAndRadio';
import calculateValueWithManualOverrideLableValueDataGrid
  from '../test/forms/calculateValueWithManualOverrideLableValueDataGrid';
import deeplyNestedDataGridAndContainer from '../test/forms/nestedDataGridsAndContainers';
import columnWithConditionalComponents from '../test/forms/columnWithConditionalComponents';
import formWithSurvey from '../test/forms/formWithSurvey';
import formWithSelectBoxes from '../test/forms/formWithSelectBoxes';
import formWithDayComp from '../test/forms/formWithDayComp';
import formWithCalcValue from '../test/forms/formWithCalcValue';
import formWithAllowCalculateOverride from '../test/forms/formWithAllowCalculateOverride';
import testClearOnHideInsideEditGrid from '../test/forms/clearOnHideInsideEditGrid';
import formWithNestedDataGridInitEmpty from '../test/forms/nestedDataGridWithInitEmpty';
import formWithEventLogicInHiddenComponent from '../test/forms/formWithEventLogicInHiddenComponent';
import * as FormioUtils from './utils/utils';
import htmlRenderMode from '../test/forms/htmlRenderMode';
import optionalSanitize from '../test/forms/optionalSanitize';
import formsWithNewSimpleConditions from '../test/forms/formsWithNewSimpleConditions';
import formWithRadioInsideDataGrid from '../test/forms/formWithRadioInsideDataGrid';
import formWithCheckboxRadioType from '../test/forms/formWithCheckboxRadioType';
import formWithFormController from '../test/forms/formWithFormController';
import calculateValueOnServerForEditGrid from '../test/forms/calculateValueOnServerForEditGrid';

global.requestAnimationFrame = (cb) => cb();
global.cancelAnimationFrame = () => {};

if (_.has(Formio, 'Components.setComponents')) {
  Formio.Components.setComponents(AllComponents);
}

/* eslint-disable max-statements  */
describe('Webform tests', function() {
  this.retries(3);

  it('Should execute form controller', function(done) {
    Formio.createForm(formWithFormController).then((form) => {
      setTimeout(() => {
        const textField = form.getComponent('textField');

        assert.equal(textField.getValue(), 'Hello World');
        assert.equal(textField.disabled, true);
        assert.equal(form.components[0].disabled, true);

        done();
      }, 300);
    }).catch((err) => done(err));
  });

  it('Should set radio components value inside data grid correctly', function(done) {
    Formio.createForm(formWithRadioInsideDataGrid).then((form) => {
      const dataGridData =  [{ radio: 'two' },{ radio: 'two' } ,{ radio: 'three' }];
      form.setValue({ data: { dataGrid: fastCloneDeep(dataGridData) } });
      setTimeout(() => {
          const dataGrid = form.getComponent('dataGrid');
          assert.deepEqual(dataGrid.dataValue, dataGridData);
          done();
      }, 200);
    }).catch((err) => done(err));
  });

  it('Should not fall into setValue calls loop when doing value calculation on server', done => {
    const formElement = document.createElement('div');
    // Set a spy for Edit Grid setValue method
    const spy = sinon.spy(Formio.Components.components.editgrid.prototype, 'setValue');

    Formio.createForm(formElement, calculateValueOnServerForEditGrid, { server: true, noDefaults: true } )
      .then(form => {
        assert.deepEqual(form.data, { editGrid: [{ fielda: undefined, fieldb: 'test' }] });
        assert.equal(spy.callCount, 1);

        const first = form.getComponent('first');

        first.setValue('test value');

        setTimeout(() => {
          assert.deepEqual(form.data, {
            first: 'test value',
            editGrid: [{ fielda: 'test value', fieldb: 'test' }]
          });
          assert.equal(spy.callCount, 2);
          // Remove the spy from setValue method
          Formio.Components.components.editgrid.prototype.setValue.restore();
          done();
        }, 300);
      })
      .catch(done);
  });

  it('Should fire blur and focus events for address and select components', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement);

    form.setForm(checkBlurFocusEventForm).then(() => {
      let blurEvents = 0;
      let focusEvents = 0;
      form.on('blur', () => {
        blurEvents = blurEvents + 1;
      });

      form.on('focus', () => {
        focusEvents = focusEvents + 1;
      });

      const focusEvent = new Event('focus');
      const blurEvent = new Event('blur');

      const selectChoices = form.getComponent('selectChoices');
      selectChoices.focusableElement.dispatchEvent(focusEvent);

      setTimeout(() => {
        selectChoices.focusableElement.dispatchEvent(blurEvent);

        const selectHtml = form.getComponent('selectHtml');
        selectHtml.refs.selectContainer.dispatchEvent(focusEvent);

        setTimeout(() => {
          selectHtml.refs.selectContainer.dispatchEvent(blurEvent);

          const address = form.getComponent('address');
          address.refs.searchInput[0].dispatchEvent(focusEvent);

          setTimeout(() => {
            address.refs.searchInput[0].dispatchEvent(blurEvent);

            setTimeout(() => {
              assert.equal(focusEvents, 3);
              assert.equal(blurEvents, 3);
              done();
            }, 300);
          }, 300);
        }, 300);
      }, 300);
    }).catch((err) => done(err));
  });

  it('Should return correct string value for checkbox radio type', function(done) {
    Formio.createForm(formWithCheckboxRadioType).then((form) => {
      form.setValue({ data: { radio: 'value1', checkbox: true } });
      setTimeout(() => {
        const stringValues = {
          checkbox1: 'Yes',
          checkbox2: 'No',
          checkbox: 'Yes'
        };

        form.eachComponent((comp) => {
          assert.equal(comp.getValueAsString(comp.dataValue), stringValues[`${comp.component.key}`], `Error for string value of ${comp.component.key}`);
        });

        form.setValue({ data: { radio: 'value2', checkbox: false } });

        setTimeout(() => {
          const stringValues2 = {
            checkbox1: 'No',
            checkbox2: 'Yes',
            checkbox: 'No'
          };

          form.eachComponent((comp) => {
            assert.equal(comp.getValueAsString(comp.dataValue), stringValues2[`${comp.component.key}`], `Error for string value of ${comp.component.key}`);
          });

          done();
        }, 200);
      }, 200);
    }).catch((err) => done(err));
  });

  it('Should set value for hidden nested component through the logic triggered by event', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement);

    form.setForm(formWithEventLogicInHiddenComponent).then(() => {
      const regesteredAddress = form.getComponent('registeredAddressInformation').getComponent('streetAddress')[0];
      const address =  form.getComponent('addressInformation').getComponent('streetAddress')[0];

      assert.equal(address.visible, true);
      assert.equal(regesteredAddress.visible, false);

      const value = 'Dallas';
      address.setValue(value);

      setTimeout(() => {
        assert.equal(address.dataValue, value);
        assert.equal(regesteredAddress.dataValue, value);

        const role =  form.getComponent('role');
        role.setValue(['client']);

        setTimeout(() => {
          assert.equal(address.visible, false);
          assert.equal(regesteredAddress.visible, true);
          assert.equal(regesteredAddress.dataValue, value);
          assert.equal(address.dataValue, value);
          done();
        }, 500);
      }, 500);
    }).catch((err) => done(err));
  });

  it('Should recalculate value when submission is being set in edit mode', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement);

    form.setForm(formWithCalcValue).then(() => {
      const numberComp = form.getComponent('number');
      const checkbox = form.getComponent('checkbox');

      form.setSubmission({}).then(() => {
        setTimeout(() => {
          assert.equal(numberComp.dataValue, 0);
          assert.equal(checkbox.dataValue, true);
          form.setSubmission({ data: { number: 7, checkbox: true } }).then(() => {
            setTimeout(() => {
              assert.equal(numberComp.dataValue, 7);
              assert.equal(checkbox.dataValue, false);
              done();
            }, 500);
          });
        }, 500);
      });
    }).catch((err) => done(err));
  });

  it('Should not activate checkbox when clicking tooltip icon', function(done) {
    const element = document.createElement('div');
    const form = new Webform(element);

    form.setForm(tooltipActivateCheckbox).then(() => {
    const checkboxValue = form.element.querySelector('[name="data[checkbox]"]').value;
    Harness.clickElement(form, form.element.querySelector('[ref="tooltip"]'));

    setTimeout(() => {
      assert.equal(form.element.querySelector('[name="data[checkbox]"]').value, checkboxValue);
      done();
    }, 200);
    })
    .catch((err) => done(err));
  });

  it('Should show submission if passed as option', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { renderMode: 'html', readOnly: true, submission: { data: { survey: { question1: 'a3', question2: 'a1' } } } });

    form.setForm(formWithSurvey).then(() => {
      const survey = form.getComponent('survey');
      const values = survey.element.querySelectorAll('td');

      assert.equal(values.length, 2);
      assert.equal(values[0].innerHTML.trim(), 'a3');
      assert.equal(values[1].innerHTML.trim(), 'a1');
      done();
   }).catch((err) => done(err));
  });

  it('Should show survey values in html render mode', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { renderMode: 'html', readOnly: true });

    form.setForm(formWithSurvey).then(() => {
      form.setSubmission({ data: { survey: { question1: 'a3', question2: 'a1' } } }).then(() => {
        const survey = form.getComponent('survey');
        const values = survey.element.querySelectorAll('td');

        assert.equal(values.length, 2);
        assert.equal(values[0].innerHTML.trim(), 'a3');
        assert.equal(values[1].innerHTML.trim(), 'a1');
        done();
      });
   }).catch((err) => done(err));
  });

  it('Should show select boxes values in html render mode', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { renderMode: 'html', readOnly: true });

    form.setForm(formWithSelectBoxes).then(() => {
      form.setSubmission({ data: { selectBoxes: { a: true, b: true, c: false } } }).then(() => {
        const selectBoxes = form.getComponent('selectBoxes');
        const values = selectBoxes.element.querySelector('[ref="value"]').textContent.trim();

        assert.equal(values, 'a, b');
        done();
      });
   }).catch((err) => done(err));
  });

  it('Should show day value in html render mode', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { renderMode: 'html', readOnly: true });

    form.setForm(formWithDayComp).then(() => {
      form.setSubmission({ data: { day: '05/07/2020' } }).then(() => {
        const day = form.getComponent('day');
        const value = day.element.querySelector('[ref="value"]').textContent.trim();

        assert.equal(value, '05/07/2020');
        done();
      });
   }).catch((err) => done(err));
  });

  it('Should allow to input value and add rows in deeply nested conditional dataGrid', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement);

    form.setForm(deeplyNestedDataGridAndContainer).then(() => {
      const parentDataGrid = form.getComponent('dataGrid6');

      assert.equal(parentDataGrid.rows.length, 1);
      assert.equal(parentDataGrid.rows[0].dataGrid5.visible, false);

      const checkbox = form.getComponent('checkbox');
      checkbox.setValue(true);

      setTimeout(() => {
        assert.equal(parentDataGrid.rows.length, 1);
        assert.equal(parentDataGrid.rows[0].dataGrid5.visible, true);

        const numberInput = parentDataGrid.rows[0].dataGrid5.rows[0].number.refs.input[0];
        numberInput.value = 555;

        const inputEvent = new Event('input');
        numberInput.dispatchEvent(inputEvent);

        setTimeout(() => {
          const conditionalDataGrid = form.getComponent('dataGrid6').rows[0].dataGrid5;
          const numberComp = conditionalDataGrid.rows[0].number;

          assert.equal(numberComp.dataValue, 555);
          assert.equal(numberComp.refs.input[0].value, 555);

          const addRowBtn = conditionalDataGrid.refs[`${'datagrid-dataGrid5-addRow'}`][0];
          const clickEvent = new Event('click');
          addRowBtn.dispatchEvent(clickEvent);

          setTimeout(() => {
            assert.equal(conditionalDataGrid.rows.length, 2);
            done();
          }, 300);
        }, 300);
      }, 300);
    }).catch((err) => done(err));
  });

  it('Should adjust columns when conditional fields appear/disappear', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement);

    form.setForm(columnWithConditionalComponents).then(() => {
      const selectBoxes = form.getComponent('selectBoxes');
      const columns = form.getComponent('columns');

      columns.columns.forEach((column, index) => {
        assert.equal(column[0].visible, false,  `Column ${index + 1} component should be hidden`);
        assert.equal( columns.component.columns[index].currentWidth, 0,  `Column ${index + 1}  width should be 0`);
      });

      selectBoxes.setValue({ '1': false, '2': false, '3': true, '4': false, '5': false, '6': true });

      setTimeout(() => {
        columns.columns.forEach((column, index) => {
          if ([3,6].includes(index+1)) {
            assert.equal(column[0].visible, true, `Column ${index + 1} component should be visible`);
            assert.equal(columns.component.columns[index].currentWidth, 2, `Column ${index + 1}  width should be 2`);
          }
          else {
            assert.equal(column[0].visible, false, `Column ${index + 1} component should be hidden`);
            assert.equal( columns.component.columns[index].currentWidth, 0, `Column ${index + 1}  width should be 0`);
          }
        });

        const visibleTextField1 = columns.columns[2][0].refs.input[0];
        const visibleTextField2 = columns.columns[5][0].refs.input[0];

        visibleTextField1.value = 'test   ';
        visibleTextField2.value = ' some ';

        const inputEvent = new Event('input');
        visibleTextField1.dispatchEvent(inputEvent);
        visibleTextField2.dispatchEvent(inputEvent);

        setTimeout(() => {
          const visibleTextField1 = columns.columns[2][0].refs.input[0];
          const visibleTextField2 = columns.columns[5][0].refs.input[0];

          assert.equal(visibleTextField1.value,'test   ', 'Should not cut whitespaces while inputting into the conditional component inside column');
          assert.equal(visibleTextField2.value,' some ', 'Should not cut whitespaces while inputting into the conditional component inside column');
          selectBoxes.setValue({ '1': false, '2': false, '3': false, '4': false, '5': false, '6': true });

          setTimeout(() => {
            columns.columns.forEach((column, index) => {
              if ([6].includes(index+1)) {
                assert.equal(column[0].visible, true, `Column ${index + 1} component should be visible`);
                assert.equal( columns.component.columns[index].currentWidth, 2,  `Column ${index + 1}  width should be 2`);
              }
              else {
                assert.equal(column[0].visible, false, `Column ${index + 1} component should be hidden`);
                assert.equal( columns.component.columns[index].currentWidth, 0,  `Column ${index + 1}  width should be 0`);
              }
            });
            done();
          }, 300);
        }, 300);
      }, 300);
    }).catch((err) => done(err));
  });

  it('Should not translate en value if _userInput option is provided and value presents in reserved translation names', done => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, {
     language: 'en'
    });
    form.setForm(translationTestForm).then(() => {
      setTimeout(() => {
        const selectComp = form.getComponent('select');
        const options = selectComp.element.querySelector('[role="listbox"]').children;
        const option1 = options[0].textContent.trim();
        const option2 = options[1].textContent.trim();
        const label = selectComp.element.querySelector('label').textContent.trim();
        assert.equal(option1, translationTestForm.components[0].data.values[0].label);
        assert.equal(option2, translationTestForm.components[0].data.values[1].label);
        assert.equal(label, translationTestForm.components[0].label);
        document.body.innerHTML = '';
        done();
      }, 100);
    }).catch(done);
  });

  it('Should translate in English if _userInput option is provided and value does not present in reserved translation names', done => {
    const formElement = document.createElement('div');
    const selectLabel = 'Select test label';
    const translationForm = fastCloneDeep(translationTestForm);
    translationForm.components[0].label = selectLabel;
    const form = new Webform(formElement, {
      language: 'en',
      i18n: {
        en: {
          'Select test label': 'English Label'
        },
        fr: {
          'Select test label': 'French Label'
        }
      }
    });

    form.setForm(translationForm).then(() => {
      const selectComp = form.getComponent('select');
      const label = selectComp.element.querySelector('label').textContent.trim();

      assert.equal(label, 'English Label');
      document.body.innerHTML = '';
      done();
    }).catch(done);
  });

  it('Should translate value in franch if _userInput option is provided and value does not present in reserved translation names', done => {
    const formElement = document.createElement('div');
    const selectLabel = 'Select test label';
    const translationForm = fastCloneDeep(translationTestForm);
    translationForm.components[0].label = selectLabel;
    const form = new Webform(formElement, {
      language: 'fr',
      i18n: {
        en: {
          'Select test label': 'English Label'
        },
        fr: {
          'Select test label': 'French Label'
        }
      }
    });

    form.setForm(translationForm).then(() => {
      const selectComp = form.getComponent('select');
      const label = selectComp.element.querySelector('label').textContent.trim();

      assert.equal(label, 'French Label');
      document.body.innerHTML = '';
      done();
    }).catch(done);
  });

  it('Should display dataGrid conditional column once the condition is met', function(done) {
    const formElement = document.createElement('div');
    const formWithCondDataGridColumn = new Webform(formElement);

    formWithCondDataGridColumn.setForm(formWithDataGridWithCondColumn).then(() => {
      const condDataGridField = formWithCondDataGridColumn.element.querySelector( '[name="data[dataGrid][0][numberCond]"]');
      assert.equal(!!condDataGridField, false);

      const textField = formWithCondDataGridColumn.element.querySelector( '[name="data[textField]"]');
      textField.value = 'show';

      const inputEvent = new Event('input');
      textField.dispatchEvent(inputEvent);

      setTimeout(() => {
        const condDataGridFieldAfterFulfillingCond = formWithCondDataGridColumn.element.querySelector( '[name="data[dataGrid][0][numberCond]"]');
        assert.equal(!!condDataGridFieldAfterFulfillingCond, true);

        done();
      }, 300);
    }).catch((err) => done(err));
  });

  it('Should remove dataGrid extra rows and components after setting value with less row number', function(done) {
    const formElement = document.createElement('div');
    const formWithDG = new Webform(formElement);

    formWithDG.setForm(formWithDataGrid.form).then(() => {
    formWithDG.setSubmission(formWithDataGrid.submission3rows);

      setTimeout(() => {
        assert.equal(formWithDG.components[0].rows.length, 3);
        assert.equal(formWithDG.components[0].components.length, 3);

        formWithDG.setSubmission(formWithDataGrid.submission1row);

        setTimeout(() => {
          assert.equal(formWithDG.components[0].rows.length, 1);
          assert.equal(formWithDG.components[0].components.length, 1);

          done();
        }, 200);
      }, 100);
    }).catch((err) => done(err));
  });

  it('Should not delete/change date value in dataGrid after adding new row', function(done) {
    const formElement = document.createElement('div');
    const formWithDate = new Webform(formElement);

    formWithDate.setForm(formWithCustomFormatDate).then(() => {
      setTimeout(() => {
        const clickEvent = new Event('click');

        const dateCompInputWidget = formWithDate.element.querySelector('.formio-component-textField').querySelector('.flatpickr-input').widget;
        const dateAltFormat = dateCompInputWidget.calendar.config.altFormat;
        dateCompInputWidget.calendar.setDate('30-05-2020', true, dateAltFormat);

        const dateCompInputWidget1 = formWithDate.element.querySelector('.formio-component-dateTime').querySelector('.flatpickr-input').widget;
        const dateAltFormat1 = dateCompInputWidget1.calendar.config.altFormat;
        dateCompInputWidget1.calendar.setDate('30-05-2020', true, dateAltFormat1);

        const dateCompInputWidget2 = formWithDate.element.querySelector('.formio-component-textField2').querySelector('.flatpickr-input').widget;
        const dateAltFormat2 = dateCompInputWidget2.calendar.config.altFormat;
        dateCompInputWidget2.calendar.setDate('30-05-2020', true, dateAltFormat2);

        setTimeout(() => {
        const dateCompAltInput = formWithDate.element.querySelector('.formio-component-textField').querySelector('.flatpickr-input');
        const dateComp = formWithDate.element.querySelector('.formio-component-textField').querySelector('[type="text"]');

        const dateCompAltInput1 = formWithDate.element.querySelector('.formio-component-dateTime').querySelector('.flatpickr-input');
        const dateComp1 = formWithDate.element.querySelector('.formio-component-dateTime').querySelector('[type="text"]');

        const dateCompAltInput2 = formWithDate.element.querySelector('.formio-component-textField2').querySelector('.flatpickr-input');
        const dateComp2 = formWithDate.element.querySelector('.formio-component-textField2').querySelector('[type="text"]');

        assert.equal(dateCompAltInput.value,'30-05-2020');
        assert.equal(dateComp.value,'30-05-2020');

        assert.equal(dateCompAltInput1.value,'2020-05-30T00:00:00');
        assert.equal(dateComp1.value,'30-05-2020');

        assert.equal(dateCompAltInput2.value,'2020-05-30T00:00:00');
        assert.equal(dateComp2.value,'30-05-2020');

        const addNewRowBtn = formWithDate.element.querySelector('.formio-button-add-row');
        addNewRowBtn.dispatchEvent(clickEvent);

        setTimeout(() => {
          const dataGridRows = formWithDate.element.querySelectorAll('[ref="datagrid-dataGrid-row"]');
          assert.equal(dataGridRows.length, 2);

          const dateCompAltInputAfterAddingRow = formWithDate.element.querySelectorAll('.formio-component-textField')[0].querySelector('.flatpickr-input');
          const dateCompAfterAddingRow = formWithDate.element.querySelectorAll('.formio-component-textField')[0].querySelector('[type="text"]');

          const dateCompAltInputAfterAddingRow1 = formWithDate.element.querySelectorAll('.formio-component-dateTime')[0].querySelector('.flatpickr-input');
          const dateCompAfterAddingRow1 = formWithDate.element.querySelectorAll('.formio-component-dateTime')[0].querySelector('[type="text"]');

          const dateCompAltInputAfterAddingRow2 = formWithDate.element.querySelectorAll('.formio-component-textField2')[0].querySelector('.flatpickr-input');
          const dateCompAfterAddingRow2 = formWithDate.element.querySelectorAll('.formio-component-textField2')[0].querySelector('[type="text"]');

          assert.equal(dateCompAltInputAfterAddingRow.value,'30-05-2020');
          assert.equal(dateCompAfterAddingRow.value,'30-05-2020');

          assert.equal(dateCompAltInputAfterAddingRow1.value,'2020-05-30T00:00:00');
          assert.equal(dateCompAfterAddingRow1.value,'30-05-2020');

          assert.equal(dateCompAltInputAfterAddingRow2.value,'2020-05-30T00:00:00');
          assert.equal(dateCompAfterAddingRow2.value,'30-05-2020');

          done();
         }, 150);
        }, 50);
      }, 100);
    }).catch((err) => done(err));
  });

  it('Should open collapsed panel with invalid components inside container that is inside the panel on submit', function(done) {
    const formElement = document.createElement('div');
    const formWithPanel = new Webform(formElement);

    formWithPanel.setForm(formWithCollapsedPanel).then(() => {
      const clickEvent = new Event('click');

      assert.equal(formWithPanel.components[0].collapsed, true);

      const submitBtn = formWithPanel.element.querySelector('[name="data[submit]"]');
      submitBtn.dispatchEvent(clickEvent);

      setTimeout(() => {
        assert.equal(formWithPanel.components[0].collapsed, false);
        done();
      }, 200);
    }).catch((err) => done(err));
  });

  it('Should correctly set date after collapsing and openning the panel', function(done) {
    const formElement = document.createElement('div');
    const formWithDate = new Webform(formElement);

    formWithDate.setForm(formWithDateTimeComponents).then(() => {
      const clickEvent = new Event('click');

      const dateTimeCompInputWidget = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('.flatpickr-input').widget;
      const dateTimeAltFormat = dateTimeCompInputWidget.calendar.config.altFormat;
      dateTimeCompInputWidget.calendar.setDate('05-05-2020T00:00:00', true, dateTimeAltFormat);

      const textFieldDateCompWidget = formWithDate.element.querySelector('.formio-component-textField1').querySelector('.flatpickr-input').widget;
      const textFieldDateAltFormat = textFieldDateCompWidget.calendar.config.altFormat;
      textFieldDateCompWidget.calendar.setDate('04-04-2020T00:00:00', true, textFieldDateAltFormat);

      setTimeout(() => {
      const dateTimeCompAltInput = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('.flatpickr-input');
      const textFieldDateCompAltInput = formWithDate.element.querySelector('.formio-component-textField1').querySelector('.flatpickr-input');

      const dateTimeComp = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('[type="text"]');
      const textFieldDateComp = formWithDate.element.querySelector('.formio-component-textField1').querySelector('[type="text"]');

      assert.equal(dateTimeCompAltInput.value,'2020-05-05T00:00:00');
      assert.equal(textFieldDateCompAltInput.value,'2020-04-04T00:00:00');

      assert.equal(dateTimeComp.value,'05-05-2020');
      assert.equal(textFieldDateComp.value,'04-04-2020');

      const panelCollapseBtn = formWithDate.element.querySelector('.formio-collapse-icon');
      panelCollapseBtn.dispatchEvent(clickEvent);

      setTimeout(() => {
        const panelBody = formWithDate.element.querySelector('.panel-body');
        assert.equal(!!panelBody, false);

        formWithDate.element.querySelector('.formio-collapse-icon').dispatchEvent(clickEvent);

        setTimeout(() => {
          const dateTimeCompAfterOpenningPanel = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('[type="text"]');
          const textFieldDateCompAfterOpenningPanel = formWithDate.element.querySelector('.formio-component-textField1').querySelector('[type="text"]');

          const dateTimeCompAltInputAfterOpenningPanel = formWithDate.element.querySelector('.formio-component-dateTime1').querySelector('.flatpickr-input');
          const textFieldDateCompAltInputAfterOpenningPanel = formWithDate.element.querySelector('.formio-component-textField1').querySelector('.flatpickr-input');

          assert.equal(dateTimeCompAltInputAfterOpenningPanel.value,'2020-05-05T00:00:00');
          assert.equal(textFieldDateCompAltInputAfterOpenningPanel.value,'2020-04-04T00:00:00');

          assert.equal(dateTimeCompAfterOpenningPanel.value,'05-05-2020');
          assert.equal(textFieldDateCompAfterOpenningPanel.value,'04-04-2020');
          done();
         }, 250);
       }, 150);
      }, 50);
    }).catch((err) => done(err));
  });

  it(`Should show confirmation alert when clicking X btn or clicking outside modal window after editing
  editGrid modal draft row`, function(done) {
   const formElement = document.createElement('div');
   const formWithNestedDraftModals = new Webform(formElement);

   formWithNestedDraftModals.setForm(formWithEditGridAndNestedDraftModalRow).then(() => {
     const editGrid = formWithNestedDraftModals.getComponent('editGrid');
     const clickEvent = new Event('click');
     const inputEvent = new Event('input');

     const addRowBtn = formWithNestedDraftModals.element.querySelector( '[ref="editgrid-editGrid-addRow"]');
     //click to open row in modal view
     addRowBtn.dispatchEvent(clickEvent);

     setTimeout(() => {
       const rowModal = document.querySelector(`.editgrid-row-modal-${editGrid.id}`);
       //checking if row modal was openned
       assert.equal(!!rowModal, true);

       const textField = rowModal.querySelector('[name="data[textField]"]');
       textField.value = 'test';
       //input value
       textField.dispatchEvent(inputEvent);

       setTimeout(() => {
         //checking if the value was set inside the field
         assert.equal(textField.value, 'test');

         const saveModalBtn = rowModal.querySelector('.btn-primary');
         //clicking save button to save row draft
         saveModalBtn.dispatchEvent(clickEvent);

        setTimeout(() => {
          const editGridRows = formWithNestedDraftModals.element.querySelectorAll('[ref="editgrid-editGrid-row"]');
          //checking if the editGrid row was created
          assert.equal(editGridRows.length, 1);

          const editRowBtn = editGridRows[0].querySelector('.editRow');
          //click the edit btn to open the row again
          editRowBtn.dispatchEvent(clickEvent);

          setTimeout(() => {
            const rowModalForEditing = document.querySelector(`.editgrid-row-modal-${editGrid.id}`);
            const textFieldInputForEditing = rowModalForEditing.querySelector('[name="data[textField]"]');
            textFieldInputForEditing.value = 'changed value';
            //changing textfield value
            textFieldInputForEditing.dispatchEvent(inputEvent);

            setTimeout(() => {
              //checking if the textfield value was changed
              const inputValue = textFieldInputForEditing.value;
              assert.equal(inputValue, 'changed value');

              const XCloseBtn = rowModalForEditing.querySelector('[ref="dialogClose"]');
              //clicking modal close btn
              XCloseBtn.dispatchEvent(clickEvent);

                setTimeout(() => {
                  const dialogConfirmationWindows = document.querySelectorAll(`.editgrid-row-modal-confirmation-${editGrid.id}`);
                  //checking if confirmation dialog is openned
                  assert.equal(dialogConfirmationWindows.length, 1);

                  const dialogCancelBtn = dialogConfirmationWindows[0].querySelector('[ref="dialogCancelButton"]');
                  //closing confirmation dialog
                  dialogCancelBtn.dispatchEvent(clickEvent);

                  setTimeout(() => {
                    const confirmationWindows = document.querySelectorAll(`.editgrid-row-modal-confirmation-${editGrid.id}`);
                    //checking if confirmation dialig is closed
                    assert.equal(confirmationWindows.length, 0);

                    const dialog = document.querySelector(`.editgrid-row-modal-${editGrid.id}`);
                    const overlay = dialog.querySelector('[ref="dialogOverlay"]');
                    //clocking model overlay to open confirmation dialog again
                    overlay.dispatchEvent(clickEvent);

                    setTimeout(() => {
                      const confirmationDialogsAfterClickingOverlay = document.querySelectorAll(`.editgrid-row-modal-confirmation-${editGrid.id}`);
                      assert.equal(confirmationDialogsAfterClickingOverlay.length, 1);

                      document.body.innerHTML = '';
                      done();
                     }, 190);
                   }, 170);
                 }, 150);
              }, 130);
            }, 110);
          }, 100);
        }, 70);
      }, 50);
    }).catch((err) => done(err));
 });

  it('Should not show validation errors when saving invalid draft row in dataGrid', function(done) {
    const formElement = document.createElement('div');
    const formWithDraftModals = new Webform(formElement);

    formWithDraftModals.setForm(formWithEditGridModalDrafts).then(() => {
      const clickEvent = new Event('click');
      const inputEvent = new Event('input');

      const addRowBtn =  formWithDraftModals.element.querySelector( '[ref="editgrid-editGrid-addRow"]');
      //click to open row in modal view
      addRowBtn.dispatchEvent(clickEvent);

      setTimeout(() => {
        const rowModal = document.querySelector('.formio-dialog-content');
        //checking if row modal was openned
        assert.equal(!!rowModal, true);

        const textFieldInput = rowModal.querySelector('[name="data[editGrid][0][textField]"]');
        textFieldInput.value = 'test';
        //input value in one of required row fields
        textFieldInput.dispatchEvent(inputEvent);

        setTimeout(() => {
          //checking if the value was set inside the field
          assert.equal(textFieldInput.value, 'test');

          const saveModalBtn = rowModal.querySelector('.btn-primary');
          //clicking save button to save row draft
          saveModalBtn.dispatchEvent(clickEvent);

          setTimeout(() => {
            const editGridRows = formWithDraftModals.element.querySelectorAll( '[ref="editgrid-editGrid-row"]');
            //checking if the editGrid row was created
            assert.equal(editGridRows.length, 1);
            const rowError = formWithDraftModals.element.querySelector('.editgrid-row-error').textContent.trim();
            const editGridError = formWithDraftModals.element.querySelector('[ref="messageContainer"]').querySelector('.error');

            assert.equal(!!rowError, false);
            assert.equal(!!editGridError, false);

            done();
          }, 200);
        }, 100);
      }, 50);
    }).catch((err) => done(err));
  });

  it('Should show dataGrid rows when viewing submission in dataGrid with initEmpty option', function(done) {
    const formElement = document.createElement('div');
    const formWithDataGridInitEmptyOption = new Webform(formElement);

    formWithDataGridInitEmptyOption.setForm(formWithDataGridInitEmpty.form).then(() => {
      formWithDataGridInitEmptyOption.setSubmission(formWithDataGridInitEmpty.submission2);

      setTimeout(() => {
        const dataGridRows =  formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid-row"]');
        const dataGrid1Rows =  formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid1-row"]');

        assert.equal(dataGrid1Rows.length, 1);
        assert.equal(dataGridRows.length, 1);

        formWithDataGridInitEmptyOption.setSubmission(formWithDataGridInitEmpty.submission3);

        setTimeout(() => {
          const dataGridRows1 =  formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid-row"]');
          const dataGrid1Rows1 =  formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid1-row"]');
          const dataGridSecondRowComponentValue = formWithDataGridInitEmptyOption.element.querySelector('[name = "data[dataGrid][1][textField]"]');
          const dataGrid1FirstRowComponentValue = formWithDataGridInitEmptyOption.element.querySelector('[name = "data[dataGrid1][0][textArea]"]');
          const dataGrid1SecondRowComponentValue = formWithDataGridInitEmptyOption.element.querySelector('[name = "data[dataGrid1][1][number]"]');

          assert.equal(dataGrid1Rows1.length, 2);
          assert.equal(dataGridRows1.length, 2);
          assert.equal(dataGridSecondRowComponentValue.value, 'test2');
          assert.equal(dataGrid1FirstRowComponentValue.textContent, 'test3');
          assert.equal(dataGrid1SecondRowComponentValue.value, 222);

          done();
        }, 300);
      }, 200);
    })
    .catch((err) => done(err));
  });

  it('Should not show dataGrid rows when empty submission is set for dataGrid with initEmpty', function(done) {
    const formElement = document.createElement('div');
    const formWithDataGridInitEmptyOption = new Webform(formElement);

    formWithDataGridInitEmptyOption.setForm(formWithDataGridInitEmpty.form).then(() => {
      formWithDataGridInitEmptyOption.setSubmission(formWithDataGridInitEmpty.submission1);

      setTimeout(() => {
        const dataGridRows =  formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid-row"]');
        const dataGrid1Rows =  formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid1-row"]');

        assert.equal(dataGridRows.length, 0);
        assert.equal(dataGrid1Rows.length, 0);

        formWithDataGridInitEmptyOption.setSubmission({ data: {} });
        setTimeout(() => {
          const dataGridRows1 =  formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid-row"]');
          const dataGrid1Rows1 =  formWithDataGridInitEmptyOption.element.querySelectorAll('[ref = "datagrid-dataGrid1-row"]');

          assert.equal(dataGridRows1.length, 0);
          assert.equal(dataGrid1Rows1.length, 0);

          done();
        }, 300);
      }, 200);
    })
    .catch((err) => done(err));
  });

  it('Should show address submission data inside dataGrid', function(done) {
    const formElement = document.createElement('div');
    const formWithAddress = new Webform(formElement);

    formWithAddress.setForm(formWithAddressComponent.form).then(() => {
      formWithAddress.setSubmission({ data: formWithAddressComponent.submission });

      setTimeout(() => {
        const addressInput = formWithAddress.element.querySelector('[name = "data[dataGrid][0][address]"]');

        assert.equal(addressInput.value, formWithAddressComponent.submission.dataGrid[0].address['formatted_address']);

        done();
      }, 300);
    })
    .catch((err) => done(err));
  });

  it('Should validate field on blur inside panel', function(done) {
    const formElement = document.createElement('div');
    const formWithBlurValidation = new Webform(formElement);

    formWithBlurValidation.setForm(formWithBlurValidationInsidePanel).then(() => {
      const inputEvent = new Event('input');
      const focusEvent = new Event('focus');
      const blurEvent = new Event('blur');
      const fieldWithBlurValidation = formWithBlurValidation.element.querySelector('[name="data[textField]"]');

      fieldWithBlurValidation.dispatchEvent(focusEvent);
        'test'.split('').forEach(character => {
          fieldWithBlurValidation.value = fieldWithBlurValidation.value + character;
          fieldWithBlurValidation.dispatchEvent(inputEvent);
      });

      setTimeout(() => {
        const validationErrorBeforeBlur = formWithBlurValidation.element.querySelector('.error');
        assert.equal(!!validationErrorBeforeBlur, false);
        assert.equal(formWithBlurValidation.data.textField, 'test');

        fieldWithBlurValidation.dispatchEvent(blurEvent);

        setTimeout(() => {
          const validationErrorAfterBlur = formWithBlurValidation.element.querySelector('.error');

          assert.equal(!!validationErrorAfterBlur, true);
          assert.equal(validationErrorAfterBlur.textContent, 'Text Field must have at least 5 characters.');

          done();
        }, 350);
      }, 300);
    })
    .catch((err) => done(err));
  });

  it('Should submit form with empty time field when time field is not required', function(done) {
    const formElement = document.createElement('div');
    const formWithTime = new Webform(formElement);

    formWithTime.setForm(formWithTimeComponent).then(() => {
      const clickEvent = new Event('click');
      const submitBtn = formWithTime.element.querySelector('[name="data[submit]"]');

      submitBtn.dispatchEvent(clickEvent);

      setTimeout(() => {
        assert.equal(formWithTime.errors.length, 0);
        assert.equal(formWithTime.data.submit, true);

        done();
      }, 200);
    })
    .catch((err) => done(err));
  });

  it(`Should show validation errors and update validation errors list when openning and editing edit grid rows
  in draft modal mode after pushing submit btn`, function(done) {
    const formElement = document.createElement('div');
    const formWithDraftModals = new Webform(formElement, { sanitize: true });

    formWithDraftModals.setForm(formWithEditGridModalDrafts).then(() => {
      const clickEvent = new Event('click');
      const inputEvent = new Event('input');

      const addRowBtn = formWithDraftModals.element.querySelector( '[ref="editgrid-editGrid-addRow"]');
      //click to open row in modal view
      addRowBtn.dispatchEvent(clickEvent);

      setTimeout(() => {
        const editGrid = formWithDraftModals.getComponent('editGrid');

        assert.equal(editGrid.editRows.length, 1, 'Should create a row');

        const rowModal = editGrid.editRows[0].dialog;
        //checking if row modal was openned
        assert.equal(!!rowModal, true, 'Should open a modal window');

        const textFieldInput = rowModal.querySelector('[name="data[editGrid][0][textField]"]');
        textFieldInput.value = 'test';
        //input value in one of required row fields
        textFieldInput.dispatchEvent(inputEvent);

        setTimeout(() => {
          //checking if the value was set inside the field
          assert.equal(textFieldInput.value, 'test');

          const saveModalBtn = rowModal.querySelector('.btn-primary');
          //clicking save button to save row draft
          saveModalBtn.dispatchEvent(clickEvent);

          setTimeout(() => {
            const editGridRows = formWithDraftModals.element.querySelectorAll( '[ref="editgrid-editGrid-row"]');
            //checking if the editGrid row was created
            assert.equal(editGridRows.length, 1);

            const submitBtn = formWithDraftModals.element.querySelector('[name="data[submit]"]');
            //pushing submit button to trigger validation
            submitBtn.dispatchEvent(clickEvent);

            setTimeout(() => {
              //checking the number of appeared errors
              assert.equal(formWithDraftModals.errors.length, 2);

              const rowError = formWithDraftModals.element.querySelector('.editgrid-row-error').textContent.trim();
              const editGridError = formWithDraftModals.element.querySelector('[ref="messageContainer"]').querySelector('.error').textContent;
              //checking if right errors were shown in right places
              assert.equal(rowError, 'Invalid row. Please correct it or delete.');
              assert.equal(editGridError, 'Please correct invalid rows before proceeding.');

              const rowEditBtn = editGridRows[0].querySelector('.editRow');
              //open row modal again to check if there are errors
              rowEditBtn.dispatchEvent(clickEvent);

              setTimeout(() => {
                const rowModalAfterValidation = editGrid.editRows[0].dialog;

                const alertWithErrorText = rowModalAfterValidation.querySelector('.alert-danger');
                //checking if alert with errors list appeared inside the modal
                assert.equal(!!alertWithErrorText, true, 'Should show error alert');

                const alertErrorMessages = rowModalAfterValidation.querySelectorAll('[ref="messageRef"]');
                assert.equal(alertErrorMessages.length, 1);

                const numberComponentError = rowModalAfterValidation.querySelector('.formio-component-number').querySelector('.error').textContent;
                //checking if error was shown for empty required field
                assert.equal(numberComponentError, 'Number is required');

                const numberInput = rowModalAfterValidation.querySelector('[name="data[editGrid][0][number]"]');
                numberInput.value = 123;
                //input value to make the field valid
                numberInput.dispatchEvent(inputEvent);

                setTimeout(() => {
                  const rowModalWithValidFields = document.querySelector(`.editgrid-row-modal-${editGrid.id}`);
                  const alertErrorMessagesAfterInputtingValidValues = rowModalWithValidFields.querySelectorAll('[ref="messageRef"]');
                  assert.equal(alertErrorMessagesAfterInputtingValidValues.length, 0);

                  //input values to make all row fields invalid
                  const validNumberInput = rowModalWithValidFields.querySelector('[name="data[editGrid][0][number]"]');
                  validNumberInput.value = null;
                  validNumberInput.dispatchEvent(inputEvent);

                  const validTextInput = rowModalWithValidFields.querySelector('[name="data[editGrid][0][textField]"]');
                  validTextInput.value = '';
                  validTextInput.dispatchEvent(inputEvent);

                  setTimeout(() => {
                    const alertErrorMessagesAfterInputtingInvalidValues = document
                      .querySelector(`.editgrid-row-modal-${editGrid.id}`)
                      .querySelectorAll('[ref="messageRef"]');

                    assert.equal(alertErrorMessagesAfterInputtingInvalidValues.length, 2);
                    document.body.innerHTML = '';

                    done();
                  }, 280);
                }, 240);
              }, 200);
            }, 160);
          }, 120);
        }, 80);
      }, 50);
    }).catch((err) => done(err));
  });

  it('Should not override calculated value', function(done) {
    const formElement = document.createElement('div');
    const formWithCalculatedAmount = new Webform(formElement);

    formWithCalculatedAmount.setForm(formWithCalculatedValueWithoutOverriding).then(() => {
      const inputEvent = new Event('input');

      const amountInput1 = formWithCalculatedAmount.element.querySelector('[name="data[amount1]"]');
      const amountInput2 = formWithCalculatedAmount.element.querySelector('[name="data[amount2]"]');

      amountInput1.value = 6;
      amountInput2.value = 4;

      amountInput1.dispatchEvent(inputEvent);
      amountInput2.dispatchEvent(inputEvent);

      setTimeout(() => {
        const totalAmountInput = formWithCalculatedAmount.element.querySelector('[name="data[currency]"]');
        //checking if the value was calculated correctly
        assert.equal(totalAmountInput.value, '$10.00');

        const inputEvent = new Event('input');
        //trying to override calculated value
        totalAmountInput.value =  55;
        totalAmountInput.dispatchEvent(inputEvent);

        setTimeout(() => {
          const totalAmountInput = formWithCalculatedAmount.element.querySelector('[name="data[currency]"]');
          //checking if the value was overridden
          assert.equal(totalAmountInput.value, '$10.00');

          done();
        }, 400);
      }, 300);
    })
    .catch((err) => done(err));
  });

  it('Should modify calculated value only if it was not manually modified when allowCalculateOverride is true', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement);

    form.setForm(formWithAllowCalculateOverride).then(() => {
      const labelComp = form.getComponent('label');
      const valueComp = form.getComponent('value');

      const inputEvent = new Event('input');
      const labelInput = labelComp.refs.input[0];
      const valueInput = valueComp.refs.input[0];
      labelInput.value = 'Hello';
      labelInput.dispatchEvent(inputEvent);

      setTimeout(() => {
        assert.equal(labelComp.dataValue, 'Hello');
        assert.equal(valueComp.dataValue, 'hello');

        valueInput.value = 'hello123';
        valueInput.dispatchEvent(inputEvent);

        setTimeout(() => {
          assert.equal(valueComp.dataValue, 'hello123');

          labelInput.value = 'HeLLo World';
          labelInput.dispatchEvent(inputEvent);

          setTimeout(() => {
            assert.equal(labelComp.dataValue, 'HeLLo World');
            assert.equal(valueComp.dataValue, 'hello123');
            done();
          }, 500);
        }, 500);
      }, 500);
    }).catch(done);
  });

  it(`Should show field only in container where radio component has 'yes' value when containers contain radio
  components with the same key`, function(done) {
    const formElement = document.createElement('div');
    const formWithCondition = new Webform(formElement);

    formWithCondition.setForm(formWithConditionalLogic).then(() => {
      Harness.clickElement(formWithCondition, formWithCondition.element.querySelector('.formio-component-container1').querySelector('[value="yes"]'));

      setTimeout(() => {
        const conditionalFieldInContainer1 = formWithCondition.element.querySelector('[name="data[container1][textField]"]');
        const conditionalFieldInContainer2 = formWithCondition.element.querySelector('[name="data[container2][textField]"]');

        assert.equal(!!conditionalFieldInContainer1, true);
        assert.equal(!!conditionalFieldInContainer2, false);

        done();
      }, 400);
    })
    .catch((err) => done(err));
  });

  it('Should show only "required field" error when submitting empty required field with pattern validation', function(done) {
    const formElement = document.createElement('div');
    const formWithPattern = new Webform(formElement);

    formWithPattern.setForm(formWithPatternValidation).then(() => {
    Harness.clickElement(formWithPattern, formWithPattern.element.querySelector('[name="data[submit]"]'));

    setTimeout(() => {
      assert.equal(formWithPattern.element.querySelector('.formio-component-textField').querySelectorAll('.error').length, 1);
      assert.equal(formWithPattern.errors[0].messages.length, 1);
      assert.equal(formWithPattern.errors[0].messages[0].message, 'Text Field is required');
      assert.equal(formWithPattern.element.querySelector('[ref="errorRef"]').textContent.trim(), 'Text Field is required');
      done();
    }, 500);
    })
    .catch((err) => done(err));
  });

  it('Should disable field applying advanced logic if dot is used inside component key', function(done) {
    const formElement = document.createElement('div');
    const formWithLogic = new Webform(formElement);

    formWithLogic.setForm(formWithAdvancedLogic).then(() => {
      assert.equal(formWithLogic.components[1].disabled, false);

      Harness.clickElement(formWithLogic, formWithLogic.element.querySelector('[name="data[requestedCovers.HOUSECONTENT_JEWELRY]"]'));

      setTimeout(() => {
        assert.equal(formWithLogic.components[1].disabled, true);
        done();
      }, 500);
    })
    .catch((err) => done(err));
  });

  it('Should only scroll to alerts dialog when submitting an invalid form', function(done) {
    const formJson =  {
      components: [
        {
          'label': 'Number',
          'inputFormat': 'plain',
          'validate': {
            'required': true,
            'max': 10
          },
          'key': 'number',
          'type': 'number',
          'input': true
        },
        {
          label: 'Submit',
          showValidations: false,
          tableView: false,
          key: 'submit',
          type: 'button',
          input: true,
          saveOnEnter: false,
        }
      ]
    };

    const formElement = document.createElement('div');
    const form = new Webform(formElement);
    const scrollIntoView = sinon.spy(form, 'scrollIntoView');

    form.setForm(formJson).then(() => {
      Harness.clickElement(form, form.element.querySelector('[name="data[submit]"]'));

      setTimeout(() => {
        assert.equal(form.errors[0].messages.length, 1);
        assert(scrollIntoView.calledOnceWith(form.root.alert));

        //changes do not trigger scrolling
        const inputEvent = new Event('input');
        const input1 = form.components[0].refs.input[0];

        //invalid input value
        input1.value =  55;
        input1.dispatchEvent(inputEvent);

        setTimeout(() => {
          assert.equal(form.errors[0].messages.length, 1);
          assert.equal(scrollIntoView.callCount, 1);

          //valid input value
          input1.value =  5;
          input1.dispatchEvent(inputEvent);

          setTimeout(() => {
            assert.equal(form.errors.length, 0);
            assert.equal(scrollIntoView.callCount, 1);
            done();
          }, 250);
        }, 250);
      }, 250);
    })
    .catch((err) => done(err));
  });

  let formWithCalculatedValue;

  it('Should calculate the field value after validation errors appeared on submit', function(done) {
    const formElement = document.createElement('div');
    formWithCalculatedValue = new Webform(formElement);
    formWithCalculatedValue.setForm(manualOverride).then(() => {
      Harness.clickElement(formWithCalculatedValue, formWithCalculatedValue.components[2].refs.button);
      setTimeout(() => {
        const inputEvent = new Event('input');
        const input1 = formWithCalculatedValue.components[0].refs.input[0];

          input1.value =  55;
          input1.dispatchEvent(inputEvent);

        setTimeout(() => {
          const input2 = formElement.querySelector('input[name="data[number2]"]');
          assert.equal(input2.value, '55');
          assert.equal(input1.value, 55);
          done();
        }, 250);
      }, 250);
    })
    .catch((err) => done(err));
  });

  it('Should calculate the value when editing set values with possibility of manual override', function(done) {
    const formElement = document.createElement('div');
    formWithCalculatedValue = new Webform(formElement);
    formWithCalculatedValue.setForm(manualOverride).then(() => {
      formWithCalculatedValue.setSubmission({
        data:{
          number1: 66,
          number2:66
        }
      }).then(()=>{
        setTimeout(()=>{
          const input1 = formElement.querySelector('input[name="data[number1]"]');
          const input2 = formElement.querySelector('input[name="data[number2]"]');

          assert.equal(input2.value, '66');
          assert.equal(input1.value, 66);

          const inputEvent = new Event('input');

          input1.value =  `${input1.value}` + '78';
          input1.dispatchEvent(inputEvent);

          setTimeout(() => {
            assert.equal(input2.value, '66');
            assert.equal(input1.value, 6678);
            //set a number as calculated value
            formWithCalculatedValue.components[1].calculatedValue = 6678;
            //change the value
            input1.value =  +(`${input1.value}` + '90');
            input1.dispatchEvent(inputEvent);

            setTimeout(() => {
              assert.equal(input2.value, '66');
              assert.equal(input1.value, 667890);
              done();
            }, 250);
          }, 250);
        }, 900);
      });
    });
  });

  let simpleForm = null;
  it('Should create a simple form', (done) => {
    const formElement = document.createElement('div');
    simpleForm = new Webform(formElement);
    simpleForm.setForm({
      title: 'Simple Form',
      components: [
        {
          type: 'textfield',
          key: 'firstName',
          input: true
        },
        {
          type: 'textfield',
          key: 'lastName',
          input: true
        }
      ]
    }).then(() => {
      Harness.testElements(simpleForm, 'input[type="text"]', 2);
      Harness.testElements(simpleForm, 'input[name="data[firstName]"]', 1);
      Harness.testElements(simpleForm, 'input[name="data[lastName]"]', 1);
      done();
    }).catch(done);
  });

  it('Should set a submission to the form.', () => {
    Harness.testSubmission(simpleForm, { data: {
      firstName: 'Joe',
      lastName: 'Smith'
    } });
  });

  it('Should translate a form from options', done => {
    const formElement = document.createElement('div');
    const translateForm = new Webform(formElement, {
      language: 'es',
      i18n: {
        es: {
          'Default Label': 'Spanish Label'
        }
      }
    });
    translateForm.setForm({
      title: 'Translate Form',
      components: [
        {
          type: 'textfield',
          label: 'Default Label',
          key: 'myfield',
          input: true,
          inputType: 'text',
          validate: {}
        }
      ]
    }).then(() => {
      const label = formElement.querySelector('.col-form-label');
      assert.equal(label.innerHTML.trim(), 'Spanish Label');
      done();
    }).catch((err) => {
      done(err);
    });
  });

  it('Should get the language passed via options', () => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, {
      language: 'es'
    });

    assert.equal(form.language, 'es');
  });

  it('Should translate form errors in alerts', () => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, {
      language: 'es',
      i18n: {
        es: {
          alertMessage: '{{message}}',
          required: '{{field}} es obligatorio'
        }
      }
    });

    return form.setForm({
      components: [
        {
          type: 'textfield',
          label: 'Field Label',
          key: 'myfield',
          input: true,
          inputType: 'text',
          validate: {
            required: true
          }
        }
      ]
    })
      .then(() => form.submit())
      .catch(() => {
        // console.warn('nooo:', error)
      })
      .then(() => {
        const ref = formElement.querySelector('[ref="errorRef"]');
        assert.equal(ref.textContent.trim(), 'Field Label es obligatorio');
      });
  });

  it('Should translate a form after instantiate', done => {
    const formElement = document.createElement('div');
    const translateForm = new Webform(formElement, {
      i18n: {
        es: {
          'Default Label': 'Spanish Label'
        }
      }
    });
    translateForm.setForm({
      title: 'Translate Form',
      components: [
        {
          type: 'textfield',
          label: 'Default Label',
          key: 'myfield',
          input: true,
          inputType: 'text',
          validate: {}
        }
      ]
    }).then(() => {
      translateForm.language = 'es';
      const label = formElement.querySelector('.col-form-label');
      assert.equal(label.innerHTML.trim(), 'Spanish Label');
      done();
    }).catch(done);
  });

  it('Should add a translation after instantiate', done => {
    const formElement = document.createElement('div');
    const translateForm = new Webform(formElement, {
      i18n: {
        language: 'es',
        es: {
          'Default Label': 'Spanish Label'
        },
        fr: {
          'Default Label': 'French Label'
        }
      }
    });
    translateForm.setForm({
      title: 'Translate Form',
      components: [
        {
          type: 'textfield',
          label: 'Default Label',
          key: 'myfield',
          input: true,
          inputType: 'text',
          validate: {}
        }
      ]
    }).then(() => {
      translateForm.language = 'fr';
      const label = formElement.querySelector('.col-form-label');
      assert.equal(label.innerHTML.trim(), 'French Label');
      done();
    }).catch(done);
  });

  it('Should switch a translation after instantiate', done => {
    const formElement = document.createElement('div');
    const translateForm = new Webform(formElement);
    translateForm.setForm({
      title: 'Translate Form',
      components: [
        {
          type: 'textfield',
          label: 'Default Label',
          key: 'myfield',
          input: true,
          inputType: 'text',
          validate: {}
        }
      ]
    }).then(() => {
      translateForm.addLanguage('es', { 'Default Label': 'Spanish Label' }, true);
      const label = formElement.querySelector('.col-form-label');
      assert.equal(label.innerHTML.trim(), 'Spanish Label');
      done();
    }).catch(done);
  });

  it('Should keep translation after redraw', done => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement);
    const schema = {
      title: 'Translate Form',
      components: [
        {
          type: 'textfield',
          label: 'Default Label',
          key: 'myfield',
          input: true,
          inputType: 'text',
          validate: {}
        }
      ]
    };

    try {
      form.setForm(schema)
        .then(() => {
          form.addLanguage('ru', { 'Default Label': 'Russian Label' }, true);
          return form.language = 'ru';
        }, done)
        .then(() => {
          expect(form.options.language).to.equal('ru');
          expect(formElement.querySelector('.col-form-label').innerHTML.trim()).to.equal('Russian Label');
          form.redraw();
          expect(form.options.language).to.equal('ru');
          expect(formElement.querySelector('.col-form-label').innerHTML.trim()).to.equal('Russian Label');
          done();
        }, done)
        .catch(done);
    }
    catch (error) {
      done(error);
    }
  });

  it('Should fire languageChanged event when language is set', done => {
    let isLanguageChangedEventFired = false;
    const formElement = document.createElement('div');
    const form = new Webform(formElement);
    const schema = {
      title: 'Translate Form',
      components: [
        {
          type: 'textfield',
          label: 'Default Label',
          key: 'myfield',
          input: true,
          inputType: 'text',
          validate: {}
        }
      ]
    };

    try {
      form.setForm(schema)
        .then(() => {
          form.addLanguage('ru', { 'Default Label': 'Russian Label' }, false);
          form.on('languageChanged', () => {
            isLanguageChangedEventFired = true;
          });
          return form.language = 'ru';
        }, done)
        .then(() => {
          assert(isLanguageChangedEventFired);
          done();
        }, done)
        .catch(done);
    }
    catch (error) {
      done(error);
    }
  });

  it('When submitted should strip fields with persistent: client-only from submission', done => {
    const formElement = document.createElement('div');
    simpleForm = new Webform(formElement);
    /* eslint-disable quotes */
    simpleForm.setForm({
      title: 'Simple Form',
      components: [
        {
          "label": "Name",
          "allowMultipleMasks": false,
          "showWordCount": false,
          "showCharCount": false,
          "tableView": true,
          "type": "textfield",
          "input": true,
          "key": "name",
          "widget": {
            "type": ""
          }
        },
        {
          "label": "Age",
          "persistent": "client-only",
          "mask": false,
          "tableView": true,
          "type": "number",
          "input": true,
          "key": "age"
        }
      ]
    });
    /* eslint-enable quotes */

    Harness.testSubmission(simpleForm, {
      data: { name: 'noname', age: '1' }
    });

    simpleForm.submit().then((submission) => {
      assert.deepEqual(submission.data, { name: 'noname' });
      done();
    });
  });

  it('Should keep components valid if they are pristine', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en' });
    form.setForm(settingErrors).then(() => {
      const inputEvent = new Event('input', { bubbles: true, cancelable: true });
      const input = form.element.querySelector('input[name="data[textField]"]');
      for (let i = 0; i < 50; i++) {
        input.value += i;
        input.dispatchEvent(inputEvent);
      }

      setTimeout(() => {
        assert.equal(form.errors.length, 0);
        Harness.setInputValue(form, 'data[textField]', '');
        setTimeout(() => {
          assert.equal(form.errors.length, 1);
          done();
        }, 250);
      }, 250);
    }).catch(done);
  });

  it('Should delete value of hidden component if clearOnHide is turned on', function(done) {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en' });
    form.setForm(clearOnHide).then(() => {
      const visibleData = {
        data: {
          visible: 'yes',
          clearOnHideField: 'some text',
          submit: false
        },
        metadata: {}
      };

      const hiddenData = {
        data: {
          visible: 'no',
          submit: false
        }
      };
      const inputEvent = new Event('input', { bubbles: true, cancelable: true });
      const textField = form.element.querySelector('input[name="data[clearOnHideField]"]');
      textField.value = 'some text';
      textField.dispatchEvent(inputEvent);
      this.timeout(1000);
      setTimeout(() => {
        assert.deepEqual(form.data, visibleData.data);
        Harness.setInputValue(form, 'data[visible]', 'no');

        setTimeout(() => {
          assert.deepEqual(form.data, hiddenData.data);
          done();
        }, 250);
      }, 250);
    });
  });

  const formElement = document.createElement('div');
  const checkForErrors = function(form, flags = {}, submission, numErrors, done) {
    form.setSubmission(submission, flags).then(() => {
      setTimeout(() => {
        const errors = formElement.querySelectorAll('.formio-error-wrapper');
        expect(errors.length).to.equal(numErrors);
        expect(form.errors.length).to.equal(numErrors);
        done();
      }, 100);
    }).catch(done);
  };

  //BUG - uncomment once fixed (ticket FIO-6042)
  // it('Should not fire validations when fields are either protected or not persistent.', (done) => {
  //   const form = new Webform(formElement,{ language: 'en' });
  //   form.setForm(
  //     {
  //       title: 'protected and persistent',
  //       components: [
  //         {
  //           type: 'textfield',
  //           label: 'A',
  //           key: 'a',
  //           validate: {
  //             required: true
  //           }
  //         },
  //         {
  //           type: 'textfield',
  //           label: 'B',
  //           key: 'b',
  //           protected: true,
  //           validate: {
  //             required: true
  //           }
  //         }
  //       ],
  //     }).then(() => {
  //       checkForErrors(form, {}, {}, 0, () => {
  //         checkForErrors(form, {}, {
  //           data: {
  //             a: 'Testing',
  //             b: ''
  //           }
  //         }, 1, () => {
  //           checkForErrors(form, {}, {
  //             _id: '123123123',
  //             data: {
  //               a: 'Testing',
  //               b: ''
  //             }
  //           }, 0, done);
  //         });
  //       });
  //   });
  // });

  it('Should not fire validation on init.', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement,{ language: 'en' });
    form.setForm(
      { title: 'noValidation flag',
        components: [{
          label: 'Number',
          validate: {
            required: true,
            min: 5
          },
          key: 'number',
          type: 'number',
          input: true
        }, {
          label: 'Text Area',
          validate: {
            required: true,
            minLength: 10
          },
          key: 'textArea',
          type: 'textarea',
          input: true
        }],
      }).then(() => {
        checkForErrors(form, {}, {}, 0, done);
    });
  });

  it('Should validation on init when alwaysDirty flag is set.', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement, {
      language: 'en',
      alwaysDirty: true
    });
    form.setForm(
      { title: 'noValidation flag',
        components: [{
          label: 'Number',
          validate: {
            required: true,
            min: 5
          },
          key: 'number',
          type: 'number',
          input: true
        }, {
          label: 'Text Area',
          validate: {
            required: true,
            minLength: 10
          },
          key: 'textArea',
          type: 'textarea',
          input: true
        }],
      }).then(() => {
      checkForErrors(form, {}, {}, 2, done);
    });
  });

  it('Should validation on init when dirty flag is set.', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement, {
      language: 'en',
    });
    form.setForm(
      { title: 'noValidation flag',
        components: [{
          label: 'Number',
          validate: {
            required: true,
            min: 5
          },
          key: 'number',
          type: 'number',
          input: true
        }, {
          label: 'Text Area',
          validate: {
            required: true,
            minLength: 10
          },
          key: 'textArea',
          type: 'textarea',
          input: true
        }],
      }).then(() => {
      checkForErrors(form, {
        dirty: true
      }, {}, 2, done);
    });
  });

  it('Should not show any errors on setSubmission when providing an empty data object', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement,{ language: 'en' });
    form.setForm(
      { title: 'noValidation flag',
        components: [{
          label: 'Number',
          validate: {
            required: true,
            min: 5
          },
          key: 'number',
          type: 'number',
          input: true
        }, {
          label: 'Text Area',
          validate: {
            required: true,
            minLength: 10
          },
          key: 'textArea',
          type: 'textarea',
          input: true
        }],
      }
    ).then(() => {
      checkForErrors(form, {}, {}, 0, done);
    });
  });

  it('Should not show errors when providing empty data object with data set.', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement,{ language: 'en' });
    form.setForm(
      { title: 'noValidation flag',
        components: [{
          label: 'Number',
          validate: {
            required: true,
            min: 5
          },
          key: 'number',
          type: 'number',
          input: true
        }, {
          label: 'Text Area',
          validate: {
            required: true,
            minLength: 10
          },
          key: 'textArea',
          type: 'textarea',
          input: true
        }],
      }
    ).then(() => {
      checkForErrors(form, {}, { data: {} }, 0, done);
    });
  });

  it('Should show errors on setSubmission when providing explicit data values.', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement,{ language: 'en' });
    form.setForm(
      { title: 'noValidation flag',
        components: [{
          label: 'Number',
          validate: {
            required: true,
            min: 5
          },
          key: 'number',
          type: 'number',
          input: true
        }, {
          label: 'Text Area',
          validate: {
            required: true,
            minLength: 10
          },
          key: 'textArea',
          type: 'textarea',
          input: true
        }],
      }
    ).then(() => {
      checkForErrors(form, {}, {
        data:{
          number: 2,
          textArea: ''
        }
      }, 2, done);
    });
  });

  it('Should not show errors on setSubmission with noValidate:TRUE', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement,{ language: 'en' });
    form.setForm(
      { title: 'noValidation flag',
        components: [{
          label: 'Number',
          validate: {
            required: true,
            min: 5
          },
          key: 'number',
          type: 'number',
          input: true
        }, {
          label: 'Text Area',
          validate: {
            required: true,
            minLength: 10
          },
          key: 'textArea',
          type: 'textarea',
          input: true
        }],
      }
    ).then(() => {
      checkForErrors(form, {
        noValidate:true
      }, {
        data:{
          number: 2,
          textArea: ''
        }
      }, 0, done);
    });
  });

  it('Should set calculated value correctly', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement);
    form.setForm(calculateZeroValue).then(() => {
      const a = form.components[0];
      const b = form.components[1];
      const sum = form.components[2];

      a.setValue(10);
      b.setValue(5);
      setTimeout(() => {
        assert.equal(a.dataValue, 10);
        assert.equal(b.dataValue, 5);
        assert.equal(sum.dataValue, 15);

        a.setValue('0');
        b.setValue('0');
        setTimeout(() => {
          assert.equal(a.dataValue, 0);
          assert.equal(b.dataValue,0);
          assert.equal(sum.dataValue, 0);

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

  it('Should render Nested Modal Wizard Form correctly', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement);
    form.setForm(nestedModalWizard).then(() => {
      const openModalRef = form.element.querySelector('[ref="openModal"]');
      assert(openModalRef, 'Should render Open Modal button');
      const wizard = form.components[1].subForm;
      wizard.setPage(1);
      setTimeout(() => {
        const openModalRef = form.element.querySelector('[ref="openModal"]');
        assert(openModalRef, 'Should render Open Modal button after the page was changed');
        done();
      }, 250);
    }).catch(done);
  });

  it('Should set calculated value correctly', (done) => {
    formElement.innerHTML = '';
    const form = new Webform(formElement);
    form.setForm(disableSubmitButton).then(() => {
      const textField = form.getComponent(['textField']);
      const fileA = form.getComponent(['upload']);
      const fileB = form.getComponent(['file']);
      const submitButton = form.getComponent(['submit']);
      assert.equal(submitButton.disabled, false, 'Button should be enabled at the beginning');

      const simulateFileUploading = (comp, debounce = 250) => {
        const filePromise = new Promise((resolve) => {
          setTimeout(() => resolve(), debounce);
        });
        filePromise.then(() => comp.emit('fileUploadingEnd', filePromise));
        comp.emit('fileUploadingStart', filePromise);
      };

      simulateFileUploading(fileA, 1000);
      textField.setValue('12345');
      setTimeout(() => {
        assert.equal(submitButton.filesUploading.length, 1);
        assert.equal(submitButton.isDisabledOnInvalid, true, 'Should be disabled on invalid due to the invalid TextField\'s value');
        assert.equal(submitButton.disabled, true, 'Should be disabled');
        simulateFileUploading(fileB, 500);
        setTimeout(() => {
          assert.equal(submitButton.filesUploading.length, 2);
          assert.equal(submitButton.disabled, true, 'Should be disabled');
          setTimeout(() => {
            assert.equal(submitButton.filesUploading.length, 0);
            assert.equal(submitButton.disabled, true, 'Should be disabled since TextField is still invalid');
            textField.setValue('123');
            setTimeout(() => {
              assert.equal(submitButton.disabled, false, 'Should be enabled');
              done();
            }, 250);
          }, 650);
        }, 100);
      }, 250);
    }).catch(done);
  });

  describe('set/get nosubmit', () => {
    it('should set/get nosubmit flag and emit nosubmit event', () => {
      const form = new Webform(null, {});
      const emit = sinon.spy(form, 'emit');
      expect(form.nosubmit).to.be.false;
      form.nosubmit = true;
      expect(form.nosubmit).to.be.true;
      expect(emit.callCount).to.equal(1);
      expect(emit.args[0]).to.deep.equal(['nosubmit', true]);
      form.nosubmit = false;
      expect(form.nosubmit).to.be.false;
      expect(emit.callCount).to.equal(2);
      expect(emit.args[1]).to.deep.equal(['nosubmit', false]);
    });
  });

  describe('getValue and setValue', () => {
    it('should setValue and getValue', (done) => {
      formElement.innerHTML = '';
      const form = new Webform(formElement, { language: 'en' });
      form.setForm({
        components: [
          {
            type: 'textfield',
            key: 'a'
          },
          {
            type: 'container',
            key: 'b',
            components: [
              {
                type: 'datagrid',
                key: 'c',
                components: [
                  {
                    type: 'textfield',
                    key: 'd'
                  },
                  {
                    type: 'textfield',
                    key: 'e'
                  },
                  {
                    type: 'editgrid',
                    key: 'f',
                    components: [
                      {
                        type: 'textfield',
                        key: 'g'
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }).then(() => {
        let count = 0;
        const onChange = form.onChange;
        form.onChange = function(...args) {
          count++;
          return onChange.apply(form, args);
        };

        // Ensure that it says it changes.
        assert.equal(form.setValue({
          a: 'a',
          b: {
            c: [
              { d: 'd1', e: 'e1', f: [{ g: 'g1' }] },
              { d: 'd2', e: 'e2', f: [{ g: 'g2' }] },
            ]
          }
        }), true);

        setTimeout(() => {
          // It should have only updated once.
          assert.equal(count, 1);
          done();
        }, 500);
      });
    });
  });

  describe('ReadOnly Form', () => {
    it('Should apply conditionals when in readOnly mode.', (done) => {
      done = _.once(done);
      const Conditions = require('../test/forms/conditions').default;
      const formElement = document.createElement('div');
      const form = new Webform(formElement, {
        readOnly: true,
        language: 'en'
      });
      form.setForm(Conditions.form).then(() => {
        Harness.testConditionals(form, {
          data: {
            typeShow: 'Show',
            typeMe: 'Me',
            typeThe: 'The',
            typeMonkey: 'Monkey!'
          }
        }, [], (error) => {
          form.destroy();
          if (error) {
            throw new Error(error);
          }
          done();
        });
      });
    });
  });

  describe('Validate onBlur', () => {
    it('Should keep component valid onChange', (done) => {
      formElement.innerHTML = '';
      const form = new Webform(formElement, { language: 'en' });
      form.setForm(validationOnBlur).then(() => {
        const field = form.components[0];
        const field2 = form.components[1];
        const fieldInput = field.refs.input[0];

        Harness.setInputValue(field, 'data[textField]', '12');

        setTimeout(() => {
          assert(!field.error, 'Should be valid while changing');
          const blurEvent = new Event('blur');
          fieldInput.dispatchEvent(blurEvent);

          setTimeout(() => {
            assert(field.error, 'Should set error after component was blurred');
            Harness.setInputValue(field2, 'data[textField1]', 'ab');

            setTimeout(() => {
              assert(field.error, 'Should keep error when editing another component');
              done();
            }, 200);
          }, 200);
        }, 200);
      }).catch(done);
    });

    it('Should keep components inside DataGrid valid onChange', (done) => {
      formElement.innerHTML = '';
      const form = new Webform(formElement, { language: 'en' });
      form.setForm(dataGridOnBlurValidation).then(() => {
        const component = form.components[0];
        Harness.setInputValue(component, 'data[dataGrid][0][textField]', '12');

        setTimeout(() => {
          const textField = component.iteratableRows[0].components.textField;
          assert.equal(!!textField.error, false, 'Should stay valid on input');
          const blur = new Event('blur', { bubbles: true, cancelable: true });
          const input = textField.refs.input[0];
          input.dispatchEvent(blur);
          textField.element.dispatchEvent(blur);
            setTimeout(() => {
              assert(textField.error, 'Should be validated after blur');
              done();
            }, 250);
        }, 250);
      }).catch(done);
    });
  });

  describe('Reset values', () => {
    it('Should reset all values correctly.', () => {
      formElement.innerHTML = '';
      const form = new Webform(formElement, { language: 'en' });
      return form.setForm(
        {
          components: [
            {
              type: 'textfield',
              key: 'firstName',
              label: 'First Name',
              placeholder: 'Enter your first name.',
              input: true,
              tooltip: 'Enter your <strong>First Name</strong>',
              description: 'Enter your <strong>First Name</strong>'
            },
            {
              type: 'textfield',
              key: 'lastName',
              label: 'Last Name',
              placeholder: 'Enter your last name',
              input: true,
              tooltip: 'Enter your <strong>Last Name</strong>',
              description: 'Enter your <strong>Last Name</strong>'
            },
            {
              type: 'select',
              label: 'Favorite Things',
              key: 'favoriteThings',
              placeholder: 'These are a few of your favorite things...',
              data: {
                values: [
                  {
                    value: 'raindropsOnRoses',
                    label: 'Raindrops on roses'
                  },
                  {
                    value: 'whiskersOnKittens',
                    label: 'Whiskers on Kittens'
                  },
                  {
                    value: 'brightCopperKettles',
                    label: 'Bright Copper Kettles'
                  },
                  {
                    value: 'warmWoolenMittens',
                    label: 'Warm Woolen Mittens'
                  }
                ]
              },
              dataSrc: 'values',
              template: '<span>{{ item.label }}</span>',
              multiple: true,
              input: true
            },
            {
              type: 'number',
              key: 'number',
              label: 'Number',
              input: true
            },
            {
              type: 'button',
              action: 'submit',
              label: 'Submit',
              theme: 'primary'
            }
          ]
        }
      ).then(() => {
        form.setSubmission({
          data: {
            firstName: 'Joe',
            lastName: 'Bob',
            favoriteThings: ['whiskersOnKittens', 'warmWoolenMittens'],
            number: 233
          }
        }).then(() => {
          expect(form.submission).to.deep.equal({
            data: {
              firstName: 'Joe',
              lastName: 'Bob',
              favoriteThings: ['whiskersOnKittens', 'warmWoolenMittens'],
              number: 233,
              submit: false
            }
          });
          form.setSubmission({ data: {} }).then(() => {
            expect(form.submission).to.deep.equal({
              data: {
                firstName: '',
                lastName: '',
                favoriteThings: [],
                submit: false
              }
            });
          });
        });
      });
    });
  });

  describe('New Simple Conditions', () => {
    it('Should show field if all conditions are met', function(done) {
      const formElement = document.createElement('div');
      const form = new Webform(formElement);

      form.setForm(formsWithNewSimpleConditions.form1).then(() => {
        const conditionalComponent = form.getComponent('conditionalField');
        assert.equal(conditionalComponent.visible, false, '(1) Component should be conditionally hidden');

        form.setValue({ data: { number: 11, email: 'test@form.io', radio: 'one' } });

        setTimeout(() => {
          assert.equal(conditionalComponent.visible, true, '(2) Component should be conditionally visible');
          const emailComponent = form.getComponent('email');

          emailComponent.setValue('test@form1.io');

          setTimeout(() => {
            assert.equal(conditionalComponent.visible, false, '(3) Component should be conditionally hidden');
            done();
          }, 300);
        }, 300);
      }).catch((err) => done(err));
    });

    it('Should show field if any condition is met', function(done) {
      const formElement = document.createElement('div');
      const form = new Webform(formElement);
      const formCopy = fastCloneDeep(formsWithNewSimpleConditions.form1);
      _.set(formCopy, 'components[0].conditional.conjunction', 'any');

      form.setForm(formCopy).then(() => {
        const conditionalComponent = form.getComponent('conditionalField');
        assert.equal(conditionalComponent.visible, false, '(1) Component should be conditionally hidden');

        form.setValue({ data: { number: 1100, email: 'test@form.io' } });

        setTimeout(() => {
          assert.equal(conditionalComponent.visible, true, '(2) Component should be conditionally visible');
          form.setValue({ data: { number: 10 , email: 'test@form1.io' } });

          setTimeout(() => {
            assert.equal(conditionalComponent.visible, true, '(3) Component should be conditionally visible');
            form.setValue({ data: { number: 10000 } });

            setTimeout(() => {
              assert.equal(conditionalComponent.visible, false, '(4) Component should be conditionally hidden');
              form.setValue({ data: { radio: 'one' } });

              setTimeout(() => {
                assert.equal(conditionalComponent.visible, true, '(5) Component should be conditionally visible');

                done();
              }, 450);
            }, 400);
          }, 350);
        }, 300);
      }).catch((err) => done(err));
    });

    it('Should hide field if any condition is met', function(done) {
      const formElement = document.createElement('div');
      const form = new Webform(formElement);
      const formCopy = fastCloneDeep(formsWithNewSimpleConditions.form1);
      _.set(formCopy, 'components[0].conditional.show', false);
      _.set(formCopy, 'components[0].conditional.conjunction', 'any');

      form.setForm(formCopy).then(() => {
        const conditionalComponent = form.getComponent('conditionalField');
        assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible');

        form.setValue({ data: { number: 1100, email: 'test@form.io' } });

        setTimeout(() => {
          assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden');
          form.setValue({ data: { number: 10 , email: 'test@form1.io' } });

          setTimeout(() => {
            assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden');
            form.setValue({ data: { number: 10000 } });

            setTimeout(() => {
              assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible');
              form.setValue({ data: { radio: 'one' } });

              setTimeout(() => {
                assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden');
                done();
              }, 300);
            }, 300);
          }, 300);
        }, 300);
      }).catch((err) => done(err));
    });

    it('Should hide field if all conditions are met', function(done) {
      const formElement = document.createElement('div');
      const form = new Webform(formElement);
      const formCopy = fastCloneDeep(formsWithNewSimpleConditions.form1);
      _.set(formCopy, 'components[0].conditional.show', false);

      form.setForm(formCopy).then(() => {
        const conditionalComponent = form.getComponent('conditionalField');
        assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible');

        form.setValue({ data: { number: 11, email: 'test@form.io', radio: 'one' } });

        setTimeout(() => {
          assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden');
          const emailComponent = form.getComponent('email');

          emailComponent.setValue('test@form1.io');

          setTimeout(() => {
            assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible');
            done();
          }, 300);
        }, 300);
      }).catch((err) => done(err));
    });

    it('Should show field if all conditions are met (test with different component types + multiple components)', function(done) {
      const formElement = document.createElement('div');
      const form = new Webform(formElement);

      form.setForm(formsWithNewSimpleConditions.form2).then(() => {
        const conditionalComponent = form.getComponent('conditionalField');
        assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden');

        form.setValue({
          data: {
            email: 'test@form.io',
            day: '10/06/2022',
            survey: {
              q1: 'true',
            },
            number: [100, 25, 350],
            checkbox: true,
            selectBoxes: {
              one: true,
              two: false,
              three: false,
              four: false,
              five: false,
            },
            radio: 'two',
            tags: 'test,newtag',
            selectValues: 'one',
            selectCustomWithValuesOfNumberType: 1,
            submit: true,
            currency: 35,
          }
        });

        setTimeout(() => {
          assert.equal(conditionalComponent.visible, true, 'Component should be conditionally visible');
          const dayComponent = form.getComponent('day');

          dayComponent.setValue('8/09/2022');

          setTimeout(() => {
            assert.equal(conditionalComponent.visible, false, 'Component should be conditionally hidden');
            done();
          }, 300);
        }, 300);
      }).catch((err) => done(err));
    });

    it('Should show/hide field inside datagrid rows', function(done) {
      const formElement = document.createElement('div');
      const form = new Webform(formElement);

      form.setForm(formsWithNewSimpleConditions.form4).then(() => {
        const dataGrid = form.getComponent('dataGrid');

        dataGrid.setValue([
          { number: 50 },
          { number: 55 },
          { number: 12 },
          { number: 105 },
        ]);

        setTimeout(() => {
          const expectedValues = {
            '0': true,
            '1': false,
            '2': true,
            '3': false
          };

          _.each(dataGrid.rows, (row, index) => {
            assert.equal(row['textField'].visible, expectedValues[`${index}`], `Component should be conditionally ${expectedValues[`${index}`] ? 'visible': 'hidden'} in row ${index}`);
          });
          done();
        }, 300);
      }).catch((err) => done(err));
    });

    it('Should set component value through logic triggered by simple condition', function(done) {
      const formElement = document.createElement('div');
      const form = new Webform(formElement);

      form.setForm(formsWithNewSimpleConditions.form3).then(() => {
        const componentWithLogic = form.getComponent('fieldWithLogic');
        assert.equal(componentWithLogic.isEmpty(), true, 'Component should be empty');

        form.setValue({
          data: {
            number: 2,
            radio: 'two'
          }
        });

        setTimeout(() => {
          assert.equal(componentWithLogic.dataValue, 'logic works', 'Component should have value set by logic');
          done();
        }, 300);
      }).catch((err) => done(err));
    });

    it('Should show field if all conditions are met (test all operators)', function(done) {
      const formElement = document.createElement('div');
      const form = new Webform(formElement);

      form.setForm(formsWithNewSimpleConditions.form5).then(() => {
        const conditionalComponent = form.getComponent('conditionalField');
        assert.equal(conditionalComponent.visible, false, '(1) Component should be conditionally hidden');

        form.setValue({ data: {
          dateTime: '2022-09-29T12:00:00+03:00',
          day: '09/29/2022',
          dateTime1: '2022-09-29T12:00:00+03:00',
          day1: '09/29/2022',
          url: 'portal.form.io',
          number: 100,
          currency: 100,
          textField1: 'some test text',
          day2: '09/29/2022',
          select: '',
          radio: 'one',
          dateTime3: '2022-09-12T12:00:00+03:00',
          textArea: 'test',
          textField2: 'test2',
          number2: [100],
          currency2: 100,
          email: 'some@form.io',
          url2: 'portal.form.io',
        } });

        setTimeout(() => {
          assert.equal(conditionalComponent.visible, true, '(2) Component should be conditionally visible');
          const selectComponent = form.getComponent('select');

          selectComponent.setValue('one');

          setTimeout(() => {
            assert.equal(conditionalComponent.visible, false, '(3) Component should be conditionally hidden');
            done();
          }, 300);
        }, 300);
      }).catch((err) => done(err));
    });
  });

  describe('Calculate Value with allowed manual override', () => {
    const initialSubmission = {
      data: {
        dataGrid: [
          { label: 'yes', value: 'yes' },
          { label: 'no', value: 'no' },
        ],
        checkbox: false,
        submit: false
      },
      metadata: {}
    };
    const submissionWithOverridenValues = {
      data: {
        dataGrid: [
          { label: 'yes', value: 'y' },
          { label: 'no', value: 'n' },
        ],
        checkbox: false,
        submit: false
      },
      metadata: {}
    };
    const submissionWithOverridenValues2 = {
      data: {
        dataGrid: [
          { label: 'yes2', value: 'y' },
          { label: 'no', value: 'n' },
        ],
        checkbox: false,
        submit: false
      },
      metadata: {}
    };
    it('Should reset all values correctly.', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en' });
      form.setForm(calculateValueWithManualOverride).then(() => {
        const dataGrid = form.getComponent('dataGrid');
        dataGrid.setValue([{ label: 'yes' }, { label: 'no' }]);
        setTimeout(() => {
          expect(form.submission).to.deep.equal(initialSubmission);
          const row1Value = form.getComponent(['dataGrid', 0, 'value']);
          const row2Value = form.getComponent(['dataGrid', 1, 'value']);
          row1Value.setValue('y');
          row2Value.setValue('n');

          setTimeout(() => {
            expect(form.submission).to.deep.equal(submissionWithOverridenValues);
            const row1Label = form.getComponent(['dataGrid', 0, 'label']);
            row1Label.setValue('yes2');
            setTimeout(() => {
              expect(form.submission).to.deep.equal(submissionWithOverridenValues2);
              form.setSubmission(submissionWithOverridenValues).then(() => {
                setTimeout(() => {
                  const tabs = form.getComponent(['tabs']);
                  tabs.setTab(1);
                  setTimeout(() => {
                    expect(form.submission).to.deep.equal(submissionWithOverridenValues);
                    done();
                  }, 250);
                }, 150);
              });
            }, 250);
          }, 250);
        }, 250);
      }).catch(done);
    });

    it('Should apply submission metadata value in calculation.', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en' });
      form.setForm(calculateValueWithSubmissionMetadata).then(() => {
        const textField = form.getComponent('textField');

        textField.setValue('test value');

        form.submit(false, {});

        setTimeout(() => {
          expect(form.submission.metadata).to.exist;
          expect(form.submission.metadata.timezone).to.be.not.empty;
          expect(form.submission.data.textField).to.be.not.empty;
          expect(form.submission.data.textArea).to.be.not.empty;
          expect(form.submission.data.textArea).to.equal(
            form.submission.data.textField + form.submission.metadata.timezone
          );

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

    it('Should allow to change value.', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en' });
      form.setForm(calculatedSelectboxes).then(() => {
        const radio = form.getComponent(['radio']);
        radio.setValue('a');
        setTimeout(() => {
          assert.equal(radio.dataValue, 'a');
          const selectBoxes = form.getComponent(['selectBoxes']);
          assert.equal(selectBoxes.dataValue['a'], true, 'Should calculate value and set it to "a"');
          selectBoxes.setValue({
            'a': true,
            'b': true,
            'c': false
          });
          setTimeout(() => {
            assert.deepEqual(selectBoxes.dataValue, {
              'a': true,
              'b': true,
              'c': false
            }, 'Should change the value');
            done();
          }, 250);
        }, 250);
      }).catch(done);
    });
  });

  describe('Modal Edit', () => {
    const submission = {
      state: 'submitted',
      data: {
        checkbox: true,
        selectBoxes: {
          a: true,
          b: true
        },
        textfield: 'My Text',
        select: 'f',
        submit: true
      }
    };
    const componentsKeys = ['checkbox', 'selectBoxes', 'select', 'textfield'];
    const expectedValues = {
      checkbox: 'Yes',
      selectBoxes: 'a, b',
      select: 'f',
      textfield: 'My Text'
    };
    it('Test rendering previews after the submission is set', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en' });
      form.setForm(modalEditComponents).then(() => {
        return form.setSubmission(submission, { fromSubmission: true }).then(() => {
          componentsKeys.forEach((key) => {
            const comp = form.getComponent([key]);
            assert(comp);
            const preview = comp.componentModal.refs.openModal;
            assert(preview);
            assert.equal(preview.textContent.replace(/\n|\t/g, '').trim(), expectedValues[key]);
          });
          done();
        });
      }).catch(done);
    });

    it('Test updating previews after aboting changes', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en' });
      form.setForm(modalEditComponents).then(() => {
        return form.setSubmission(submission, { fromSubmission: true }).then(() => {
          const comp = form.getComponent(['textfield']);
          comp.componentModal.openModal();
          Harness.dispatchEvent('input', comp.componentModal.refs.modalContents, '[name="data[textfield]"]', (el) => {
            el.value = 'My Text v2';
          });
          setTimeout(() => {
            const fakeEvent = {
              preventDefault: () => {}
            };
            comp.componentModal.closeModalHandler(fakeEvent);
            const preview = comp.componentModal.refs.openModal;
            assert.equal(preview.textContent.replace(/\n|\t/g, '').trim(), 'My Text');
            done();
          }, 100);
        });
      }).catch(done);
    });
  });

  describe('Initially Collapsed Panel', () => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en' });
    form.setForm(initiallyCollapsedPanel).then(() => {
      it('Should be collapsed', (done) => {
        try {
          const panelBody = form.element.querySelector('[ref=nested-panel]');
          assert.equal(panelBody, null, 'Should not render the panel\'s body when initially collapsed');
          done();
        }
        catch (err) {
          done(err);
        }
      });
      it('Should open when an Error occured', (done) => {
        form.executeSubmit().catch(() => {
          try {
            const panelBody = form.element.querySelector('[ref=nested-panel]');
            assert(panelBody, 'Should open the panel when an error occured');
            done();
          }
          catch (err) {
            done(err);
          }
        });
      });
    }).catch((err) => console.error(err));
  });

  describe('Calculate Value', () => {
    it('Should calculate value when set submission if the component is not persistent', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en', pdf: true });
      form.setForm(calculatedNotPersistentValue).then(() => {
        form.setSubmission({
          data:
            {
              a: 'testValue'
            },
          state: 'submitted'
        });
        setTimeout(() => {
          const persistentField = form.getComponent(['a']);
          assert.equal(persistentField.dataValue, 'testValue', 'Should set the value from the submission');
          const notPersistentFieldInput = form.element.querySelector('input[name="data[textField]"]');
          assert.equal(notPersistentFieldInput.value, 'testValue', 'Should calculate the value');
          done();
        }, 550);
      }).catch(done);
    });
    it('Should calculate value by datasouce component when editing mode is on', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en', pdf: true });
      form.setForm(calculateValueInEditingMode).then(() => {
        form.editing = true;
        form.setSubmission({
          data:
            {
              select: { label: 'Dummy #1', value: 'dummy1' },
              dataSourceDisplay: 'some value'
            },
          state: 'submitted'
        })
        .then(() => {
          const dataSource = form.getComponent('datasource');
          dataSource.dataValue = { value: 'some value' };
          form.checkData(null, { dataSourceInitialLoading: true });
          setTimeout(() => {
            const dataSourceDisplay = form.getComponent('dataSourceDisplay');
            assert.equal(dataSourceDisplay.dataValue, 'some value', 'Should set and keep the value');
            done();
          }, 1000);
       });
      }).catch(done);
    });
    it('Should calculate value properly in editing mode', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en', pdf: true });
      form.setForm(calculatedValue).then(() => {
        form.editing = true;
        form.setSubmission({
          data:
            {
              a: 4,
              b: 5,
              total: 9,
            },
          state: 'submitted'
        });
        setTimeout(() => {
          const total = form.getComponent(['total']);
          assert.equal(total.dataValue, 9, 'Should set and keep the value');

          const b = form.getComponent(['b']);
          Harness.dispatchEvent('input', b.element, 'input', (i) => i.value = '6');

          setTimeout(() => {
            assert.equal(total.dataValue, 10, 'Should recalculate the value');
          }, 300);
          done();
        }, 1000);
      }).catch(done);
    });
    it('Should not override value which was set from submission', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en', pdf: true });
      form.setForm(calculateValueWithManualOverrideLableValueDataGrid).then(() => {
        form.editing = true;
        form.setSubmission({
          state: 'submitted',
          data: {
            dataGrid: [
              { label: '1', value: '1a' },
              { label: '2', value: '2a' },
              { label: '3', value: '3a' },
            ]
          },
        });
        setTimeout(() => {
          const value1 = form.getComponent(['dataGrid', 0, 'value']);
          assert.equal(value1.dataValue, '1a', 'Should have a value set from submission');
          const value2 = form.getComponent(['dataGrid', 1, 'value']);
          assert.equal(value2.dataValue, '2a', 'Should have a value set from submission');
          const value3 = form.getComponent(['dataGrid', 2, 'value']);
          assert.equal(value3.dataValue, '3a', 'Should have a value set from submission');
          done();
        }, 1000);
      }).catch(done);
    });
  });

  it('Should set different ids for components inside different Table rows', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en', pdf: true });
    form.setForm(conditionalDataGridWithTableAndRadio).then(() => {
      const radioInspection0 = form.getComponent(['inspectionDataGrid', 0, 'initialExam']);
      Harness.dispatchEvent(
        'click',
        radioInspection0.element,
        'input[value="reject"]',
        i => i.checked = true,
      );

      setTimeout(() => {
        const repairDataGrid0 = form.getComponent(['inspectionDataGrid', 0, 'repairDataGrid']);
        assert.equal(radioInspection0.dataValue, 'reject', 'Should set value');
        assert.equal(repairDataGrid0.visible, true, 'Should become visible');

        const radioRepair0 = form.getComponent(['inspectionDataGrid', 0, 'repairDataGrid', 0, 'repairExam']);
        Harness.dispatchEvent(
          'click',
          radioRepair0.element,
          'input[value="accept"]',
            i => i.checked = true,
        );

        setTimeout(() => {
          assert.equal(radioRepair0.dataValue, 'accept', 'Should set value');
          const inspectionDataGrid = form.getComponent(['inspectionDataGrid']);
          inspectionDataGrid.addRow();

          setTimeout(() => {
            assert.equal(inspectionDataGrid.rows.length, 2, 'Should add a row');

            const radioInspection1 = form.getComponent(['inspectionDataGrid', 1, 'initialExam']);
            Harness.dispatchEvent(
              'click',
              radioInspection1.element,
              'input[value="reject"]',
              i => i.checked = true,
            );

            setTimeout(() => {
              const repairDataGrid1 = form.getComponent(['inspectionDataGrid', 1, 'repairDataGrid']);
              assert.equal(radioInspection1.dataValue, 'reject', 'Should set value');
              assert.equal(repairDataGrid1.visible, true, 'Should become visible');

              const radioRepair1 = form.getComponent(['inspectionDataGrid', 1, 'repairDataGrid', 0, 'repairExam']);
              Harness.dispatchEvent(
                'click',
                form.element,
                form.element.querySelector(`#${radioRepair1.root.id}-${radioRepair1.id}-${radioRepair1.row}-accept`),
                i => i.checked = true
              );

              setTimeout(() => {
                assert.equal(radioRepair1.dataValue, 'accept', 'Should set value of the clicked radio');
                assert.equal(radioRepair0.dataValue, 'accept', 'Value of the radio inside another row should stay the same');

                done();
              }, 300);
            }, 350);
          }, 300);
        }, 250);
      }, 350);
    }).catch(done);
  });

  it('Should render components properly', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en' });
    form.setForm(multipleTextareaInsideConditionalComponent).then(() => {
      form.setSubmission({
        data: {
          textArea2: [
            'test'
          ],
          didAnyBehavioralIssuesOccurOnYourShift: 'yes',
          submit: false,
        }
      });
      setTimeout(() => {
        const textarea = form.getComponent(['textArea2']);
        const panel = form.getComponent(['behavioralIssues']);
        assert.equal(panel.visible, true, 'Should be visible');
        assert.deepEqual(textarea.dataValue, ['test'], 'Should set the value from the submission');
        const inputRows = textarea.element.querySelectorAll('[ref="input"]');
        assert.equal(inputRows.length, 1, 'Should render all the rows of the Textarea');
        done();
      }, 750);
    }).catch(done);
  });

  it('Should disable all the components inside Nested Form if it is disabled', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en' });
    form.setForm(disabledNestedForm).then(() => {
      assert.equal(form.components[0].disabled, false, 'Component that is outside of disabled Nested Form should be editable');
      const subFormComponents = form.components[1].subForm.components;
      assert.deepEqual([subFormComponents[0].disabled, subFormComponents[1].disabled], [true, true], 'Components that are inside of disabled Nested Form should be disabled');
      done();
    }).catch(done);
  });

  it('Should restore value correctly if NestedForm is saved as reference', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en' });
    form.setForm(nestedFormInsideDataGrid).then(() => {
      const nestedForm = form.getComponent(['dataGrid', 0, 'form1']);
      const submissionWithIdOnly = { _id: '1232', data: {} };
      nestedForm.dataValue = { ...submissionWithIdOnly };
      nestedForm.restoreValue();

      setTimeout(() => {
        assert.deepEqual(nestedForm.dataValue, submissionWithIdOnly, 'Should not set to defaultValue after restore');
        done();
      }, 350);
    }).catch(done);
  });

  it('Should not set the default value if there is only Radio with False value', (done) => {
    const formElement = document.createElement('div');
    Formio.createForm(formElement, nestedFormInWizard).then((form) => {
      const nestedForm = form.getComponent(['form']);
      const submission = {
        data: {
          radio: false
        }
      };

      nestedForm.dataValue = { ...submission };

      setTimeout(() => {
        assert.deepEqual(nestedForm.dataValue, submission, 'Should set submission');
        nestedForm.valueChanged = true;
        form.setPage(1);

        setTimeout(() => {
          assert.deepEqual(nestedForm.dataValue.data, submission.data, 'Should not set to defaultValue after restore');
          done();
        }, 300);
      }, 300);
    }).catch(done);
  });

  it('Should add and clear input error classes correctly', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en' });

    form.setForm(UpdateErrorClassesWidgets).then(() => {
      const checkbox = form.getComponent('showDate');
      checkbox.setValue(true);
      setTimeout(() => {
        const dateTimeComponent = form.getComponent('condtionalDate');
        const dateComponentElement = dateTimeComponent.element;
        assert.equal(!dateComponentElement.className.includes('formio-hidden'), true, 'Should not be hidden');

        form.submit();

        setTimeout(() => {
          const dateVisibleInput = dateComponentElement.querySelector('.input');
          const flatpickerInput = dateComponentElement.querySelector('.flatpickr-input');

          assert(dateVisibleInput.className.includes('is-invalid'), 'Visible field should have invalid class');
          assert(flatpickerInput.className.includes('is-invalid'), 'Flatpickr field should have invalid class as well');

          dateTimeComponent.setValue('2020-12-09T00:00:00');

          setTimeout(() => {
            assert.equal(dateTimeComponent.dataValue, '2020-12-09T00:00:00', 'Should set value');
            assert(!dateVisibleInput.className.includes('is-invalid'), 'Invalid class should be removed');
            assert(!flatpickerInput.className.includes('is-invalid'), 'Invalid class should be removed from flatpickr field as well');

            checkbox.setValue(false);

            setTimeout(() => {
              const dateComponentElement = dateTimeComponent.element;
              assert.equal(dateComponentElement.className.includes('formio-hidden'), true, 'Should be hidden');
              checkbox.setValue(true);

              setTimeout(() => {
                const dateComponentElement = dateTimeComponent.element;
                assert.equal(!dateComponentElement.className.includes('formio-hidden'), true, 'Should be visible');
                const dateVisibleInput = dateComponentElement.querySelector('.input:not([type="hidden"])');
                const flatpickerInput = dateComponentElement.querySelector('.flatpickr-input');

                assert(dateVisibleInput.className.includes('is-invalid'), 'Visible field should has invalid class');
                assert(flatpickerInput.className.includes('is-invalid'), 'Flatpickr field should has invalid class as well');

                dateTimeComponent.setValue('2020-10-19T00:00:00');
                setTimeout(() => {
                  assert.equal(dateTimeComponent.dataValue, '2020-10-19T00:00:00', 'Should set value');
                  assert(!dateVisibleInput.className.includes('is-invalid'), 'Invalid class should be removed');
                  assert(!flatpickerInput.className.includes('is-invalid'), 'Invalid class should be removed from flatpickr field as well');
                  done();
                }, 300);
              }, 400);
            }, 300);
          }, 300);
        }, 300);
      }, 350);
    }).catch(done);
  }).timeout(3000);

  it('Should have number and currency fields in empty form submission', function(done) {
    const formElement = document.createElement('div');
    const form= new Webform(formElement);
    const formJson = {
      components: [
        {
          label: 'Number',
          key: 'number',
          type: 'number'
        },
        {
          label: 'Currency',
          key: 'currency',
          type: 'currency'
        },
        {
          type: 'button',
          label: 'Submit',
          key: 'submit'
        },
      ],
    };

    const emptySubmissionData = {
      submit: true
    };

    form.setForm(formJson).then(() => {
      const clickEvent = new Event('click');
      const submitBtn = form.element.querySelector('[name="data[submit]"]');

      submitBtn.dispatchEvent(clickEvent);

      setTimeout(() => {
        assert.deepEqual(form.data, emptySubmissionData);
        done();
      }, 400);
    })
    .catch((err) => done(err));
  });

  it('Test Truncate Multiple Spaces', (done) => {
    const formElement = document.createElement('div');
    const form= new Webform(formElement);

    form.setForm(truncateMultipleSpaces).then(() => {
      const textFieldRequired = form.getComponent(['textField1']);
      const textFieldMinMaxLength = form.getComponent(['textField']);
      const textAreaMinMaxLength = form.getComponent(['textArea']);
      Harness.dispatchEvent('input', textFieldRequired.element, 'input', (i) => i.value = '        ');
      Harness.dispatchEvent(
        'input',
        textFieldMinMaxLength.element,
        'input',
        (i) => i.value = '     546       456     '
      );
      Harness.dispatchEvent(
        'input',
        textAreaMinMaxLength.element,
        'textarea',
        (i) => i.value = '     546       456     '
      );

      setTimeout(() => {
        assert.equal(textFieldRequired.dataValue, '        ', 'Should set value');
        assert.equal(textFieldMinMaxLength.dataValue, '     546       456     ', 'Should set value');
        assert.equal(textAreaMinMaxLength.dataValue, '     546       456     ', 'Should set value');

        assert.equal(textFieldRequired.errors.length, 1, 'Should be invalid since it does not have a value');
        assert.equal(
          textFieldMinMaxLength.errors.length,
          0,
          'Should be valid since it value does not exceed the max length after truncating spaces'
        );
        assert.equal(
          textAreaMinMaxLength.errors.length,
          0,
          'Should be valid since it value does not exceed the max length after truncating spaces'
        );

        form.submit(false, {}).finally(() => {
          assert.equal(textFieldRequired.dataValue, '', 'Should truncate the value before submit');
          assert.equal(textFieldMinMaxLength.dataValue, '546 456', 'Should truncate the value before submit');
          assert.equal(textAreaMinMaxLength.dataValue, '546 456', 'Should truncate the value before submit');

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

  it('HTML render mode for Webform', (done) => {
    const element = document.createElement('div');

    const originalMakeRequest = Formio.makeRequest;
    Formio.makeRequest = function() {
      return new Promise(resolve => {
        setTimeout(() => {
          const values = [
            {
              _id: '5a53f8a044398b0001023eab',
              modified: '2019-02-01T16:12:06.618Z',
              data: {
                firstName: 'Bob',
                lastName: 'Thompson',
                status: 'inactive',
                email: 'bob@example.com',
                submit: true,
              },
              form: '5a53f887c8930000010f8b22',
              _fvid: 0,
              _vid: 0,
              created: '2018-01-08T23:02:56.484Z',
              externalIds: [],
              access: [],
              roles: [],
              owner: '553dbfc08d22d5cb1a7024f2',
              state: 'submitted',
              project: '5692b91fd1028f01000407e3',
            }, {
              _id: '5a53f8ad0dc919000194ab6b',
              modified: '2019-02-01T16:12:01.781Z',
              data: {
                firstName: 'Sally',
                lastName: 'Tanner',
                status: 'active',
                email: 'sally@example.com',
                submit: true,
              },
              form: '5a53f887c8930000010f8b22',
              _fvid: 0,
              _vid: 0,
              created: '2018-01-08T23:03:09.730Z',
              externalIds: [],
              access: [],
              roles: [],
              owner: '553dbfc08d22d5cb1a7024f2',
              state: 'submitted',
              project: '5692b91fd1028f01000407e3',
            }, {
              _id: '5a53f8b744398b0001023eaf',
              modified: '2019-02-01T16:11:57.139Z',
              data: {
                firstName: 'Jane',
                lastName: 'Doe',
                status: 'active',
                email: 'jane@example.com',
                submit: true,
              },
              form: '5a53f887c8930000010f8b22',
              _fvid: 0,
              _vid: 0,
              created: '2018-01-08T23:03:19.473Z',
              externalIds: [],
              access: [],
              roles: [],
              owner: '553dbfc08d22d5cb1a7024f2',
              state: 'submitted',
              project: '5692b91fd1028f01000407e3',
            },
          ];
          resolve(values);
        }, 50);
      });
    };

    Formio.createForm(element, htmlRenderMode, {
      readOnly: true,
      renderMode: 'html',
    })
      .then(form => {
        form.submission = {
          data: {
            textfieldonPage3: 'test',
            signature: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABTIAAACWCAYAAADt5XcLAAAgAElEQVR4Xu3dfWydV30H8NM1dLRdndIOKKJxGHSh0MalgBilpLAJyIsatQgJu4rT8peNiFOEhl0pyR8gxZHq/DGJJAhHCNbEEU5BHVWyJpWYuja0tOPdSctLQVBHWmAwkTi0lEFh+j3h3tqOG9vJvfa59/kcybKT3Ps85/mcE0f+5nfOOe/ZZ3/156QRIECAAAECBAgQIECAAAECBAgQIEAgY4HzBJkZj46uESBAgAABAgQIECBAgAABAgQIECBQCAgyTQQCBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIO1Fjhx4kQaGTlSXHbhwoXFx6WXthSfNQIECBAgQIAAAQIECBAgQIAAgTwFBJl5jotezUIggslnnjmaTpwYS/F1fBw/furrZ54Z/cufnfr9eN2Z2uLFi1Jb29J06vO16d3vvrH4WiNAgAABAgQIECBAgAABAgQIEJhfAUHm/Pq7+ywFIoj8+tcfTbt3D6fR0VMhZT3bggUL0sUXX1yEmjfddGO6+eaVxdcaAQIECBAgQIAAAQIECBAgQIDA3AoIMufW293OUmDfvgfSF7+4Oz3++DeLysrZtNbWRdWqymXLbizeGsvIr7tuYiAZoWhUcEY7dOjRdPz4ifT00z9Nzz///ITbRYXmunUfTevWdc2mG15LgAABAgQIECBAgAABAgQIECBwDgKCzHPA89b6CkSwuGfPcBoa+lJReblkyVVpyZK/T6tXr0oRSFaWfFf2u4zeRMjZ2tpa0z0v494RbEaYun//gepDR2Xm4OA2FZr1nQauToAAAQIECBAgQIAAAQIECBAoBASZJkJWAhFERlgYS8cjPIzW0tKSenq6U2fnbfO+X2WEmhGsbtmytehbVHbu3XtPEaxqBAgQIECAAAECBAgQIECAAAEC9RMQZNbP1pVnIRAB4ZYtA2nfvgNFVWWEl7H0O8LLzs6OWVxpbl4aIWtX1/o0Onpqj84NG3rTxo19c3NzdyFAgAABAgQIECBAgAABAgQIlFBAkFnCQc/pkYeGhtOOHYOpsjw8Asy1azuKADP3Q3UicF2+/JZ0+PCTBenVVy9J9903PO9VozmNr74QIECAAAECBAgQIECAAAECBGolIMislaTrzFggAsAdO3ZW976MN8aBPFHRmGP15ZkeLJ6lt3dTsZdnpe3cua043TyWnWsECBAgQIAAAQIECBAgQIAAAQK1ERBk1sbRVWYgUAkwt28frJ48vmzZu4oAs9H3mPzCF3al9ev/uaoQBxLdffdm1ZkzmBdeQoAAAQIECBAgQIAAAQIECBCYiYAgcyZKXnPOAv39A0UVZoSZ0das6SgCzMrJ4+d8gwwuEPt8trevrS41j2fbsKHxqkwzoNQFAgQIECBAgAABAgQIECBAgMBpAoJMk6KuAvv2PZDuumtTipCvWQPMyYAR2kbV6djYWPFHsVx+YGCzpeZ1nWkuToAAAQIECBAgQIAAAQIECDS7gCCz2Ud4np4vgsvu7vUpTveOFntGDgz0N1UF5ploJ59qHtWZw8O7sj/AaJ6mi9sSIECAAAECBAgQIECAAAECBKYVEGROS+QFsxGIpeNbtmwtKhKjNcsemLMxqLx28kFAcfhPT0932rCh92wu5z0ECBAgQIAAAQIECBAgQIAAgVILCDJLPfy1ffioQowqzKjGXLr0mtTT89GGO4W8tiKnrhahbiw3ryw1dxBQPZRdkwABAgQIECBAgAABAgQIEGh2AUFms4/wHDzfyMiRIqjbv/9Acbc4rXvt2g57Qo6zD6Ourp4JBwEdOHB/aZbaz8E0dAsCBAgQIECAAAECBAgQIECgyQUEmU0+wPV8vPHLyK+88rXpLW9pK9U+mLO1Da+urvXVwDeWmg8OfiZFhaZGgAABAgQIECBAgAABAgQIECBwZgFBphlyVgKxXHrPnuEUlYatrYuKU7kFcjOjHL/U3L6ZMzPzKgIECBAgQIAAAQIECBAgQICAINMcmJVABJexD2Z8jrZmTUfaunWzZeSzUkzFae5RnTk6erR4Z2dnRxoc3DbLq3g5AQIECBAgQIAAAQIECBAgQKA8AoLM8oz1OT9pBG9RhRktqjB37tyWli278ZyvW9YLxKFI3d096dChxwqCsNy79x6hcFknhOcmQIAAAQIECBAgQIAAAQIEziggyDRBphXYt++B1N19Z4o9HqOtW9eVNm7sE7hNKzf9C8I0lppv2bK1ePHixYuSQ4Cmd/MKAgQIECBAgAABAgQIECBAoHwCgszyjfmMnzhCtr6+TWloSBXmjNHO8oURFnd03FENM+Pkd3uOniWmtxEgQIAAAQIECBAgQIAAAQJNKSDIbMphPfeHij0c29vvUIV57pQzvkIsNW9vX5sOH36yqHatVL7O+AJeSIAAAQIECBAgQIAAAQIECBBoYgFBZhMP7tk+Wix1vuuuTcXb7YV5topn976ogu3t3VTdizQOU4q9SDUCBAgQIECAAAECBAgQIECAQNkFBJllnwHjnj9CtNgLM5Y5R1u27F1pcHB7sW+jNrcClaXmL3/5y9OSJVelgwe/ak/SuR0CdyNAgAABAgQIECBAgAABAgQyExBkZjYg89WdCDFXrLg1jYwcKbrgQJ/5GokX7xtjsnz5LcVS82gRZjolfv7HRQ8IECBAgAABAgQIECBAgACB+REQZM6Pe1Z3jf0wu7vXp9ijsaWlJfX0dBenkmt5CPT1bUw7duwsOhPjsmFDbx4d0wsCBAgQIECAAAECBAgQIECAwBwKCDLnEDvHW8US5lhOHtV/EWLee+8uVX8ZDlScHN/buzGNjY0V4xPVmRoBAgQIECBAgAABAgQIECBAoEwCgswyjfakZx1/qM/SpdeknTu3p7a2a0sskvejR+VsV9f6NDp6tNi3dHh4l/HKe8j0jgABAgQIECBAgAABAgQIEKihgCCzhpiNdKn+/oG0ZcvWoss337yyOBl74cKFjfQIpezr+H0zY7wGBz+TVq9eVUoLD02AAAECBAgQIECAAAECBAiUS0CQWa7xLp62vf32tH//geLrONRnYKC/hAqN+8gRZsYYHjr0WPEQ9s1s3LHUcwIECBAgQIAAAQIECBAgQGDmAoLMmVs1xSvf+c73Vk/BvvvuzcXBPlpjCoyvqo2qzKjOVFXbmGOp1wQI1E4gDq6LLTiitbVd4/ti7WhdiQABAgQIECBAgMC8Cwgy530I5qYDIyNH0gc+sDqdPPnb4oZxWEwcGqM1tkAc1tTRcUfxEDGeg4Pbiv0zNQIECJRJIMLLT32qPz300MPpV7/69YRHj72f43ujPaDLNCM8KwECBAgQIECAQLMKCDKbdWTHPVflUJ/zzjsvXXLJJemHP/yOCpUmGvcIqWOpuUOAmmhQPQoBAjMSiADzs58dTLt3D6fYduOl2po1HcVe0BoBAgQIECBAgAABAo0tIMhs7PE7Y+/jh7ru7jtTVO1Fa2lpSceO/bSJn7i8jzZ538yoPurs7CgviCcnQKDpBQ4dejR1d69PEWZO1+wHPZ2QPydAgAABAgQIECDQGAKCzMYYp1n3Mn6wW7nyluoPeELMWRM25Bv6+jamHTt2Fn2PIHPDhj5LzRtyJHWaAIEzCZwpxHzFKy5Nq1atSKOjo+n48ROprW1pcSiabTfMKQIECBAgQIAAAQKNLyDIbPwxPO0JJv+A19q6KD3++EOWkzfhWE/1SFGB29W1Po2NjRV7wg0MbLYfaknG3mMSKINA/BvX3n7HaUvJly17VxFY2v+5DLPAMxIgQIAAAQIECJRVQJDZZCM//vCXeLQIMQ8evF8lSpON83SPExW5UZ25f/+BIsCOZZXxA75GgACBRhaIEPPDH769+I+a8e3uuzennp7uRn40fSdAgAABAgQIECBAYAYCgswZIDXKS4aGhov9wsa3p576jhCzUQawDv2szImWlkvS6163OA0P7zIf6uDskgQI1F9g8pYpccfYNiUO8Vm9elX9O+AOBAgQIECAAAECBAjMu4Agc96HoDYdmBxixg939967yxK72vA29FXih//29rXp8OEni+eIH/pvvnmlrQYaelR1nkD5BGLLjD17hqsPHv9Bc++9u/07V76p4IkJECBAgAABAgRKLCDIbILBj6V2K1bcOuFJhofvUaHSBGNby0fo7x9IEXiPjh4tfvCPk80dflFLYdciQKBeApP/nfOfdfWSdl0CBAgQIECAAAECeQsIMvMen2l7N9Vy8g0beu2HOK1cOV8QYUBv78ZqdWbsmxnzRSNAgECuAlMtKbcnZq6jpV8ECBAgQIAAAQIE6isgyKyvb12vHgf7dHffOeHk1jjUZWCgv673dfHGFjhx4kTavn0wbdmytXiQqMqM6kwn/Tb2uOo9gWYUGBk5kvr6NqX4T5hKi9PJ4xA7jQABAgQIECBAgACB8gkIMht0zOOHu46O21NUqlRa7HsY+x/GKdUagekEYg51dfVUqzM7OzvSwMBm82c6OH9OgMCcCER4edttH0m/+c3x6v1aWxcVIaZtMeZkCNyEAAECBAgQIECAQHYCgszshmT6Dk21zG7p0mvSgw/eL4Sans8rJgnE3pmV6swIwbdu3ZzWrOngVAOBqH49fnysuNKll7b4+1kD02a9RMyVkZEniwr7+Dh27H/SsWP/nS6//PLi18ePx++fmkvRIsiLv6/PP//79Oyzvy1eN1274IK/Tn/84x/SokWvLeZlXHeqFtdM6c/p1a9+9YTXxOvj35+FC1uKt1144YVpwYIF6frr24pfL116bWpru3a6bkz757HaYM+evSk+j2/x79zWrf2qx6cV9AICBAgQIECAAAECzSsgyGywsZ0qxIwKlccff0hI0mBjmVN3Y151d/ekQ4ceK7oVy8w3buwVGMxgkKKy9Yknvpl+/vPRIiT6/vePpPi9lwqJIuiJACqCqMWLW6tfv+xlL0sXXXRR9Y6VQKsShL7461OBVgRbU7W47qWXLqxeN+4VwZOtA2YwmHPwkpgbP/jBj9LTT/8knTx5spgv45dNz0EX6n6LG274h/Tcc8+lWAIebfwqgZjHv//9/6Wf/ezn6eqrlxR/Fh8jI4fTj370dPrxj38y5d+deO0XvzhYk6C07gBuQIAAAQIECBAgQIBA3QQEmXWjrf2Fp1pOHiHm3r27/HBXe+5SXjEqoKJC8/DhJ4vnX716VYpDNSzjnDgdIoyJKtbwGr+9Q86T5oILLigq6KIytLW1NV133bXF5/h1rSrpcn7++ehbfM+O/xyIkC6+jg9t9gIRiMa/c7ZNmb2ddxAgQIAAAQIECBBoNgFBZoOM6FQHHrS0tKTHH/9PIVODjGEjdTMOA4pAc2xsrAgPenq6UxwkJUhIaWhoOHV3r5/X4Yy/+1F1OT5gnjw24ytCo+rvD3/4Q/rd7353xn5HtWh83HTTjend777R95ZZjHJ4P/bYE+nb3/5uOnLkqdOWRc/kUrF0OsY12hvfuKT4+3fVVW8441tPLS1/Nl1++WXT3uLFpeVX/mWp+tRVvfG6kyfH0qte9arimvFslfl1qtq3pagKPnbsl0UV8p/+9KeiqjRC/dHRF/dtnrZD07wgAsx167qL/1DRCBAgQIAAAQIECBAgEAKCzAaYB/EDYn//1gnLDx140AAD1+BdjPCit3dT2rNnuHiSsp9uHh7d3XdOGVBdcsklacmSq9KHP/yhwinCnsoS8so0qASLk6vyKkvEo2LyoosunDBrKuFRfK7VHpuVZe8ROlUqBaMPlSrc8R2IZ4kQKZalR6gkyH5RJ/z27z9QfF8Ox9lU5t5wwzvSxRf/TXr/+/+pWhnbLFXPMb8efPA/0hNP/Fe67LLL0ujo6GnfCa+44op07NixdN11S4sq1bD79a//Ny1YcH56+9vflpYvf18x75rFpMH/KdB9AgQIECBAgAABAlkJCDKzGo7TOxOVcZ/+dH967rkXK6miamfnzu2Wk2c+ds3SvVPVwBur+2eWcbn5VBXRMb4R7m3c2NcU+09G0PrIIxHKndqzsbJf6vh5XKnWLGuwefTo0RTfkytOM/k7Ht+v29qWFlWulYrXmbzPawgQIECAAAECBAgQIEDgdAFBZqazIoKEvr5Np+2p5tTWTAesBN2KJdXbt3+uqNyLSqkNG/pSZ2fzn24ewd6qVR9Mv/nN8eoox9LuCDBjyX0zt/g+FKFd7AU6VcVmJZiLoK6t7ZqmCHSnGs+YA5U9Uacb77e97fqiMjf2IHXA0nRa/pwAAQIECBAgQIAAAQKzExBkzs6r7q+uHCISVT+T2ytecWl64IF/U4lZ91Fwg5cSiPkZczNCnZaWS9LrXrc4DQ/vatoloBFgrVhx64RTlMtaET25YjP23Yw9HMe317zmivTKV/5tsQQ9KhDjc4SdsRVGIy4Tni7AjOeKsHL16pXFCfTxrBoBAgQIECBAgAABAgQI1E9AkFk/21lfOYKCCE1e6mTbgwe/qsJn1qreUA+B2NOuvX1ttUpvYKA/dXa2N90eim9601snHF7y+tf/Xdqz5wsCq79MqvH7bEb15i9+8cv09NM/fckpF2FmnJQeLSoWK3tuxu/H16eCz2uKP5+v/TjjoJ6HH/56Ghr60pTfi2M/1Ntvvy11dt5mHtTjm4trEiBAgAABAgQIECBA4AwCgsyMpsfk0GR81+6+e3PTL2PNaCh0ZYYCsdw8TjePk4qjGm1gYHPThO2TTyePSsy9e3c3ZGXhDIezZi+rnGD9zDOjfznMZbQ40GVyBeeZblip5ozXRNAZFY+VryuhZ/z6/PP/Kr3wwgtxdl1xmnblYKTxYWjloKXjx8eK6tr4OP/889MLL/yp+LpyANKZ9r6M6suY307Qrtk0cSECBAgQIECAAAECBAjMWkCQOWuy+rzhNa95w0v+kD84uK0UexHWR9ZV6y0QAVV3d0/1cJjYO3LDht5637bu13/nO987YV/Ib3zjIRV4NVAff2p6BJ3RKr/3ve8dTidPnqzBXWp3iTjQKaovy7AfbO3UXIkAAQIECBAgQIAAAQL1ERBk1sd1VleNgzQ6Ou447T1xoMi99+5qmgq3WaF4ccMJxN6Zd921qeh3VGc28t6Zk/9OrlnTkXbu3NZwY9KIHY4KyaicHB09VcUZYWd8jl8fP35i1pWdszV485uvThdeeGFxYE9UXzbi3p6zfWavJ0CAAAECBAgQIECAQKMICDIzGKmpgswIMR988H4VYBmMjy7MXCAq69rbby+WmscS36jOXLeua+YXyOSV73nP8vStb32n2hv702YyMOO6EYFnBJzRToWfsWR8rPhc+b3JvR6/J+cFF1yQLrrowmKexvvHL2XP72n1iAABAgQIECBAgAABAgRCQJCZyTwYv7Q8Qsxjx176wIxMuqwbBKYUiFCoq2t92r//QPHncapz7C3YKCc6Rzj25je/tfpsN9zwjvS1r/270SZAgAABAgQIECBAgAABAgTmWUCQOc8DMP72UZkZzWESGQ2Krpy1wORK456e7vSxj3Vnv1R3/BL5ePgdO/4lfeQjnWft4I0ECBAgQIAAAQIECBAgQIBAbQQEmbVxdBUCBKYQmFydGfsNfuhDH0yf/OSdxVLeHNuKFbdUDy6Kk6p/8IMXl5jn2F99IkCAAAECBAgQIECAAAECZREQZJZlpD0ngXkUOHTo0dTbu3HCKeADA/2ps7M9u0Bz/DYPDvmZx0nj1gQIECBAgAABAgQIECBAYJKAINOUIEBgTgSiOnPv3vvSJz7RV71fVGiuW/fRbALNoaHh1N29vtq/wcFtqbOzY0583IQAAQIECBAgQIAAAQIECBA4s4Ag0wwhQGBOBSLQ7O8fSPv2HShON4+WS6C5cuWt6ZFHHq16PPXUd7Lf03NOB8/NCBAgQIAAAQIECBAgQIDAPAoIMucR360JlFkgTgffseNzaceOnVWG2DczDgWKJd0Rbs5lm3xa+bJl70oHD94/l11wLwIECBAgQIAAAQIECBAgQOAMAoJM04MAgXkVmCrQjA6tXr0qrV69sgg156L19W2cEKred9+X0vLl75uLW7sHAQIECBAgQIAAAQIECBAgMAMBQeYMkLyEAIH6C8SS8+3bB4uPsbGx6g2jSnPt2o4i0Gxru7YuHYnDiFasuLV6bdWYdWF2UQIECBAgQIAAAQIECBAgcE4Cgsxz4vNmAgTqIbBv3wNpx47BdOjQYxMuH0Hmxz++Lq1c+f6anXY+MnKkOOAnPlfa8PA9RUWoRoAAAQIECBAgQIAAAQIECOQjIMjMZyz0hACBSQKx7Hxo6EspThOvHAxUeUmcJh5h4803rzwnt5tu+kD69re/W71GXG/v3l3ndE1vJkCAAAECBAgQIECAAAECBGovIMisvakrEiBQB4FY/v2Vr3w1ff7z/3ra1ZctuzFdffWStGDBgnT99W1p6dJrZ7QMPU5P37Jl64QQc+fObTWr9qwDg0sSIECAAAECBAgQIECAAIHSCggySzv0HpxAYwrEXpq7dw8XS88nV2lOfqIIOG+66cYi1Iy9NqP99rfPph//+On0jW88kWIJe6VdeeVr05e/PDSjALQx5fSaAAECBAgQIECAAAECBAg0toAgs7HHT+8JlFog9rWMpef79h2YNtQ8E1Rr66J08OD9afHiRaX29PAECBAgQIAAAQIECBAgQCBnAUFmzqOjbwQIzFjg6NGj6eGHH00jI4eLg3smHxT0UheKE8oHB7cLMWcs7YUECBAgQIAAAQIECBAgQGB+BASZ8+PurgQIzIFA7Kv5yCOPpjg0aHR0tLjjFVdcUXx+3/v+sVhGHh8aAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIEL7bi68AAAGrSURBVCBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv8D/A4EQEWXi+YzZAAAAAElFTkSuQmCC',
            panelDataGrid: [
              {
                panelDataGridD: 'd',
                panelDataGridC: 'c',
                panelDataGridB: 'b',
                panelDataGridA: 'a',
              }, {
                panelDataGridD: 'h',
                panelDataGridC: 'g',
                panelDataGridB: 'f',
                panelDataGridA: 'e',
              }, {
                panelDataGridD: 'l',
                panelDataGridC: 'k',
                panelDataGridB: 'j',
                panelDataGridA: 'i',
              },
            ],
            textfield: 'testing',
            page2Customer: 'bob@example.com',
            textfieldonPage2: 'test',
            numberField: 234,
            textfieldonpage1: [
              'a', 'b', 'c',
            ],
            panelHtml5Select: 'banana',
            page3Iagreetothefollowtherules: true,
            panelText: 'hello',
          },
        };

        setTimeout(() => {
          const customerSelectEl = form.element.querySelector('.formio-component-page2Customer');
          const customerSelectValueEl = customerSelectEl.querySelector('[ref="value"]');
          const htmlSelectEl = form.element.querySelector('.formio-component-panelHtml5Select');
          const htmlSelectValueEl = htmlSelectEl.querySelector('[ref="value"]');
          const checkboxEl = form.element.querySelector('.formio-component-page3Iagreetothefollowtherules');
          const checkboxValueEl = checkboxEl.querySelector('[ref="value"]');

          assert.equal(customerSelectValueEl.textContent.trim(), 'Bob Thompson', 'Should render Select value properly');
          assert.equal(htmlSelectValueEl.textContent.trim(), 'Banana', 'Should render HTML5 Select value properly');
          assert.equal(checkboxValueEl.textContent.trim(), 'True', 'Should render Checkbox value properly');

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

  it('HTML render mode for Wizard', (done) => {
    const element = document.createElement('div');
    htmlRenderMode.display = 'wizard';

    const originalMakeRequest = Formio.makeRequest;
    Formio.makeRequest = function() {
      return new Promise(resolve => {
        setTimeout(() => {
          const values = [
            {
              _id: '5a53f8a044398b0001023eab',
              modified: '2019-02-01T16:12:06.618Z',
              data: {
                firstName: 'Bob',
                lastName: 'Thompson',
                status: 'inactive',
                email: 'bob@example.com',
                submit: true,
              },
              form: '5a53f887c8930000010f8b22',
              _fvid: 0,
              _vid: 0,
              created: '2018-01-08T23:02:56.484Z',
              externalIds: [],
              access: [],
              roles: [],
              owner: '553dbfc08d22d5cb1a7024f2',
              state: 'submitted',
              project: '5692b91fd1028f01000407e3',
            },
            {
              _id: '5a53f8ad0dc919000194ab6b',
              modified: '2019-02-01T16:12:01.781Z',
              data: {
                firstName: 'Sally',
                lastName: 'Tanner',
                status: 'active',
                email: 'sally@example.com',
                submit: true,
              },
              form: '5a53f887c8930000010f8b22',
              _fvid: 0,
              _vid: 0,
              created: '2018-01-08T23:03:09.730Z',
              externalIds: [],
              access: [],
              roles: [],
              owner: '553dbfc08d22d5cb1a7024f2',
              state: 'submitted',
              project: '5692b91fd1028f01000407e3',
            },
            {
              _id: '5a53f8b744398b0001023eaf',
              modified: '2019-02-01T16:11:57.139Z',
              data: {
                firstName: 'Jane',
                lastName: 'Doe',
                status: 'active',
                email: 'jane@example.com',
                submit: true,
              },
              form: '5a53f887c8930000010f8b22',
              _fvid: 0,
              _vid: 0,
              created: '2018-01-08T23:03:19.473Z',
              externalIds: [],
              access: [],
              roles: [],
              owner: '553dbfc08d22d5cb1a7024f2',
              state: 'submitted',
              project: '5692b91fd1028f01000407e3',
            },
          ];
          resolve(values);
        }, 50);
      });
    };

    Formio.createForm(element, htmlRenderMode, {
      readOnly: true,
      renderMode: 'html',
    }).then(form => {
      form.submission = {
        data: {
          textfieldonPage3: 'test',
          signature: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABTIAAACWCAYAAADt5XcLAAAgAElEQVR4Xu3dfWydV30H8NM1dLRdndIOKKJxGHSh0MalgBilpLAJyIsatQgJu4rT8peNiFOEhl0pyR8gxZHq/DGJJAhHCNbEEU5BHVWyJpWYuja0tOPdSctLQVBHWmAwkTi0lEFh+j3h3tqOG9vJvfa59/kcybKT3Ps85/mcE0f+5nfOOe/ZZ3/156QRIECAAAECBAgQIECAAAECBAgQIEAgY4HzBJkZj46uESBAgAABAgQIECBAgAABAgQIECBQCAgyTQQCBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIMECBAgQIAAAQIECBAgQIAAAQIECAgyzQECBAgQIECAAAECBAgQIECAAAECBLIXEGRmP0Q6SIAAAQIECBAgQIAAAQIECBAgQICAINMcIECAAAECBAgQIECAAAECBAgQIEAgewFBZvZDpIO1Fjhx4kQaGTlSXHbhwoXFx6WXthSfNQIECBAgQIAAAQIECBAgQIAAgTwFBJl5jotezUIggslnnjmaTpwYS/F1fBw/furrZ54Z/cufnfr9eN2Z2uLFi1Jb29J06vO16d3vvrH4WiNAgAABAgQIECBAgAABAgQIEJhfAUHm/Pq7+ywFIoj8+tcfTbt3D6fR0VMhZT3bggUL0sUXX1yEmjfddGO6+eaVxdcaAQIECBAgQIAAAQIECBAgQIDA3AoIMufW293OUmDfvgfSF7+4Oz3++DeLysrZtNbWRdWqymXLbizeGsvIr7tuYiAZoWhUcEY7dOjRdPz4ifT00z9Nzz///ITbRYXmunUfTevWdc2mG15LgAABAgQIECBAgAABAgQIECBwDgKCzHPA89b6CkSwuGfPcBoa+lJReblkyVVpyZK/T6tXr0oRSFaWfFf2u4zeRMjZ2tpa0z0v494RbEaYun//gepDR2Xm4OA2FZr1nQauToAAAQIECBAgQIAAAQIECBAoBASZJkJWAhFERlgYS8cjPIzW0tKSenq6U2fnbfO+X2WEmhGsbtmytehbVHbu3XtPEaxqBAgQIECAAAECBAgQIECAAAEC9RMQZNbP1pVnIRAB4ZYtA2nfvgNFVWWEl7H0O8LLzs6OWVxpbl4aIWtX1/o0Onpqj84NG3rTxo19c3NzdyFAgAABAgQIECBAgAABAgQIlFBAkFnCQc/pkYeGhtOOHYOpsjw8Asy1azuKADP3Q3UicF2+/JZ0+PCTBenVVy9J9903PO9VozmNr74QIECAAAECBAgQIECAAAECBGolIMislaTrzFggAsAdO3ZW976MN8aBPFHRmGP15ZkeLJ6lt3dTsZdnpe3cua043TyWnWsECBAgQIAAAQIECBAgQIAAAQK1ERBk1sbRVWYgUAkwt28frJ48vmzZu4oAs9H3mPzCF3al9ev/uaoQBxLdffdm1ZkzmBdeQoAAAQIECBAgQIAAAQIECBCYiYAgcyZKXnPOAv39A0UVZoSZ0das6SgCzMrJ4+d8gwwuEPt8trevrS41j2fbsKHxqkwzoNQFAgQIECBAgAABAgQIECBAgMBpAoJMk6KuAvv2PZDuumtTipCvWQPMyYAR2kbV6djYWPFHsVx+YGCzpeZ1nWkuToAAAQIECBAgQIAAAQIECDS7gCCz2Ud4np4vgsvu7vUpTveOFntGDgz0N1UF5ploJ59qHtWZw8O7sj/AaJ6mi9sSIECAAAECBAgQIECAAAECBKYVEGROS+QFsxGIpeNbtmwtKhKjNcsemLMxqLx28kFAcfhPT0932rCh92wu5z0ECBAgQIAAAQIECBAgQIAAgVILCDJLPfy1ffioQowqzKjGXLr0mtTT89GGO4W8tiKnrhahbiw3ryw1dxBQPZRdkwABAgQIECBAgAABAgQIEGh2AUFms4/wHDzfyMiRIqjbv/9Acbc4rXvt2g57Qo6zD6Ourp4JBwEdOHB/aZbaz8E0dAsCBAgQIECAAAECBAgQIECgyQUEmU0+wPV8vPHLyK+88rXpLW9pK9U+mLO1Da+urvXVwDeWmg8OfiZFhaZGgAABAgQIECBAgAABAgQIECBwZgFBphlyVgKxXHrPnuEUlYatrYuKU7kFcjOjHL/U3L6ZMzPzKgIECBAgQIAAAQIECBAgQICAINMcmJVABJexD2Z8jrZmTUfaunWzZeSzUkzFae5RnTk6erR4Z2dnRxoc3DbLq3g5AQIECBAgQIAAAQIECBAgQKA8AoLM8oz1OT9pBG9RhRktqjB37tyWli278ZyvW9YLxKFI3d096dChxwqCsNy79x6hcFknhOcmQIAAAQIECBAgQIAAAQIEziggyDRBphXYt++B1N19Z4o9HqOtW9eVNm7sE7hNKzf9C8I0lppv2bK1ePHixYuSQ4Cmd/MKAgQIECBAgAABAgQIECBAoHwCgszyjfmMnzhCtr6+TWloSBXmjNHO8oURFnd03FENM+Pkd3uOniWmtxEgQIAAAQIECBAgQIAAAQJNKSDIbMphPfeHij0c29vvUIV57pQzvkIsNW9vX5sOH36yqHatVL7O+AJeSIAAAQIECBAgQIAAAQIECBBoYgFBZhMP7tk+Wix1vuuuTcXb7YV5topn976ogu3t3VTdizQOU4q9SDUCBAgQIECAAAECBAgQIECAQNkFBJllnwHjnj9CtNgLM5Y5R1u27F1pcHB7sW+jNrcClaXmL3/5y9OSJVelgwe/ak/SuR0CdyNAgAABAgQIECBAgAABAgQyExBkZjYg89WdCDFXrLg1jYwcKbrgQJ/5GokX7xtjsnz5LcVS82gRZjolfv7HRQ8IECBAgAABAgQIECBAgACB+REQZM6Pe1Z3jf0wu7vXp9ijsaWlJfX0dBenkmt5CPT1bUw7duwsOhPjsmFDbx4d0wsCBAgQIECAAAECBAgQIECAwBwKCDLnEDvHW8US5lhOHtV/EWLee+8uVX8ZDlScHN/buzGNjY0V4xPVmRoBAgQIECBAgAABAgQIECBAoEwCgswyjfakZx1/qM/SpdeknTu3p7a2a0sskvejR+VsV9f6NDp6tNi3dHh4l/HKe8j0jgABAgQIECBAgAABAgQIEKihgCCzhpiNdKn+/oG0ZcvWoss337yyOBl74cKFjfQIpezr+H0zY7wGBz+TVq9eVUoLD02AAAECBAgQIECAAAECBAiUS0CQWa7xLp62vf32tH//geLrONRnYKC/hAqN+8gRZsYYHjr0WPEQ9s1s3LHUcwIECBAgQIAAAQIECBAgQGDmAoLMmVs1xSvf+c73Vk/BvvvuzcXBPlpjCoyvqo2qzKjOVFXbmGOp1wQI1E4gDq6LLTiitbVd4/ti7WhdiQABAgQIECBAgMC8Cwgy530I5qYDIyNH0gc+sDqdPPnb4oZxWEwcGqM1tkAc1tTRcUfxEDGeg4Pbiv0zNQIECJRJIMLLT32qPz300MPpV7/69YRHj72f43ujPaDLNCM8KwECBAgQIECAQLMKCDKbdWTHPVflUJ/zzjsvXXLJJemHP/yOCpUmGvcIqWOpuUOAmmhQPQoBAjMSiADzs58dTLt3D6fYduOl2po1HcVe0BoBAgQIECBAgAABAo0tIMhs7PE7Y+/jh7ru7jtTVO1Fa2lpSceO/bSJn7i8jzZ538yoPurs7CgviCcnQKDpBQ4dejR1d69PEWZO1+wHPZ2QPydAgAABAgQIECDQGAKCzMYYp1n3Mn6wW7nyluoPeELMWRM25Bv6+jamHTt2Fn2PIHPDhj5LzRtyJHWaAIEzCZwpxHzFKy5Nq1atSKOjo+n48ROprW1pcSiabTfMKQIECBAgQIAAAQKNLyDIbPwxPO0JJv+A19q6KD3++EOWkzfhWE/1SFGB29W1Po2NjRV7wg0MbLYfaknG3mMSKINA/BvX3n7HaUvJly17VxFY2v+5DLPAMxIgQIAAAQIECJRVQJDZZCM//vCXeLQIMQ8evF8lSpON83SPExW5UZ25f/+BIsCOZZXxA75GgACBRhaIEPPDH769+I+a8e3uuzennp7uRn40fSdAgAABAgQIECBAYAYCgswZIDXKS4aGhov9wsa3p576jhCzUQawDv2szImWlkvS6163OA0P7zIf6uDskgQI1F9g8pYpccfYNiUO8Vm9elX9O+AOBAgQIECAAAECBAjMu4Agc96HoDYdmBxixg939967yxK72vA29FXih//29rXp8OEni+eIH/pvvnmlrQYaelR1nkD5BGLLjD17hqsPHv9Bc++9u/07V76p4IkJECBAgAABAgRKLCDIbILBj6V2K1bcOuFJhofvUaHSBGNby0fo7x9IEXiPjh4tfvCPk80dflFLYdciQKBeApP/nfOfdfWSdl0CBAgQIECAAAECeQsIMvMen2l7N9Vy8g0beu2HOK1cOV8QYUBv78ZqdWbsmxnzRSNAgECuAlMtKbcnZq6jpV8ECBAgQIAAAQIE6isgyKyvb12vHgf7dHffOeHk1jjUZWCgv673dfHGFjhx4kTavn0wbdmytXiQqMqM6kwn/Tb2uOo9gWYUGBk5kvr6NqX4T5hKi9PJ4xA7jQABAgQIECBAgACB8gkIMht0zOOHu46O21NUqlRa7HsY+x/GKdUagekEYg51dfVUqzM7OzvSwMBm82c6OH9OgMCcCER4edttH0m/+c3x6v1aWxcVIaZtMeZkCNyEAAECBAgQIECAQHYCgszshmT6Dk21zG7p0mvSgw/eL4Sans8rJgnE3pmV6swIwbdu3ZzWrOngVAOBqH49fnysuNKll7b4+1kD02a9RMyVkZEniwr7+Dh27H/SsWP/nS6//PLi18ePx++fmkvRIsiLv6/PP//79Oyzvy1eN1274IK/Tn/84x/SokWvLeZlXHeqFtdM6c/p1a9+9YTXxOvj35+FC1uKt1144YVpwYIF6frr24pfL116bWpru3a6bkz757HaYM+evSk+j2/x79zWrf2qx6cV9AICBAgQIECAAAECzSsgyGywsZ0qxIwKlccff0hI0mBjmVN3Y151d/ekQ4ceK7oVy8w3buwVGMxgkKKy9Yknvpl+/vPRIiT6/vePpPi9lwqJIuiJACqCqMWLW6tfv+xlL0sXXXRR9Y6VQKsShL7461OBVgRbU7W47qWXLqxeN+4VwZOtA2YwmHPwkpgbP/jBj9LTT/8knTx5spgv45dNz0EX6n6LG274h/Tcc8+lWAIebfwqgZjHv//9/6Wf/ezn6eqrlxR/Fh8jI4fTj370dPrxj38y5d+deO0XvzhYk6C07gBuQIAAAQIECBAgQIBA3QQEmXWjrf2Fp1pOHiHm3r27/HBXe+5SXjEqoKJC8/DhJ4vnX716VYpDNSzjnDgdIoyJKtbwGr+9Q86T5oILLigq6KIytLW1NV133bXF5/h1rSrpcn7++ehbfM+O/xyIkC6+jg9t9gIRiMa/c7ZNmb2ddxAgQIAAAQIECBBoNgFBZoOM6FQHHrS0tKTHH/9PIVODjGEjdTMOA4pAc2xsrAgPenq6UxwkJUhIaWhoOHV3r5/X4Yy/+1F1OT5gnjw24ytCo+rvD3/4Q/rd7353xn5HtWh83HTTjend777R95ZZjHJ4P/bYE+nb3/5uOnLkqdOWRc/kUrF0OsY12hvfuKT4+3fVVW8441tPLS1/Nl1++WXT3uLFpeVX/mWp+tRVvfG6kyfH0qte9arimvFslfl1qtq3pagKPnbsl0UV8p/+9KeiqjRC/dHRF/dtnrZD07wgAsx167qL/1DRCBAgQIAAAQIECBAgEAKCzAaYB/EDYn//1gnLDx140AAD1+BdjPCit3dT2rNnuHiSsp9uHh7d3XdOGVBdcsklacmSq9KHP/yhwinCnsoS8so0qASLk6vyKkvEo2LyoosunDBrKuFRfK7VHpuVZe8ROlUqBaMPlSrc8R2IZ4kQKZalR6gkyH5RJ/z27z9QfF8Ox9lU5t5wwzvSxRf/TXr/+/+pWhnbLFXPMb8efPA/0hNP/Fe67LLL0ujo6GnfCa+44op07NixdN11S4sq1bD79a//Ny1YcH56+9vflpYvf18x75rFpMH/KdB9AgQIECBAgAABAlkJCDKzGo7TOxOVcZ/+dH967rkXK6miamfnzu2Wk2c+ds3SvVPVwBur+2eWcbn5VBXRMb4R7m3c2NcU+09G0PrIIxHKndqzsbJf6vh5XKnWLGuwefTo0RTfkytOM/k7Ht+v29qWFlWulYrXmbzPawgQIECAAAECBAgQIEDgdAFBZqazIoKEvr5Np+2p5tTWTAesBN2KJdXbt3+uqNyLSqkNG/pSZ2fzn24ewd6qVR9Mv/nN8eoox9LuCDBjyX0zt/g+FKFd7AU6VcVmJZiLoK6t7ZqmCHSnGs+YA5U9Uacb77e97fqiMjf2IHXA0nRa/pwAAQIECBAgQIAAAQKzExBkzs6r7q+uHCISVT+T2ytecWl64IF/U4lZ91Fwg5cSiPkZczNCnZaWS9LrXrc4DQ/vatoloBFgrVhx64RTlMtaET25YjP23Yw9HMe317zmivTKV/5tsQQ9KhDjc4SdsRVGIy4Tni7AjOeKsHL16pXFCfTxrBoBAgQIECBAgAABAgQI1E9AkFk/21lfOYKCCE1e6mTbgwe/qsJn1qreUA+B2NOuvX1ttUpvYKA/dXa2N90eim9601snHF7y+tf/Xdqz5wsCq79MqvH7bEb15i9+8cv09NM/fckpF2FmnJQeLSoWK3tuxu/H16eCz2uKP5+v/TjjoJ6HH/56Ghr60pTfi2M/1Ntvvy11dt5mHtTjm4trEiBAgAABAgQIECBA4AwCgsyMpsfk0GR81+6+e3PTL2PNaCh0ZYYCsdw8TjePk4qjGm1gYHPThO2TTyePSsy9e3c3ZGXhDIezZi+rnGD9zDOjfznMZbQ40GVyBeeZblip5ozXRNAZFY+VryuhZ/z6/PP/Kr3wwgtxdl1xmnblYKTxYWjloKXjx8eK6tr4OP/889MLL/yp+LpyANKZ9r6M6suY307Qrtk0cSECBAgQIECAAAECBAjMWkCQOWuy+rzhNa95w0v+kD84uK0UexHWR9ZV6y0QAVV3d0/1cJjYO3LDht5637bu13/nO987YV/Ib3zjIRV4NVAff2p6BJ3RKr/3ve8dTidPnqzBXWp3iTjQKaovy7AfbO3UXIkAAQIECBAgQIAAAQL1ERBk1sd1VleNgzQ6Ou447T1xoMi99+5qmgq3WaF4ccMJxN6Zd921qeh3VGc28t6Zk/9OrlnTkXbu3NZwY9KIHY4KyaicHB09VcUZYWd8jl8fP35i1pWdszV485uvThdeeGFxYE9UXzbi3p6zfWavJ0CAAAECBAgQIECAQKMICDIzGKmpgswIMR988H4VYBmMjy7MXCAq69rbby+WmscS36jOXLeua+YXyOSV73nP8vStb32n2hv702YyMOO6EYFnBJzRToWfsWR8rPhc+b3JvR6/J+cFF1yQLrrowmKexvvHL2XP72n1iAABAgQIECBAgAABAgRCQJCZyTwYv7Q8Qsxjx176wIxMuqwbBKYUiFCoq2t92r//QPHncapz7C3YKCc6Rzj25je/tfpsN9zwjvS1r/270SZAgAABAgQIECBAgAABAgTmWUCQOc8DMP72UZkZzWESGQ2Krpy1wORK456e7vSxj3Vnv1R3/BL5ePgdO/4lfeQjnWft4I0ECBAgQIAAAQIECBAgQIBAbQQEmbVxdBUCBKYQmFydGfsNfuhDH0yf/OSdxVLeHNuKFbdUDy6Kk6p/8IMXl5jn2F99IkCAAAECBAgQIECAAAECZREQZJZlpD0ngXkUOHTo0dTbu3HCKeADA/2ps7M9u0Bz/DYPDvmZx0nj1gQIECBAgAABAgQIECBAYJKAINOUIEBgTgSiOnPv3vvSJz7RV71fVGiuW/fRbALNoaHh1N29vtq/wcFtqbOzY0583IQAAQIECBAgQIAAAQIECBA4s4Ag0wwhQGBOBSLQ7O8fSPv2HShON4+WS6C5cuWt6ZFHHq16PPXUd7Lf03NOB8/NCBAgQIAAAQIECBAgQIDAPAoIMucR360JlFkgTgffseNzaceOnVWG2DczDgWKJd0Rbs5lm3xa+bJl70oHD94/l11wLwIECBAgQIAAAQIECBAgQOAMAoJM04MAgXkVmCrQjA6tXr0qrV69sgg156L19W2cEKred9+X0vLl75uLW7sHAQIECBAgQIAAAQIECBAgMAMBQeYMkLyEAIH6C8SS8+3bB4uPsbGx6g2jSnPt2o4i0Gxru7YuHYnDiFasuLV6bdWYdWF2UQIECBAgQIAAAQIECBAgcE4Cgsxz4vNmAgTqIbBv3wNpx47BdOjQYxMuH0Hmxz++Lq1c+f6anXY+MnKkOOAnPlfa8PA9RUWoRoAAAQIECBAgQIAAAQIECOQjIMjMZyz0hACBSQKx7Hxo6EspThOvHAxUeUmcJh5h4803rzwnt5tu+kD69re/W71GXG/v3l3ndE1vJkCAAAECBAgQIECAAAECBGovIMisvakrEiBQB4FY/v2Vr3w1ff7z/3ra1ZctuzFdffWStGDBgnT99W1p6dJrZ7QMPU5P37Jl64QQc+fObTWr9qwDg0sSIECAAAECBAgQIECAAIHSCggySzv0HpxAYwrEXpq7dw8XS88nV2lOfqIIOG+66cYi1Iy9NqP99rfPph//+On0jW88kWIJe6VdeeVr05e/PDSjALQx5fSaAAECBAgQIECAAAECBAg0toAgs7HHT+8JlFog9rWMpef79h2YNtQ8E1Rr66J08OD9afHiRaX29PAECBAgQIAAAQIECBAgQCBnAUFmzqOjbwQIzFjg6NGj6eGHH00jI4eLg3smHxT0UheKE8oHB7cLMWcs7YUECBAgQIAAAQIECBAgQGB+BASZ8+PurgQIzIFA7Kv5yCOPpjg0aHR0tLjjFVdcUXx+3/v+sVhGHh8aAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIEL7bi68AAAGrSURBVCBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv4AgM/8x0kMCBAgQIECAAAECBAgQIECAAAECpRcQZJZ+CgAgQIAAAQIECBAgQIAAAQIECBAgkL+AIDP/MdJDAgQIECBAgAABAgQIECBAgAABAqUXEGSWfgoAIECAAAECBAgQIECAAAECBAgQIJC/gCAz/zHSQwIECBAgQIAAAQIECBAgQIAAAQKlFxBkln4KACBAgAABAgQIECBAgAABAgQIECCQv8D/A4EQEWXi+YzZAAAAAElFTkSuQmCC',
          panelDataGrid: [
            {
              panelDataGridD: 'd',
              panelDataGridC: 'c',
              panelDataGridB: 'b',
              panelDataGridA: 'a'
            },
            {
              panelDataGridD: 'h',
              panelDataGridC: 'g',
              panelDataGridB: 'f',
              panelDataGridA: 'e'
            },
            {
              panelDataGridD: 'l',
              panelDataGridC: 'k',
              panelDataGridB: 'j',
              panelDataGridA: 'i'
            }
          ],
          textfield: 'testing',
          page2Customer: 'bob@example.com',
          textfieldonPage2: 'test',
          numberField: 234,
          textfieldonpage1: [
            'a',
            'b',
            'c'
          ],
          panelHtml5Select: 'banana',
          page3Iagreetothefollowtherules: true,
          panelText: 'hello'
        },
      };

      setTimeout(() => {
        form.setPage(1);

        setTimeout(() => {
          const customerSelectEl = form.element.querySelector('.formio-component-page2Customer');
          const customerSelectValueEl = customerSelectEl.querySelector('[ref="value"]');

          assert.equal(customerSelectValueEl.textContent.trim(), 'Bob Thompson', 'Should render Select value properly');

          form.setPage(2);

          setTimeout(() => {
            const htmlSelectEl = form.element.querySelector('.formio-component-panelHtml5Select');
            const htmlSelectValueEl = htmlSelectEl.querySelector('[ref="value"]');

            assert.equal(htmlSelectValueEl.textContent.trim(), 'Banana', 'Should render HTML5 Select value properly');

            Formio.makeRequest = originalMakeRequest;

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

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

    Formio.createForm(element, optionalSanitize, {
      sanitize: false,
    }).then(form => {
      const sanitize = sinon.spy(FormioUtils, 'sanitize');
      form.redraw();
      setTimeout(() => {
        assert.equal(sanitize.callCount, 0, 'Should not sanitize templates when sanitize in not turned on');
        element.innerHTML = '';
        Formio.createForm(element, optionalSanitize, {
          sanitize: true,
        }).then(form => {
          sanitize.resetHistory();
          form.redraw();
          setTimeout(() => {
            assert.equal(sanitize.callCount, 1, 'Should sanitize templates when sanitize in turned on');
            done();
          }, 250);
        }, 250);
      });
    }).catch(done);
  });

  it('Should execute clearOnHide if visibility of the component inside an EditGrid has changed', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en' });

    form.setForm(testClearOnHideInsideEditGrid).then(() => {
      form.submission = {
        state: 'submitted',
        data: {
          subsidiaryEditGrid: [
            {
              subsidiaryEntityContainer: {
                entityFullName: 'test',
                divisionNum: '',
                entityType: 'otherEntity',
                ifOtherEntityPleaseExplain: 'test',
              },
            },
          ],
        },
      };

      setTimeout(() => {
        const clearOnHideField = form.getComponent([
          'subsidiaryEditGrid',
          0,
          'subsidiaryEntityContainer',
          'ifOtherEntityPleaseExplain',
        ]);
        const radioTrigger = form.getComponent(['subsidiaryEditGrid', 0, 'subsidiaryEntityContainer', 'entityType']);
        assert.equal(form.rootPristine, true, 'Should not change this prop  after setting a submission');
        assert.equal(clearOnHideField.visible, true, 'Should become visible');
        assert.equal(clearOnHideField.dataValue, 'test', 'Should set a value from  the submission');

        radioTrigger.setValue('subsidiary', { modified: true });
        setTimeout(() => {
          assert.equal(clearOnHideField.visible, false, 'Should become invisible');

          radioTrigger.setValue('otherEntity', { modified: true });
          setTimeout(() => {
            assert.equal(clearOnHideField.visible, true, 'Should become visible');
            assert.equal(clearOnHideField.dataValue, '', 'Should clear a value due to the clearOnHide');

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

  it('Should show values in editGrid rows with nested dataGrid when viewing submission with initEmpty option', function(done) {
    const formElement = document.createElement('div');
    const formWithNestedDataGridInitEmptyOption = new Webform(formElement);

    formWithNestedDataGridInitEmptyOption.setForm(formWithNestedDataGridInitEmpty.form).then(() => {
      formWithNestedDataGridInitEmptyOption.setSubmission(formWithNestedDataGridInitEmpty.submission);

      setTimeout(() => {
        const nestedDataGridFirstRowComponentValue = formWithNestedDataGridInitEmptyOption.element.querySelector(
          '[ref="editgrid-editGrid-row"]').querySelectorAll('.col-sm-2');

        assert.equal(nestedDataGridFirstRowComponentValue[1].textContent.trim(), 'email');
        assert.equal(nestedDataGridFirstRowComponentValue[2].textContent.trim(), 'hhh@gmail.com');

        done();
      }, 200);
    })
      .catch((err) => done(err));
  });

  it('Should not refetch options for Select if there was an error', function(done) {
    const formElement = document.createElement('div');
    const form= new Webform(formElement);
    const formJson = {
      components: [
        {
          label: 'Select',
          widget: 'html5',
          tableView: true,
          dataSrc: 'url',
          data: {
            url: 'http://example.com',
            headers: [
              {
                key: '',
                value: '',
              },
            ],
          },
          key: 'select',
          hidden: true,
          type: 'select',
          input: true,
          disableLimit: false,
        },
      ],
    };

    let counter = 0;
    const originalMakeRequest = Formio.makeRequest;
    Formio.makeRequest = function() {
      return new Promise((_, reject) => {
        setTimeout(() => {
          counter++;
          const err = new Error('Failed to fetch');
          err.networkError = true;
          reject(err);
        }, 50);
      });
    };

    form.setForm(formJson).then(() => {
      const select = form.getComponent('select');

      select.visible = true;

      setTimeout(() => {
        setTimeout(() => {
          select.visible = false;

          setTimeout(() => {
            select.visible = true;

            setTimeout(() => {
              expect(select.networkError).to.be.true;
              expect(select.loadingError).to.be.true;
              expect(counter).to.equal(1);
              Formio.makeRequest = originalMakeRequest;
              done();
            }, 200);
          }, 200);
        }, 200);
      }, 200);
    })
    .catch((err) => done(err));
  });

  it('Should show only one custom error when submitting empty required field with multiple validation', function(done) {
    const formJson =  {
      components: [
        {
          label: 'This line',
          tableView: false,
          storage: 'base64',
          webcam: false,
          fileTypes: [{ label: '', value: '' }],
          multiple: true,
          validate: { required: true, customMessage: 'will be showed once' },
          key: 'file',
          type: 'file',
          input: true,
        },
        {
          label: 'Submit',
          showValidations: false,
          tableView: false,
          key: 'submit',
          type: 'button',
          input: true,
          saveOnEnter: false,
        }
      ]
    };
    const element = document.createElement('div');
    const form = new Webform(element);

    form.setForm(formJson).then(() => {
    Harness.clickElement(form, form.element.querySelector('[name="data[submit]"]'));

    setTimeout(() => {
      assert.equal(form.errors[0].messages.length, 1);
      assert.equal(form.errors[0].messages[0].message, 'will be showed once');
      assert.equal(form.element.querySelector('[ref="errorRef"]').textContent.trim().includes('will be showed once'), true);
      done();
    }, 200);
    })
    .catch((err) => done(err));
  });

  it('Should show validation error when submitting number with just "-" sign and required validation', function(done) {
    const formJson =  {
      components: [
        {
          label: 'Number',
          mask: false,
          tableView: false,
          delimiter: false,
          requireDecimal: false,
          inputFormat: 'plain',
          truncateMultipleSpaces: false,
          validate: {
            required: true
          },
          key: 'number',
          type: 'number',
          input: true
        },
        {
          label: 'Submit',
          showValidations: false,
          tableView: false,
          key: 'submit',
          type: 'button',
          input: true,
          saveOnEnter: false,
        }
      ]
    };
    const element = document.createElement('div');
    const form = new Webform(element);

    form.setForm(formJson).then(() => {
      Harness.setInputValue(form, 'data[number]', '-_');
      Harness.clickElement(form, form.element.querySelector('[name="data[submit]"]'));

      setTimeout(() => {
        assert.equal(form.errors[0].messages.length, 1);
        assert.equal(form.errors[0].messages[0].message, 'Number is required');
        assert.equal(form.element.querySelector('[ref="errorRef"]').textContent.trim().includes('Number is required'), true);
        done();
      }, 200);
    })
    .catch((err) => done(err));
  });

  for (const formTest of FormTests) {
    const useDoneInsteadOfPromise = formTest.useDone;

    if (useDoneInsteadOfPromise) {
      describe(formTest.title || '', () => {
        for (const title in formTest.tests) {
          const formTestTest = formTest.tests[title];
          it(title, function(done) {
            const self = this;
            const formElement = document.createElement('div');
            let form = new Webform(formElement, _.cloneDeep(formTest.formOptions || {}));
            form.setForm(formTest.form).then(function() {
              formTestTest(form, function(error) {
                form = null;
                formElement.innerHTML = '';
                if (error) {
                  return done(error);
                }
                done();
              }, self);
            });
          });
        }
      });
    }
    else {
      describe(formTest.title || '', () => {
        for (const title in formTest.tests) {
          const formTestTest = formTest.tests[title];
          it(title, function() {
            const formElement = document.createElement('div');
            const form = new Webform(formElement, { language: 'en' });
            return form.setForm(formTest.form).then(function() {
              formTestTest(form, function(error) {
                form.destroy();
                if (error) {
                  throw new Error(error);
                }
              });
            });
          });
        }
      });
    }
  }
});

// describe('Test the saveDraft and restoreDraft feature', () => {
//   APIMock.submission('https://savedraft.form.io/myform', {
//     components: [
//       {
//         type: 'textfield',
//         key: 'a',
//         label: 'A'
//       },
//       {
//         type: 'textfield',
//         key: 'b',
//         label: 'B'
//       }
//     ]
//   });
//
//   const saveDraft = function(user, draft, newData, done) {
//     const formElement = document.createElement('div');
//     const form = new Webform(formElement, {
//       saveDraft: true,
//       saveDraftThrottle: false
//     });
//     form.src = 'https://savedraft.form.io/myform';
//     Formio.setUser(user);
//     form.on('restoreDraft', (existing) => {
//       assert.deepEqual(existing ? existing.data : null, draft);
//       form.setSubmission({ data: newData }, { modified: true });
//     });
//     form.on('saveDraft', (saved) => {
//       // Make sure the modified class was added to the components.
//       const a = form.getComponent('a');
//       const b = form.getComponent('b');
//       assert.equal(a.hasClass(a.getElement(), 'formio-modified'), true);
//       assert.equal(b.hasClass(b.getElement(), 'formio-modified'), true);
//       assert.deepEqual(saved.data, newData);
//       form.draftEnabled = false;
//       done();
//     });
//     form.formReady.then(() => {
//       assert.equal(form.savingDraft, true);
//     });
//   };
//
//   it('Should allow a user to start a save draft session.', (done) => saveDraft({
//     _id: '1234',
//     data: {
//       firstName: 'Joe',
//       lastName: 'Smith'
//     }
//   }, null, {
//     a: 'one',
//     b: 'two'
//   }, done));
//
//   it('Should allow a different user to start a new draft session', (done) => saveDraft({
//     _id: '2468',
//     data: {
//       firstName: 'Sally',
//       lastName: 'Thompson'
//     }
//   }, null, {
//     a: 'three',
//     b: 'four'
//   }, done));
//
//   it('Should restore a users existing draft', (done) => saveDraft({
//     _id: '1234',
//     data: {
//       firstName: 'Joe',
//       lastName: 'Smith'
//     }
//   }, {
//     a: 'one',
//     b: 'two'
//   }, {
//     a: 'five',
//     b: 'six'
//   }, done));
// });
/* eslint-enable max-statements */