Home Reference Source

src/Webform.unit.js

import assert from 'power-assert';
import { expect } from 'chai';
import sinon from 'sinon';
import _ from 'lodash';
import each from 'lodash/each';
import i18next from 'i18next';
import Harness from '../test/harness';
import FormTests from '../test/forms';
import Webform from './Webform';
import 'flatpickr';
import {
  settingErrors,
  clearOnHide,
  manualOverride,
  validationOnBlur,
  calculateValueWithManualOverride,
  formWithAdvancedLogic,
  formWithPatternValidation,
  calculatedSelectboxes,
  calculateZeroValue,
  formWithConditionalLogic,
  formWithCalculatedValueWithoutOverriding,
  formWithTimeComponent,
  formWithEditGridModalDrafts,
  formWithBlurValidationInsidePanel,
  modalEditComponents,
  calculatedNotPersistentValue,
  initiallyCollapsedPanel,
  multipleTextareaInsideConditionalComponent,
  disabledNestedForm,
  propertyActions,
  formWithEditGridAndNestedDraftModalRow,
  formWithDateTimeComponents,
  formWithCollapsedPanel,
  formWithCustomFormatDate
} from '../test/formtest';
import DataGridOnBlurValidation from '../test/forms/dataGridOnBlurValidation';
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';

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

  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(() => {
      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);
    }).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);

    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;
              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 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, '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));
  });

  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, '6678');
            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, '667890');
              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, {
      template: 'bootstrap3',
      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('.control-label');
      assert.equal(label.innerHTML.trim(), 'Spanish Label');
      done();
    }).catch(done);
  });

  it('Should treat double colons as i18next namespace separators', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement);
    form.setForm({
      title: 'Test Form',
      components: []
    }).then(() => {
      const str = 'Test: this is only a test';

      assert.equal(form.t(str), str);
      assert.equal(form.t(`Namespace::${str}`), str);

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

  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, 'Field Label es obligatorio');
      });
  });

  it('Should translate a form after instantiate', done => {
    const formElement = document.createElement('div');
    const translateForm = new Webform(formElement, {
      template: 'bootstrap3',
      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('.control-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, {
      template: 'bootstrap3',
      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('.control-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, {
      template: 'bootstrap3',
    });
    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('.control-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, {
      template: 'bootstrap3',
    });
    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('.control-label').innerHTML.trim()).to.equal('Russian Label');
          form.redraw();
          expect(form.options.language).to.equal('ru');
          expect(formElement.querySelector('.control-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, {
      template: 'bootstrap3',
    });
    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 not mutate the global i18next if it gets an instance', async function() {
    await i18next.init({ lng: 'en' });
    const instance = i18next.createInstance();

    const formElement = document.createElement('div');
    const translateForm = new Webform(formElement, {
      template: 'bootstrap3',
      language: 'es',
      i18next: instance,
      i18n: {
        es: {
          'Default Label': 'Spanish Label'
        }
      }
    });

    return translateForm.setForm({
      title: 'Translate Form',
      components: [
        {
          type: 'textfield',
          label: 'Default Label',
          key: 'myfield',
          input: true,
          inputType: 'text',
          validate: {}
        }
      ]
    }).then(() => {
      assert.equal(i18next.language, 'en');
      assert.equal(translateForm.i18next.language, 'es');
      assert.equal(translateForm.i18next, instance);

      const label = formElement.querySelector('.control-label');
      assert.equal(label.innerHTML.trim(), 'Spanish Label');
    });
  });

  it('Should keep components valid if they are pristine', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en', template: 'bootstrap3' });
    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', template: 'bootstrap3' });
    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);
  };

  it('Should not fire validations when fields are either protected or not persistent.', (done) => {
    const form = new Webform(formElement,{ language: 'en', template: 'bootstrap3' });
    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', template: 'bootstrap3' });
    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',
      template: 'bootstrap3',
      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',
      template: 'bootstrap3'
    });
    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', template: 'bootstrap3' });
    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', template: 'bootstrap3' });
    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', template: 'bootstrap3' });
    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', template: 'bootstrap3' });
    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 correclty', (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', template: 'bootstrap3' });
      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',
        template: 'bootstrap3'
      });
      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', template: 'bootstrap3' });
      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 aftre component was blured');
            Harness.setInputValue(field2, 'data[textField1]', 'ab');

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

    it('Should keep components inside DataGrid valid onChange', (done) => {
      formElement.innerHTML = '';
      const form = new Webform(formElement, { language: 'en', template: 'bootstrap3' });
      form.setForm(DataGridOnBlurValidation).then(() => {
        const component = form.components[0];
        Harness.setInputValue(component, 'data[dataGrid][0][textField]', '12');
        const textField = component.iteratableRows[0].components.textField;
        setTimeout(() => {
          assert.equal(textField.error, '', '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', template: 'bootstrap3' });
      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('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: 'yes2' },
          { 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', template: 'bootstrap3' });
      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 allow to change value.', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en', template: 'bootstrap3' });
      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', template: 'bootstrap3' });
      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', template: 'bootstrap3' });
      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', template: 'bootstrap3' });
    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', template: 'bootstrap3' });
      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 render components properly', (done) => {
    const formElement = document.createElement('div');
    const form = new Webform(formElement, { language: 'en', template: 'bootstrap3' });
    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', template: 'bootstrap3' });
    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', template: 'bootstrap3' });
    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();
      }, 150);
    }).catch(done);
  });

  describe('Custom Logic', () => {
    it('Should rerender components using updated properties', (done) => {
      const formElement = document.createElement('div');
      const form = new Webform(formElement, { language: 'en', template: 'bootstrap3' });
      form.setForm(propertyActions).then(() => {
        form.emit('disabled');
        form.emit('hide');
        form.emit('require');
        setTimeout(() => {
          const textFieldDisabled = form.getComponent(['textField']);
          const textFieldHidden = form.getComponent(['textField1']);
          const textFieldRequired = form.getComponent(['textField2']);
          assert.equal(textFieldDisabled.component.disabled, true, 'Should be disabled');
          assert.equal(textFieldHidden.component.hidden, true, 'Should be hidden');
          assert.equal(textFieldRequired.component.validate.required, true, 'Should be required');
          const disabledInput = textFieldDisabled.element.querySelector('[ref="input"]');
          assert.equal(disabledInput.disabled, true, 'Should found a disabled input');
          const hiddenInput = textFieldHidden.element.querySelector('[ref="input"]');
          assert(!hiddenInput, 'Should not found a hidden input');
          const requiredFieldLabel = textFieldRequired.element.querySelector('label');
          assert(requiredFieldLabel.classList.contains('field-required'), 'Should mark a field as required');
          done();
        }, 550);
      }).catch(done);
    });
  });

  each(FormTests, (formTest) => {
    describe(formTest.title || '', () => {
      each(formTest.tests, (formTestTest, title) => {
        it(title, () => {
          const formElement = document.createElement('div');
          const form = new Webform(formElement, { language: 'en', template: 'bootstrap3' });
          return form.setForm(formTest.form).then(() => {
            formTestTest(form, (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 */