Home Reference Source

src/Formio.spec.js

'use strict';
import Formio from './Formio';
import _each from 'lodash/each';
import assert from 'power-assert';
import sinon from 'sinon';
import Chance from 'chance';
import fetchMock from 'fetch-mock/es5/client';
import _ from 'lodash';

const chance = Chance();
const protocol = 'https';
const domain = 'localhost:3000';
const baseUrl = `${protocol}://api.${domain}`;
Formio.setBaseUrl(baseUrl);
Formio.setToken(null);

var generateID = function() {
  return chance.string({length: 24, pool: '0123456789abcdef'});
};

var runTests = function(cb, options) {
  var tests = {};
  var noBefore = cb(tests);
  if (!noBefore) {
    beforeEach(() => {
      Formio.setBaseUrl(baseUrl);
      Formio.projectUrlSet = false;
      Formio.projectUrl = 'https://api.form.io';
    });
  }
  _each(tests, (test, path) => {
    it(`Should initialize for ${path}`, (done) => {
      if (typeof test === 'function') {
        test();
      }
      else {
        var formio = new Formio(path, options);
        for (var param in test) {
          assert.equal(formio[param], test[param], `${param} is not equal. ${formio[param]} == ${test[param]}\n`);
        }
      }
      done();
    });
  });
};

describe('Formio Constructor Tests', () => {
  runTests((tests) => {
    tests['http://form.io/project/234234234234/form/23234234234234'] = {
      projectUrl: 'http://form.io/project/234234234234',
      projectsUrl: 'http://form.io/project',
      projectId: '234234234234',
      formsUrl: 'http://form.io/project/234234234234/form',
      formUrl: 'http://form.io/project/234234234234/form/23234234234234',
      formId: '23234234234234',
      actionsUrl: 'http://form.io/project/234234234234/form/23234234234234/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://form.io/project/234234234234/form/23234234234234/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests['http://form.io/form/23234234234234'] = {
      projectUrl: 'http://form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: '',
      formsUrl: 'http://form.io/form',
      formUrl: 'http://form.io/form/23234234234234',
      formId: '23234234234234',
      actionsUrl: 'http://form.io/form/23234234234234/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://form.io/form/23234234234234/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests['http://form.io/form/23234234234234/submission/982398220983'] = {
      projectUrl: 'http://form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: '',
      formsUrl: 'http://form.io/form',
      formUrl: 'http://form.io/form/23234234234234',
      formId: '23234234234234',
      actionsUrl: 'http://form.io/form/23234234234234/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://form.io/form/23234234234234/submission',
      submissionUrl: 'http://form.io/form/23234234234234/submission/982398220983',
      submissionId: '982398220983',
      query: ''
    };
    tests['http://form.io/form/23234234234234/action/234230987872'] = {
      projectUrl: 'http://form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: '',
      formsUrl: 'http://form.io/form',
      formUrl: 'http://form.io/form/23234234234234',
      formId: '23234234234234',
      actionsUrl: 'http://form.io/form/23234234234234/action',
      actionUrl: 'http://form.io/form/23234234234234/action/234230987872',
      actionId: '234230987872',
      submissionsUrl: 'http://form.io/form/23234234234234/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests['http://form.io/project/092934882/form/23234234234234/action/234230987872'] = {
      projectUrl: 'http://form.io/project/092934882',
      projectsUrl: 'http://form.io/project',
      projectId: '092934882',
      formsUrl: 'http://form.io/project/092934882/form',
      formUrl: 'http://form.io/project/092934882/form/23234234234234',
      formId: '23234234234234',
      actionsUrl: 'http://form.io/project/092934882/form/23234234234234/action',
      actionUrl: 'http://form.io/project/092934882/form/23234234234234/action/234230987872',
      actionId: '234230987872',
      submissionsUrl: 'http://form.io/project/092934882/form/23234234234234/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests['http://api.form.io/project/092934882'] = {
      projectUrl: 'http://api.form.io/project/092934882',
      projectsUrl: 'http://api.form.io/project',
      projectId: '092934882',
      formsUrl: 'http://api.form.io/project/092934882/form',
      formUrl: '',
      formId: '',
      actionsUrl: 'http://api.form.io/project/092934882/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://api.form.io/project/092934882/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests['http://form.io/project/092934882/form/23234234234234/submission/2987388987982'] = {
      projectUrl: 'http://form.io/project/092934882',
      projectsUrl: 'http://form.io/project',
      projectId: '092934882',
      formsUrl: 'http://form.io/project/092934882/form',
      formUrl: 'http://form.io/project/092934882/form/23234234234234',
      formId: '23234234234234',
      actionsUrl: 'http://form.io/project/092934882/form/23234234234234/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://form.io/project/092934882/form/23234234234234/submission',
      submissionUrl: 'http://form.io/project/092934882/form/23234234234234/submission/2987388987982',
      submissionId: '2987388987982',
      query: ''
    };
    tests['http://form.io/project/092934882/form/23234234234234?test=hello&test2=there'] = {
      projectUrl: 'http://form.io/project/092934882',
      projectsUrl: 'http://form.io/project',
      projectId: '092934882',
      formsUrl: 'http://form.io/project/092934882/form',
      formUrl: 'http://form.io/project/092934882/form/23234234234234',
      formId: '23234234234234',
      actionsUrl: 'http://form.io/project/092934882/form/23234234234234/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://form.io/project/092934882/form/23234234234234/submission',
      submissionUrl: '',
      submissionId: '',
      query: '?test=hello&test2=there'
    };
    tests['http://project.form.io/user/login'] = {
      projectUrl: 'http://project.form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: 'project',
      formsUrl: 'http://project.form.io/form',
      formUrl: 'http://project.form.io/user/login',
      formId: 'user/login',
      actionsUrl: 'http://project.form.io/user/login/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://project.form.io/user/login/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests['http://project.form.io/user/login/submission/234234243234'] = {
      projectUrl: 'http://project.form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: 'project',
      formsUrl: 'http://project.form.io/form',
      formUrl: 'http://project.form.io/user/login',
      formId: 'user/login',
      actionsUrl: 'http://project.form.io/user/login/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://project.form.io/user/login/submission',
      submissionUrl: 'http://project.form.io/user/login/submission/234234243234',
      submissionId: '234234243234',
      query: ''
    };
    tests['http://project.form.io/user/login/action/234234243234'] = {
      projectUrl: 'http://project.form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: 'project',
      formsUrl: 'http://project.form.io/form',
      formUrl: 'http://project.form.io/user/login',
      formId: 'user/login',
      actionsUrl: 'http://project.form.io/user/login/action',
      actionUrl: 'http://project.form.io/user/login/action/234234243234',
      actionId: '234234243234',
      submissionsUrl: 'http://project.form.io/user/login/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests['http://project.form.io/user/login/action/234234243234?test=test2'] = {
      projectUrl: 'http://project.form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: 'project',
      formsUrl: 'http://project.form.io/form',
      formUrl: 'http://project.form.io/user/login',
      formId: 'user/login',
      actionsUrl: 'http://project.form.io/user/login/action',
      actionUrl: 'http://project.form.io/user/login/action/234234243234',
      actionId: '234234243234',
      submissionsUrl: 'http://project.form.io/user/login/submission',
      submissionUrl: '',
      submissionId: '',
      query: '?test=test2'
    };
    tests['http://project.form.io/user/loginform/action/234234243234?test=test2'] = {
      projectUrl: 'http://project.form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: 'project',
      formsUrl: 'http://project.form.io/form',
      formUrl: 'http://project.form.io/user/loginform',
      formId: 'user/loginform',
      actionsUrl: 'http://project.form.io/user/loginform/action',
      actionUrl: 'http://project.form.io/user/loginform/action/234234243234',
      actionId: '234234243234',
      submissionsUrl: 'http://project.form.io/user/loginform/submission',
      submissionUrl: '',
      submissionId: '',
      query: '?test=test2'
    };
    tests['http://project.form.io/user/loginform/submission'] = {
      projectUrl: 'http://project.form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: 'project',
      formsUrl: 'http://project.form.io/form',
      formUrl: 'http://project.form.io/user/loginform',
      formId: 'user/loginform',
      actionsUrl: 'http://project.form.io/user/loginform/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://project.form.io/user/loginform/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests['http://project.form.io/user'] = {
      projectUrl: 'http://project.form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: 'project',
      formsUrl: 'http://project.form.io/form',
      formUrl: 'http://project.form.io/user',
      formId: 'user',
      actionsUrl: 'http://project.form.io/user/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://project.form.io/user/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests['http://project.form.io/user/actionform/submission/2342424234234'] = {
      projectUrl: 'http://project.form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: 'project',
      formsUrl: 'http://project.form.io/form',
      formUrl: 'http://project.form.io/user/actionform',
      formId: 'user/actionform',
      actionsUrl: 'http://project.form.io/user/actionform/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://project.form.io/user/actionform/submission',
      submissionUrl: 'http://project.form.io/user/actionform/submission/2342424234234',
      submissionId: '2342424234234',
      query: ''
    };
    tests['http://project.form.io/user/actionform/?test=foo'] = {
      projectUrl: 'http://project.form.io',
      projectsUrl: `${baseUrl}/project`,
      projectId: 'project',
      formsUrl: 'http://project.form.io/form',
      formUrl: 'http://project.form.io/user/actionform',
      formId: 'user/actionform',
      actionsUrl: 'http://project.form.io/user/actionform/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'http://project.form.io/user/actionform/submission',
      submissionUrl: '',
      submissionId: '',
      query: '?test=foo'
    };
  });
});

describe('Localhost Constructor Tests', () => {
  var testBaseUrl = 'localhost:3000';
  var projectName = 'myproject';
  var projectUrl = `${protocol}://${projectName}.${testBaseUrl}`;
  runTests((tests) => {
    tests[`${projectUrl}/user/actionform/?test=foo`] = {
      projectUrl: projectUrl,
      projectsUrl: `${baseUrl}/project`,
      projectId: projectName,
      formsUrl: `${projectUrl}/form`,
      formUrl: `${projectUrl}/user/actionform`,
      formId: 'user/actionform',
      actionsUrl: `${projectUrl}/user/actionform/action`,
      actionUrl: '',
      actionId: '',
      submissionsUrl: `${projectUrl}/user/actionform/submission`,
      submissionUrl: '',
      submissionId: '',
      query: '?test=foo'
    };
    tests[`${projectUrl}/user`] = {
      projectUrl: projectUrl,
      projectsUrl: `${baseUrl}/project`,
      projectId: projectName,
      formsUrl: `${projectUrl}/form`,
      formUrl: `${projectUrl}/user`,
      formId: 'user',
      actionsUrl: `${projectUrl}/user/action`,
      actionUrl: '',
      actionId: '',
      submissionsUrl: `${projectUrl}/user/submission`,
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
  }, {base: baseUrl});
});

describe('Subdomain Constructor Tests', () => {
  var testBaseUrl = 'foo.blah.form.io';
  var projectName = 'myproject';
  var projectUrl = `${protocol}://${projectName}.${testBaseUrl}`;
  runTests((tests) => {
    tests[`${projectUrl}/user/actionform/?test=foo`] = {
      projectUrl: projectUrl,
      projectsUrl: `${baseUrl}/project`,
      projectId: projectName,
      formsUrl: `${projectUrl}/form`,
      formUrl: `${projectUrl}/user/actionform`,
      formId: 'user/actionform',
      actionsUrl: `${projectUrl}/user/actionform/action`,
      actionUrl: '',
      actionId: '',
      submissionsUrl: `${projectUrl}/user/actionform/submission`,
      submissionUrl: '',
      submissionId: '',
      query: '?test=foo'
    };
    tests[`${projectUrl}/user`] = {
      projectUrl: projectUrl,
      projectsUrl: `${baseUrl}/project`,
      projectId: projectName,
      formsUrl: `${projectUrl}/form`,
      formUrl: `${projectUrl}/user`,
      formId: 'user',
      actionsUrl: `${projectUrl}/user/action`,
      actionUrl: '',
      actionId: '',
      submissionsUrl: `${projectUrl}/user/submission`,
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
  }, {base: baseUrl});
});

describe('Subdirectory Constructor Tests', () => {
  var testBaseUrl = 'foo.blah.form.io';
  var projectName = 'myproject';
  var projectUrl = `${protocol}://${testBaseUrl}/${projectName}`;
  runTests((tests) => {
    tests[`${projectUrl}/user/actionform/?test=foo`] = {
      projectUrl: projectUrl,
      projectsUrl: `${protocol}://${testBaseUrl}/project`,
      projectId: projectName,
      formsUrl: `${projectUrl}/form`,
      formUrl: `${projectUrl}/user/actionform`,
      formId: 'user/actionform',
      actionsUrl: `${projectUrl}/user/actionform/action`,
      actionUrl: '',
      actionId: '',
      submissionsUrl: `${projectUrl}/user/actionform/submission`,
      submissionUrl: '',
      submissionId: '',
      query: '?test=foo'
    };
    tests[`${projectUrl}/user`] = {
      projectUrl: projectUrl,
      projectsUrl: `${protocol}://${testBaseUrl}/project`,
      projectId: projectName,
      formsUrl: `${projectUrl}/form`,
      formUrl: `${projectUrl}/user`,
      formId: 'user',
      actionsUrl: `${projectUrl}/user/action`,
      actionUrl: '',
      actionId: '',
      submissionsUrl: `${projectUrl}/user/submission`,
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests[projectUrl] = {
      projectUrl: projectUrl,
      projectsUrl: `${protocol}://${testBaseUrl}/project`,
      projectId: projectName,
      formsUrl: `${projectUrl}/form`,
      formUrl: projectUrl,
      formId: '',
      actionsUrl: `${projectUrl}/action`,
      actionUrl: '',
      actionId: '',
      submissionsUrl: `${projectUrl}/submission`,
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
  }, {base: `${protocol}://${testBaseUrl}`});
});

describe('Simple Form Constructor Tests', () => {
  runTests((tests) => {
    tests['init'] = () => {
      Formio.setBaseUrl('https://api.form.io');
      Formio.projectUrlSet = false;
      Formio.projectUrl = 'https://api.form.io';
    };
    tests['https://examples.form.io/example'] = {
      projectUrl: 'https://examples.form.io',
      projectsUrl: '',
      projectId: '',
      formsUrl: 'https://examples.form.io/form',
      formUrl: 'https://examples.form.io/example',
      formId: 'example',
      actionsUrl: 'https://examples.form.io/example/action',
      actionUrl: '',
      actionId: '',
      submissionsUrl: 'https://examples.form.io/example/submission',
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    return true;
  });
});

describe('Open Source Constructor Tests', () => {
  const formBaseUrl = 'http://localhost:3000';
  runTests((tests) => {
    tests[`${formBaseUrl}/user`] = {
      projectUrl: formBaseUrl,
      projectsUrl: '',
      projectId: '',
      formsUrl: `${formBaseUrl}/form`,
      formUrl: `${formBaseUrl}/user`,
      formId: 'user',
      actionsUrl: `${formBaseUrl}/user/action`,
      actionUrl: '',
      actionId: '',
      submissionsUrl: `${formBaseUrl}/user/submission`,
      submissionUrl: '',
      submissionId: '',
      query: ''
    };
    tests[`${formBaseUrl}/user/actionform/?test=foo`] = {
      projectUrl: formBaseUrl,
      projectsUrl: '',
      projectId: '',
      formsUrl: `${formBaseUrl}/form`,
      formUrl: `${formBaseUrl}/user/actionform`,
      formId: 'user/actionform',
      actionsUrl: `${formBaseUrl}/user/actionform/action`,
      actionUrl: '',
      actionId: '',
      submissionsUrl: `${formBaseUrl}/user/actionform/submission`,
      submissionUrl: '',
      submissionId: '',
      query: '?test=foo'
    };
  }, {base: formBaseUrl, project: formBaseUrl});
});

describe('Plugins', () => {
  var plugin = null;
  beforeEach(() => {
    assert.equal(Formio.getPlugin('test-plugin'), undefined, 'No plugin may be returned under the name `test-plugin`');
    plugin = {init: sinon.spy()};
    Formio.registerPlugin(plugin, 'test-plugin');
    assert.ok(plugin.init.calledOnce, 'plugin.init must be called exactly once');
    assert.ok(plugin.init.calledOn(plugin), 'plugin.init must be called on plugin as `this`');
    assert.ok(plugin.init.calledWithExactly(Formio), 'plugin.init must be given Formio as argument');
    assert.equal(Formio.getPlugin('test-plugin'), plugin, 'getPlugin must return plugin');
  });

  afterEach(() => {
    assert.equal(Formio.getPlugin('test-plugin'), plugin, 'getPlugin must return plugin');
    plugin.deregister = sinon.spy();
    Formio.deregisterPlugin(plugin, 'test-plugin');
    assert.ok(plugin.deregister.calledOnce, 'plugin.deregister must be called exactly once');
    assert.ok(plugin.deregister.calledOn(plugin), 'plugin.deregister must be called on plugin as `this`');
    assert.ok(plugin.deregister.calledWithExactly(Formio), 'plugin.deregister must be given Formio as argument');
    assert.equal(Formio.getPlugin('test-plugin'), undefined, 'No plugin may be returned under the name `test-plugin`');
  });

  // Test a request to see if the plugin flow order is correct
  var testRequest = function testRequest(url, method, type) {
    var fnName;
    switch (method) {
      case 'GET': fnName = `load${_.capitalize(type)}`; break;
      case 'POST':
      case 'PUT': fnName = `save${_.capitalize(type)}`; break;
      case 'DELETE': fnName = `delete${_.capitalize(type)}`; break;
    }

    it(`Plugin ${method} ${fnName}`, (done) => {
      let step = 0;
      var formio = new Formio(url);
      method = method.toUpperCase();
      var testData = {testRequest: 'TEST_REQUEST'};
      var testOpts = {testOption: true};
      var testResult = {_id: 'TEST_ID', testResult: 'TEST_RESULT'};

      var expectedArgs = {
        formio: formio,
        type: type,
        method: method,
        url: formio[type + (method === 'POST' ? 'sUrl' : 'Url')],
        data: _.startsWith(fnName, 'save') ? testData : null,
        opts: testOpts
      };

      // Set up plugin hooks
      plugin.preRequest = function(requestArgs) {
        assert.equal(++step, 1, 'preRequest hook should be called first');
        assert.deepEqual(requestArgs, expectedArgs, 'Request hook arguments match expected arguments');
        return Promise.resolve()
          .then(() => {
            assert.equal(++step, 3, 'preRequest promise should resolve third');
            // TODO
          });
      };
      plugin.request = function(requestArgs) {
        assert.equal(++step, 4, 'request hook should be called fourth');
        assert.deepEqual(requestArgs, expectedArgs, 'Request hook arguments match expected arguments');
        return Promise.resolve()
          .then(() => {
            assert.equal(++step, 5, 'request promise should resolve fifth');
            return testResult;
          });
      };
      plugin.wrapRequestPromise = function(promise, requestArgs) {
        assert.equal(++step, 2, 'wrapRequestPromise hook should be called second');
        assert.deepEqual(requestArgs, expectedArgs, 'Request hook arguments match expected arguments');
        return promise.then((result) => {
          assert.equal(++step, 6, 'wrapRequestPromise post-result promise should resolve sixth');
          assert.deepEqual(result, testResult, 'Result should match result from request hook');
          return result;
        });
      };

      var promise;
      if (_.startsWith(fnName, 'save')) {
        promise = formio[fnName](testData, testOpts);
      }
      else if (_.startsWith(fnName, 'load')) {
        promise = formio[fnName](null, testOpts);
      }
      else {
        promise = formio[fnName](testOpts);
      }
      promise.then((result) => {
        assert.equal(++step, 7, 'post request promise should resolve last');
        assert.deepEqual(result, testResult, 'Result should match result from request hook');
        done();
      });
    });
  };

  var tests = [
    {
      url: 'https://api.localhost:3000/project/myproject',
      method: 'GET',
      type: 'project'
    },
    {
      url: '',
      method: 'POST',
      type: 'project'
    },
    {
      url: 'https://api.localhost:3000/project/myproject',
      method: 'PUT',
      type: 'project'
    },
    {
      url: 'https://api.localhost:3000/project/myproject',
      method: 'DELETE',
      type: 'project'
    },

    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567',
      method: 'GET',
      type: 'form'
    },
    {
      url: 'https://api.localhost:3000/project/myproject',
      method: 'POST',
      type: 'form'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567',
      method: 'PUT',
      type: 'form'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567',
      method: 'DELETE',
      type: 'form'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/',
      method: 'GET',
      type: 'forms'
    },

    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/submission/76543210FEDCBA9876543210',
      method: 'GET',
      type: 'submission'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567',
      method: 'POST',
      type: 'submission'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/submission/76543210FEDCBA9876543210',
      method: 'PUT',
      type: 'submission'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/submission/76543210FEDCBA9876543210',
      method: 'DELETE',
      type: 'submission'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567',
      method: 'GET',
      type: 'submissions'
    },

    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/action/76543210FEDCBA9876543210',
      method: 'GET',
      type: 'action'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567',
      method: 'POST',
      type: 'action'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/action/76543210FEDCBA9876543210',
      method: 'PUT',
      type: 'action'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567/action/76543210FEDCBA9876543210',
      method: 'DELETE',
      type: 'action'
    },
    {
      url: 'https://api.localhost:3000/project/myproject/form/0123456789ABCDEF01234567',
      method: 'GET',
      type: 'actions'
    }
  ];

  tests.forEach((test) => {
    testRequest(test.url, test.method, test.type);
  });

  var testStaticRequest = function testStaticRequest(fnName, url, method) {
    it(`Plugin ${fnName}`, (done) => {
      var step = 0;
      var testResult = {_id: 'TEST_ID', testResult: 'TEST_RESULT'};
      var expectedArgs = {
        url: url,
        method: method,
        data: null,
        opts: {}
      };

      // Set up plugin hooks
      plugin.preRequest = function(requestArgs) {
        assert.equal(++step, 1, 'preRequest hook should be called first');
        assert.deepEqual(requestArgs, expectedArgs, 'Request hook arguments match expected arguments');
        return Promise.resolve()
          .then(() => {
            assert.equal(++step, 3, 'preRequest promise should resolve third');
            // TODO
          });
      };
      plugin.staticRequest = function(requestArgs) {
        assert.equal(++step, 4, 'request hook should be called fourth');
        assert.deepEqual(requestArgs, expectedArgs, 'Request hook arguments match expected arguments');
        return Promise.resolve()
          .then(() => {
            assert.equal(++step, 5, 'request promise should resolve fifth');
            return testResult;
          });
      };
      plugin.wrapStaticRequestPromise = function(promise, requestArgs) {
        assert.equal(++step, 2, 'wrapRequestPromise hook should be called second');
        assert.deepEqual(requestArgs, expectedArgs, 'Request hook arguments match expected arguments');
        return promise.then((result) => {
          assert.equal(++step, 6, 'wrapRequestPromise post-result promise should resolve sixth');
          assert.deepEqual(result, testResult, 'Result should match result from request hook');
          return result;
        });
      };

      Formio[fnName]()
        .then((result) => {
          assert.equal(++step, 7, 'post request promise should resolve last');
          assert.deepEqual(result, testResult, 'Result should match result from request hook');
          done();
        });
    });
  };

  var staticTests = [
    {
      fnName: 'loadProjects',
      url: 'https://api.localhost:3000/project',
      method: 'GET'
    },
    {
      fnName: 'logout',
      url: 'https://api.localhost:3000/logout',
      method: 'GET'
    }
  ];

  staticTests.forEach((test) => {
    testStaticRequest(test.fnName, test.url, test.method, test.type);
  });

  var testFileRequest = function testFileRequest(fnName, formUrl, args) {
    it(`Plugin ${fnName}`, (done) => {
      var step = 0;
      var testResult = {_id: 'TEST_ID', testResult: 'TEST_RESULT'};

      if (fnName == 'downloadFile') {
        var expectedArgs = {
          method: 'download',
          file: args[0]
        };
      }
      else if (fnName === 'uploadFile') {
        var expectedArgs = {
          provider: args[0],
          method: 'upload',
          file: args[1],
          fileName: args[2],
          dir: args[3]
        };
      }

      // Set up plugin hooks
      plugin.preRequest = function(requestArgs) {
        assert.equal(++step, 1, 'preRequest hook should be called first');
        assert.deepEqual(requestArgs, expectedArgs, 'Request hook arguments match expected arguments');
        return Promise.resolve()
          .then(() => {
            assert.equal(++step, 3, 'preRequest promise should resolve third');
            // TODO
          });
      };
      plugin.fileRequest = function(requestArgs) {
        assert.equal(++step, 4, 'request hook should be called fourth');
        assert.deepEqual(requestArgs, expectedArgs, 'Request hook arguments match expected arguments');
        return Promise.resolve()
          .then(() => {
            assert.equal(++step, 5, 'request promise should resolve fifth');
            return testResult;
          });
      };
      plugin.wrapFileRequestPromise = function(promise, requestArgs) {
        assert.equal(++step, 2, 'wrapFileRequestPromise hook should be called second');
        assert.deepEqual(requestArgs, expectedArgs, 'Request hook arguments match expected arguments');
        return promise.then((result) => {
          assert.equal(++step, 6, 'wrapFileRequestPromise post-result promise should resolve sixth');
          assert.deepEqual(result, testResult, 'Result should match result from request hook');
          return result;
        });
      };

      var formio = new Formio(formUrl);
      formio[fnName].apply(null, args)
        .then((result) => {
          assert.equal(++step, 7, 'post request promise should resolve last');
          assert.deepEqual(result, testResult, 'Result should match result from request hook');
          done();
        });
    });
  };

  var fileTests = [
    {
      fnName: 'uploadFile',
      formUrl: 'https://api.localhost:3000/project/123/form/123',
      args: [
        's3',
        'FILE',
        'file.jpg',
        'dir/'
      ]
    },
    {
      fnName: 'uploadFile',
      formUrl: 'https://api.localhost:3000/project/123/form/123',
      args: [
        'dropbox',
        'FILE',
        'file.jpg',
        'dir/'
      ]
    },
    {
      fnName: 'downloadFile',
      formUrl: 'https://api.localhost:3000/project/123/form/123',
      args: [
        {
          storage: 's3',
          name: 'test'
        }
      ]
    },
    {
      fnName: 'downloadFile',
      formUrl: 'https://api.localhost:3000/project/123/form/123',
      args: [
        {
          storage: 'dropbox',
          name: 'test'
        }
      ]
    }
  ];

  fileTests.forEach((test) => {
    testFileRequest(test.fnName, test.formUrl, test.args);
  });
});
describe('Test Formio.js capabilities', () => {
  var testCapability = function(test) {
    it(test.name, (done) => {
      if (test.mock) {
        var mock = test.mock();
        if (mock instanceof Array) {
          _.each(mock, (_mock) => {
            fetchMock.mock(_mock.url, _mock.response, {method: _mock.method});
          });
        }
        else {
          fetchMock.mock(mock.url, mock.response, {method: mock.method});
        }
      }
      Promise.resolve()
        .then(() => {
          return test.test();
        })
        .then(() => {
          if (test.mock) fetchMock.restore();
          done();
        })
        .catch((err) => {
          assert.equal(err, null, 'Caught error during test');
          if (err) console.error(err.stack);
          if (test.mock) fetchMock.restore();
          done();
        });
    });
  };

  var user;
  var userPassword;
  var userToken = chance.string({length: 450});
  var userFormId = generateID();
  var project;
  var form;
  var submission;

  var tests = [
    {
      name: 'Registering user.',
      test: function() {
        var req = {
          data: {
            'user.name': chance.string({
              length: 10,
              pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
            }),
            'user.email': chance.email(),
            'user.password': chance.string({length: 12})
          }
        };
        var formio = new Formio(`${Formio.getBaseUrl()}/user/register`);
        return formio.saveSubmission(req)
          .then((response) => {
            assert.deepEqual(response, user, 'saveSubmission response should match test user');
            assert.equal(Formio.getToken(), userToken, 'Formio should save the user token');
          });
      },
      mock: function() {
        return [
          {
            url: `${Formio.getBaseUrl()}/current`,
            method: 'GET',
            response: function() {
              return {
                headers: {
                  'Content-Type': 'application/json',
                  'x-jwt-token': userToken
                },
                body: user
              };
            }
          },
          {
            url: `${Formio.getBaseUrl()}/user/register/submission`,
            method: 'POST',
            response: function(url, opts) {
              var body = JSON.parse(opts.body);
              var userId = generateID();
              user = {
                _id: userId,
                created: new Date().toISOString(),
                modified: new Date().toISOString(),
                data: {
                  email: body.data['user.email'],
                  name: body.data['user.name']
                },
                externalIds: [],
                externalTokens: [],
                form: userFormId,
                owner: userId
              };
              userPassword = body.data['user.password'];
              return {
                headers: {
                  'Content-Type': 'application/json',
                  'x-jwt-token': userToken
                },
                body: user
              };
            }
          }
        ];
      }
    },
    {
      name: 'Logging in.',
      test: function() {
        var req = {
          data: {
            'user.email': user.data.email,
            'user.password': userPassword
          }
        };
        var formio = new Formio(`${Formio.getBaseUrl()}/user/login`);
        return formio.saveSubmission(req)
          .then((response) => {
            assert.deepEqual(response, user, 'saveSubmission response should match test user');
            assert.equal(Formio.getToken(), userToken, 'Formio should save the user token');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/user/login/submission`,
          method: 'POST',
          response: function(url, opts) {
            var body = JSON.parse(opts.body);
            userToken = chance.string({length: 450});
            assert.equal(body.data['user.email'], user.data.email, 'Login email must be correct.');
            assert.equal(body.data['user.password'], userPassword, 'Login password must be correct.');
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: user
            };
          }
        };
      }
    },
    {
      name: 'Current user.',
      test: function() {
        return Formio.currentUser()
          .then((response) => {
            assert.deepEqual(response, user, 'currentUser response should match test user');
            return Formio.currentUser();
          })
          .then((response) => {
            assert.deepEqual(response, user, 'currentUser response should match test user');
          });
      },
      mock: function() {
        var called = false;
        return {
          url: `${Formio.getBaseUrl()}/current`,
          method: 'GET',
          response: function() {
            assert.ok(!called, 'User should be requested only once.');
            called = true;
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: user
            };
          }
        };
      }
    },
    {
      name: 'Create Project',
      test: function() {
        var formio = new Formio();
        var req = {
          title: chance.string({
            length: 10,
            pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
          }),
          name: chance.string({
            length: 10,
            pool: 'abcdefghijklmnopqrstuvwxyz'
          }),
          description: chance.paragraph({sentences: 1}),
          settings: {
            cors: '*'
          },
          template: 'http://help.form.io/templates/empty.json'
        };
        return formio.saveProject(req)
          .then((response) => {
            assert.deepEqual(response, project, 'saveProject response should match test user');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project`,
          method: 'POST',
          response: function(url, opts) {
            var body = JSON.parse(opts.body);
            var projectId = generateID();
            project = {
              _id: projectId,
              created: new Date().toISOString(),
              modified: new Date().toISOString(),
              apiCalls: {
                used: 0,
                limit: 1000,
                remaining: 1000,
                reset: new Date(Date.now() + 2.628e9).toISOString() // ~1 month later
              },
              access: [],
              title: body.title,
              name: body.name,
              description: body.description,
              plan: 'basic',
              owner: user._id
            };
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: project
            };
          }
        };
      }
    },
    {
      name: 'Getting Projects',
      test: function() {
        return Formio.loadProjects()
          .then((projects) => {
            assert.equal(projects.length, 1, 'Should return only one project.');
            assert.equal(projects.skip, 0, 'skip should be 0.');
            assert.equal(projects.limit, 1, 'limit should be 1.');
            assert.equal(projects.serverCount, 1, 'serverCount should be 1.');
            assert.deepEqual(projects[0], project, 'Should match project');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project`,
          method: 'GET',
          response: function() {
            return {
              headers: {
                'Content-Type': 'application/json',
                'Content-Range': '0-0/1',
                'Range-Unit': 'items',
                'x-jwt-token': userToken
              },
              body: [project]
            };
          }
        };
      }
    },
    {
      name: 'Read Project',
      test: function() {
        var formio = new Formio(`${Formio.getBaseUrl()}/project/${project._id}`);
        return formio.loadProject()
          .then((response) => {
            assert.deepEqual(response, project, 'Should match project');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}`,
          method: 'GET',
          response: function() {
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: project
            };
          }
        };
      }
    },
    {
      name: 'Update Project',
      test: function() {
        var formio = new Formio(`/project/${project._id}`);
        var newProject = _.cloneDeep(project);
        newProject.name = chance.string({
          length: 10,
          pool: 'abcdefghijklmnopqrstuvwxyz'
        });
        newProject.title = chance.string({
          length: 10,
          pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
        });
        newProject.description = chance.paragraph({sentences: 1});
        return formio.saveProject(newProject)
          .then((response) => {
            assert.deepEqual(response, project, 'Project should match');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}`,
          method: 'PUT',
          response: function(url, opts) {
            var body = JSON.parse(opts.body);
            project = body;
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: project
            };
          }
        };
      }
    },
    {
      name: 'Create Form',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form`);
        var req = {
          title: chance.string({
            length: 10,
            pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
          }),
          name: chance.string({
            length: 10,
            pool: 'abcdefghijklmnopqrstuvwxyz'
          }),
          path: chance.string({
            length: 10,
            pool: 'abcdefghijklmnopqrstuvwxyz'
          }),
          components: [
            {
              defaultValue: '',
              input: true,
              inputMask: '',
              inputType: 'text',
              isNew: false,
              key: 'fieldLabel',
              label: 'Field Label',
              multiple: false,
              persistent: true,
              placeholder: '',
              prefix: '',
              protected: false,
              suffix: '',
              tableView: true,
              type: 'textfield',
              unique: false,
              validate: {
                required: false,
                minLength: '',
                maxLength: '',
                pattern: '',
                custom: '',
                customPrivate: false
              }
            },
            {
              action: 'submit',
              block: false,
              disableOnInvalid: true,
              input: true,
              key: 'submit',
              label: 'Submit',
              leftIcon: '',
              rightIcon: '',
              size: 'md',
              tableView: false,
              theme: 'primary',
              type: 'button'
            }
          ],
          type: 'form',
          access: [],
          submissionAccess: []
        };
        return formio.saveForm(req)
          .then((response) => {
            assert.deepEqual(response, form, 'Form should match');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form`,
          method: 'POST',
          response: function(url, opts) {
            var body = JSON.parse(opts.body);
            var formId = generateID();
            form = _.cloneDeep(body);
            _.assign(form, {
              _id: formId,
              created: new Date().toISOString(),
              modified: new Date().toISOString(),
              project: project._id,
              owner: user._id
            });
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: form
            };
          }
        };
      }
    },
    {
      name: 'Load Forms',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form`);
        return formio.loadForms()
          .then((forms) => {
            assert.equal(forms.length, 1, 'Should return only one form.');
            assert.equal(forms.skip, 0, 'skip should be 0.');
            assert.equal(forms.limit, 1, 'limit should be 1.');
            assert.equal(forms.serverCount, 1, 'serverCount should be 1.');
            assert.deepEqual(forms[0], form, 'Should match form');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form`,
          method: 'GET',
          response: function() {
            return {
              headers: {
                'Content-Type': 'application/json',
                'Content-Range': '0-0/1',
                'Range-Unit': 'items',
                'x-jwt-token': userToken
              },
              body: [form]
            };
          }
        };
      }
    },
    {
      name: 'Read Form',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form/${form._id}`);
        return formio.loadForm()
          .then((response) => {
            assert.deepEqual(response, form, 'Form should match');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}`,
          method: 'GET',
          response: function() {
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: form
            };
          }
        };
      }
    },
    {
      name: 'Update Form',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form/${form._id}`);
        var newForm = _.cloneDeep(form);
        newForm.title = chance.string({
          length: 10,
          pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
        });
        newForm.name = chance.string({
          length: 10,
          pool: 'abcdefghijklmnopqrstuvwxyz'
        });
        newForm.path = chance.string({
          length: 10,
          pool: 'abcdefghijklmnopqrstuvwxyz'
        });
        return formio.saveForm(newForm)
          .then((response) => {
            assert.deepEqual(response, form, 'Form should match');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}`,
          method: 'PUT',
          response: function(url, opts) {
            var body = JSON.parse(opts.body);
            form = body;
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: form
            };
          }
        };
      }
    },
    {
      name: 'Create Submission',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form/${form._id}/submission`);
        var req = {
          data: {
            fieldLabel: chance.string()
          }
        };
        return formio.saveSubmission(req)
          .then((response) => {
            assert.deepEqual(response, submission, 'Submission should match');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission`,
          method: 'POST',
          response: function(url, opts) {
            var body = JSON.parse(opts.body);
            var submissionId = generateID();
            submission = {
              _id: submissionId,
              created: new Date().toISOString(),
              modified: new Date().toISOString(),
              data: body.data,
              externalIds: [],
              externalTokens: [],
              form: form._id,
              owner: user._id,
              roles: []
            };
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: submission
            };
          }
        };
      }
    },
    {
      name: 'Load Submissions',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form/${form._id}/submission`);
        return formio.loadSubmissions()
          .then((submissions) => {
            assert.equal(submissions.length, 1, 'Should return only one submission.');
            assert.equal(submissions.skip, 0, 'skip should be 0.');
            assert.equal(submissions.limit, 1, 'limit should be 1.');
            assert.equal(submissions.serverCount, 1, 'serverCount should be 1.');
            assert.deepEqual(submissions[0], submission, 'Should match submission');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission`,
          method: 'GET',
          response: function() {
            return {
              headers: {
                'Content-Type': 'application/json',
                'Content-Range': '0-0/1',
                'Range-Unit': 'items',
                'x-jwt-token': userToken
              },
              body: [submission]
            };
          }
        };
      }
    },
    {
      name: 'Read Submission',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form/${form._id}/submission/${submission._id}`);
        return formio.loadSubmission()
          .then((response) => {
            assert.deepEqual(response, submission, 'Submission should match');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission/${submission._id}`,
          method: 'GET',
          response: function() {
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: submission
            };
          }
        };
      }
    },
    {
      name: 'Update Submission',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form/${form._id}/submission/${submission._id}`);
        var newSubmission = _.cloneDeep(submission);
        newSubmission.data.fieldLabel = chance.string();
        return formio.saveSubmission(newSubmission)
          .then((response) => {
            assert.deepEqual(response, submission, 'Submission should match');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission/${submission._id}`,
          method: 'PUT',
          response: function(url, opts) {
            var body = JSON.parse(opts.body);
            submission = body;
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: submission
            };
          }
        };
      }
    },
    {
      name: 'Update Submission without ID',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form/${form._id}`);
        var newSubmission = _.cloneDeep(submission);
        newSubmission.data.fieldLabel = chance.string();
        return formio.saveSubmission(newSubmission)
          .then((response) => {
            assert.deepEqual(response, submission, 'Submission should match');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission/${submission._id}`,
          method: 'PUT',
          response: function(url, opts) {
            var body = JSON.parse(opts.body);
            submission = body;
            return {
              headers: {
                'Content-Type': 'application/json',
                'x-jwt-token': userToken
              },
              body: submission
            };
          }
        };
      }
    },
    // // Actions
    // // Available Actions
    // // Action Info
    {
      name: 'Delete Submission',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form/${form._id}/submission/${submission._id}`);
        return formio.deleteSubmission()
          .then((response) => {
            assert.equal(response, 'OK', 'Submission should be deleted.');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}/submission/${submission._id}`,
          method: 'DELETE',
          response: {
            status: 200,
            body: 'OK',
            headers: {
              'Content-Type': 'text/plain; charset=utf-8',
              'x-jwt-token': userToken
            }
          }
        };
      }
    },
    {
      name: 'Delete Form',
      test: function() {
        var formio = new Formio(`/project/${project._id}/form/${form._id}`);
        return formio.deleteForm()
          .then((response) => {
            assert.equal(response, 'OK', 'Submission should be deleted.');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/form/${form._id}`,
          method: 'DELETE',
          response: {
            status: 200,
            body: 'OK',
            headers: {
              'Content-Type': 'text/plain; charset=utf-8',
              'x-jwt-token': userToken
            }
          }
        };
      }
    },
    {
      name: 'Delete Project',
      test: function() {
        var formio = new Formio(`/project/${project._id}`);
        return formio.deleteProject()
          .then((response) => {
            assert.equal(response, 'OK', 'Submission should be deleted.');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}`,
          method: 'DELETE',
          response: {
            status: 200,
            body: 'OK',
            headers: {
              'Content-Type': 'text/plain; charset=utf-8',
              'x-jwt-token': userToken
            }
          }
        };
      }
    },
    {
      name: 'Getting Projects',
      test: function() {
        return Formio.loadProjects()
          .then((projects) => {
            assert.equal(projects.length, 0, 'Should return no projects.');
            assert.equal(projects.skip, undefined, 'skip should be undefined.');
            assert.equal(projects.limit, undefined, 'limit should be undefined.');
            assert.equal(projects.serverCount, 0, 'serverCount should be 0.');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project`,
          method: 'GET',
          response: function() {
            return {
              headers: {
                'Content-Type': 'application/json',
                'Content-Range': '*/0',
                'Range-Unit': 'items',
                'x-jwt-token': userToken
              },
              body: []
            };
          }
        };
      }
    },
    {
      name: 'Temporary Token',
      test: function() {
        var formio = new Formio(`/project/${project._id}`);
        return formio.getTempToken(200, 'GET:/current').then((tempToken) => {
          assert.equal(tempToken, userToken);
        });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/project/${project._id}/token`,
          method: 'GET',
          response: function() {
            return {
              status: 200,
              body: userToken,
              headers: {
                'Content-Type': 'text/plain; charset=utf-8',
                'x-jwt-token': userToken
              }
            };
          }
        };
      }
    },
    {
      name: 'Logging Out',
      test: function() {
        return Formio.logout()
          .then(() => {
            assert.equal(Formio.getToken(), '', 'Logged out');
          });
      },
      mock: function() {
        return {
          url: `${Formio.getBaseUrl()}/logout`,
          method: 'GET',
          response: function() {
            userToken = null;
            return {
              status: 200,
              body: 'OK',
              headers: {
                'Content-Type': 'text/plain; charset=utf-8',
                'x-jwt-token': ''
              }
            };
          }
        };
      }
    }
  ];

  tests.forEach(testCapability);
});
describe('Formio.currentUser', () => {
  var plugin = null;
  beforeEach(() => {
    plugin = {
      wrapStaticRequestPromise: sinon.spy((promise, promiseArgs) => {
        return promise;
      }),
      staticRequest: sinon.spy(() => {
        // Return dummy user
        var userId = generateID();
        return Promise.resolve({
          _id: userId,
          created: new Date().toISOString(),
          modified: new Date().toISOString(),
          data: {
            email: 'user@place.com',
            name: 'user'
          },
          externalIds: [],
          externalTokens: [],
          form: generateID(),
          owner: userId
        });
      })
    };
    Formio.registerPlugin(plugin, 'currentUserTestPlugin');
  });

  afterEach(() => {
    Formio.deregisterPlugin(plugin);
  });

  it('Initial currentUser() should make static request', (done) => {
    // Force token
    Formio.token = chance.string({length: 30});
    Formio.currentUser()
      .then(() => {
        assert.ok(plugin.staticRequest.calledOnce, 'staticRequest should be called once');
        done();
      });
    assert.ok(plugin.wrapStaticRequestPromise.calledOnce, 'wrapStaticRequestPromise should be called once');
  });

  it('Next currentUser() should return cached value', (done) => {
    // Clear token
    Formio.currentUser()
      .then(() => {
        assert.ok(!plugin.staticRequest.called, 'staticRequest should not be called');
        done();
      });
    assert.ok(plugin.wrapStaticRequestPromise.calledOnce, 'wrapStaticRequestPromise should be called once');
  });
});