Home Reference Source

src/components/_classes/component/Component.unit.js

'use strict';
import assert from 'power-assert';
import { expect } from 'chai';
import sinon from 'sinon';
import Component from './Component';
import Webform from '../../../Webform';
import Harness from '../../../../test/harness';
import { comp1 } from './fixtures';
import _merge from 'lodash/merge';
import comp3 from './fixtures/comp3';
import comp4 from './fixtures/comp4';

describe('Component', () => {
  it('Should create a Component', (done) => {
    const component = new Component();

    // Test that we have a proper constructed component.
    assert.equal(component.options.renderMode, 'form');
    assert.equal(component.options.attachMode, 'full');
    assert.equal(component.attached, false);
    assert.equal(component.rendered, false);
    done();
  });

  it('Should build a base component', () => {
    return Harness.testCreate(Component, { type: 'base' }).then((component) => {
      const element = component.element.querySelector('[ref="component"]');
      assert.equal(element.textContent.trim(), 'Unknown component: base');
    });
  });

  it('Should provide required validation', (done) => {
    Harness.testCreate(Component, _merge({}, comp1, {
      validate: { required: true }
    })).then((component) => Harness.testComponent(component, {
      bad: {
        value: '',
        field: 'firstName',
        error: 'First Name is required'
      },
      good: {
        value: 'te'
      }
    }, done)).catch(done);
  });

  it('Should provide minLength validation', (done) => {
    Harness.testCreate(Component, _merge({}, comp1, {
      validate: { minLength: 2 }
    })).then((component) => Harness.testComponent(component, {
      bad: {
        value: 't',
        field: 'firstName',
        error: 'First Name must have at least 2 characters.'
      },
      good: {
        value: 'te'
      }
    }, done));
  });

  it('Should provide maxLength validation', (done) => {
    Harness.testCreate(Component, _merge({}, comp1, {
      validate: { maxLength: 5 }
    })).then((component) => Harness.testComponent(component, {
      bad: {
        value: 'testte',
        field: 'firstName',
        error: 'First Name must have no more than 5 characters.'
      },
      good: {
        value: 'te'
      }
    }, done));
  });

  it('Should provide maxWords validation', (done) => {
    Harness.testCreate(Component, _merge({}, comp1, {
      validate: { maxWords: 2 }
    })).then((component) => Harness.testComponent(component, {
      bad: {
        value: 'test test test',
        field: 'firstName',
        error: 'First Name must have no more than 2 words.'
      },
      good: {
        value: 'te st'
      }
    }, done));
  });

  it('Should provide minWords validation', (done) => {
    Harness.testCreate(Component, _merge({}, comp1, {
      validate: { minWords: 2 }
    })).then((component) => Harness.testComponent(component, {
      bad: {
        value: 'test',
        field: 'firstName',
        error: 'First Name must have at least 2 words.'
      },
      good: {
        value: 'te st'
      }
    }, done));
  });

  it('Should provide custom validation', (done) => {
    Harness.testCreate(Component, _merge({}, comp1, {
      validate: {
        custom: 'valid = (input !== "Joe") ? true : "You cannot be Joe"'
      }
    })).then((component) => Harness.testComponent(component, {
      bad: {
        value: 'Joe',
        field: 'firstName',
        error: 'You cannot be Joe'
      },
      good: {
        value: 'Tom'
      }
    }, done));
  });

  it('Should provide json validation', (done) => {
    Harness.testCreate(Component, _merge({}, comp1, {
      validate: {
        json: {
          'if': [
            {
              '===': [
                { var: 'data.firstName' },
                'Joe'
              ]
            },
            true,
            'You must be Joe'
          ]
        }
      }
    })).then((component) => Harness.testComponent(component, {
      bad: {
        value: 'Tom',
        field: 'firstName',
        error: 'You must be Joe'
      },
      good: {
        value: 'Joe'
      }
    }, done));
  });

  describe('shouldSkipValidation', () => {
    it('should return true if component is hidden', done => {
      Harness.testCreate(Component, comp1)
        .then(cmp => {
          cmp.visible = false;
          cmp.checkCondition = () => true;
          expect(cmp.visible).to.be.false;
          expect(cmp.checkCondition()).to.be.true;
          expect(cmp.shouldSkipValidation()).to.be.true;
          done();
        }, done)
        .catch(done);
    });

    it('should return true if component is conditionally hidden', done => {
      Harness.testCreate(Component, comp1)
        .then(cmp => {
          cmp.visible = true;
          cmp.checkCondition = () => false;
          expect(cmp.visible).to.be.true;
          expect(cmp.checkCondition()).to.be.false;
          expect(cmp.shouldSkipValidation()).to.be.true;
          done();
        }, done)
        .catch(done);
    });

    it('should return false if not hidden', done => {
      Harness.testCreate(Component, comp1)
        .then(cmp => {
          cmp.visible = true;
          cmp.checkCondition = () => true;
          expect(cmp.visible).to.be.true;
          expect(cmp.checkCondition()).to.be.true;
          expect(cmp.shouldSkipValidation()).to.be.false;
          done();
        }, done)
        .catch(done);
    });
  });

  describe('Component Modal', () => {
    it('Modal window should stay opened after redrawing component if it was opened ont hte moment of calling', (done) => {
      Harness.testCreate(Component, comp3).then((component) => {
        component.componentModal.openModal();
        component.redraw().then(() => {
          const isVisible = !component.componentModal.refs.modalWrapper.classList.contains('component-rendering-hidden');
          assert(isVisible);
          done();
        }).catch(done);
      }).catch(done);
    });
  });
});

it('Should return value for HTML mode', () => {
  return Harness.testCreate(Component, comp1).then((component) => {
    assert.equal(component.itemValueForHTMLMode(['option 1', 'option 2', 'option 3']), 'option 1, option 2, option 3');
    assert.equal(component.itemValueForHTMLMode(['option 1', ['option 2', 'option 3']]), 'option 1, option 2, option 3');
    assert.equal(component.itemValueForHTMLMode(['2020-03-18T15:00:00.000Z', '2020-03-31T09:05:00.000Z']), '2020-03-18T15:00:00.000Z, 2020-03-31T09:05:00.000Z');
    assert.equal(component.itemValueForHTMLMode('test'), 'test');
  });
});

it('Should protect against change loops', function(done) {
  const formElement = document.createElement('div');
  const form = new Webform(formElement);
  const formJson = {
    components: [
      {
        key: 'textField',
        label: 'Text Field',
        type: 'textfield',
        calculateValue: "value = value + '_calculated'",
      },
    ],
  };

  form.setForm(formJson).then(() => {
    const textField = form.getComponent('textField');
    const spy = sinon.spy(textField, 'calculateComponentValue');
    form.onChange({ textField: 'test' });

    setTimeout(() => {
      expect(spy.calledOnce).to.be.true;

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

it('Should mark as invalid only invalid fields in multiple components', function(done) {
  const formElement = document.createElement('div');
  const form = new Webform(formElement);
  const formJson = {
    components: [
      {
        label: 'Email',
        tableView: true,
        multiple: true,
        validate: {
          required: true
        },
        key: 'email',
        type: 'email',
        input: true
      },
    ],
  };

  form.setForm(formJson).then(() => {
    return form.setSubmission({
      data: {
        email: [
          'oleg@form.io',
          'oleg@form',
          '',
        ]
      }
    });
  })
  .then(() => {
    setTimeout(() => {
      const email = form.getComponent('email');
      expect(email.refs.input[0].classList.contains('is-invalid')).to.be.false;
      expect(email.refs.input[1].classList.contains('is-invalid')).to.be.true;
      expect(email.refs.input[2].classList.contains('is-invalid')).to.be.true;
      done();
    }, 300);
  })
  .catch(done);
});

describe('shouldDisplayRedAsterisk', () => {
  it('modalPreview template should have className "field-required" if component is required', done => {
    Harness.testCreate(Component, _merge({}, comp4, {
      validate: { required: true }
    })).then(cmp => {
      assert.equal(!!cmp.element.querySelector('.field-required'), true);
      done();
    }, done)
    .catch(done);
  });
});