formio
Version:
The formio server application.
1,527 lines (1,390 loc) • 195 kB
JavaScript
/* eslint-env mocha */
'use strict';
const request = require('./formio-supertest');
const assert = require('assert');
const _ = require('lodash');
const chance = new (require('chance'))();
const http = require('http');
const url = require('url');
const testMappingDataForm = require('./fixtures/forms/testMappingDataForm');
const customSaveSubmissionTransformForm = require('./fixtures/forms/customSaveSubmissionTransformForm');
const customSaveSubmissionTransformResource = require('./fixtures/forms/customSaveSubmissionTransformResource');
const selectBoxesSourceTypeForm = require('./fixtures/forms/selectBoxesSourceTypeForm.js');
const testSelectInEmail = require('./fixtures/forms/testSelectInEmail.js');
const { wait } = require('./util');
const testRadioInEmail = require('./fixtures/forms/testRadioInEmail.js');
const docker = process.env.DOCKER;
module.exports = (app, template, hook) => {
const Helper = require('./helper')(app);
describe('Actions', () => {
// Store the temp form for this test suite.
let tempForm = {
title: 'Temp Form',
name: 'tempForm',
path: 'temp',
type: 'form',
access: [],
submissionAccess: [],
components: [
{
type: 'textfield',
validate: {
custom: '',
pattern: '',
maxLength: '',
minLength: '',
required: false,
},
defaultValue: '',
multiple: false,
suffix: '',
prefix: '',
placeholder: 'foo',
key: 'foo',
label: 'foo',
inputMask: '',
inputType: 'text',
input: true,
},
],
};
// Store the temp action for this test suite.
let tempAction = {};
describe('Bootstrap', () => {
it('Create a Form for Action tests', (done) => {
request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(tempForm)
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
const response = res.body;
assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.');
assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.');
assert(response.hasOwnProperty('access'), 'The response should contain an the `access`.');
assert.equal(response.title, tempForm.title);
assert.equal(response.name, tempForm.name);
assert.equal(response.path, tempForm.path);
assert.equal(response.type, 'form');
assert.equal(response.access.length, 1);
assert.equal(response.access[0].type, 'read_all');
assert.equal(response.access[0].roles.length, 3);
assert(response.access[0].roles.includes(template.roles.anonymous._id.toString()));
assert(response.access[0].roles.includes(template.roles.authenticated._id.toString()));
assert(response.access[0].roles.includes(template.roles.administrator._id.toString()));
assert.deepEqual(response.submissionAccess, []);
assert.deepEqual(response.components, tempForm.components);
tempForm = response;
tempAction = {
title: 'Login',
name: 'login',
handler: ['before'],
method: ['create'],
priority: 0,
settings: {
resources: [tempForm._id.toString()],
username: 'username',
password: 'password',
},
};
// Store the JWT for future API calls.
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
});
describe('Permissions - Project Level - Project Owner', () => {
it('A Project Owner should be able to Create an Action', (done) => {
request(app)
.post(hook.alter('url', `/form/${tempForm._id}/action`, template))
.set('x-jwt-token', template.users.admin.token)
.send(tempAction)
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
const response = res.body;
assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.equal(response.title, tempAction.title);
assert.equal(response.name, tempAction.name);
assert.deepEqual(response.handler, tempAction.handler);
assert.deepEqual(response.method, tempAction.method);
assert.equal(response.priority, tempAction.priority);
assert.deepEqual(response.settings, tempAction.settings);
assert.equal(response.form, tempForm._id);
tempAction = response;
// Store the JWT for future API calls.
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
it('A Project Owner should be able to Read an Action', (done) => {
request(app)
.get(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template))
.set('x-jwt-token', template.users.admin.token)
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
if (err) {
return done(err);
}
const response = res.body;
assert.deepEqual(response, tempAction);
// Store the JWT for future API calls.
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
it('A Project Owner should be able to Update an Action', (done) => {
const updatedAction = _.clone(tempAction);
updatedAction.title = 'Updated';
request(app)
.put(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template))
.set('x-jwt-token', template.users.admin.token)
.send({title: updatedAction.title})
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
if (err) {
return done(err);
}
const response = res.body;
assert.deepEqual(response, updatedAction);
tempAction = response;
// Store the JWT for future API calls.
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
it('A Project Owner should not be able to Patch an Action', (done) => {
request(app)
.patch(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template))
.set('x-jwt-token', template.users.admin.token)
.send([{op: 'replace', path: 'title', value: 'Patched'}])
// .expect('Content-Type', /json/)
.expect(405)
.end(done);
});
it('A Project Owner should be able to Read the Index of Actions', (done) => {
request(app)
.get(hook.alter('url', `/form/${tempForm._id}/action`, template))
.set('x-jwt-token', template.users.admin.token)
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
if (err) {
return done(err);
}
const response = res.body;
assert.equal(response.length, 2);
_.each(response, (action) => {
if (action.name === 'login') {
assert.deepEqual(action, tempAction);
}
else {
// Make sure it added a save action.
assert.equal(action.name, 'save');
}
});
// Store the JWT for future API calls.
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
it('Cant access an Action without a valid Action Id', (done) => {
request(app)
.get(hook.alter('url', `/form/${tempForm._id}/action/2342342344234`, template))
.set('x-jwt-token', template.users.admin.token)
.expect('Content-Type', /json/)
.expect(400)
.end((err, res) => {
if (err) {
return done(err);
}
// Store the JWT for future API calls.
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
});
describe('Permissions - Project Level - Authenticated User', () => {
it('A user should not be able to Create an Action for a User-Created Project Form', (done) => {
request(app)
.post(hook.alter('url', `/form/${tempForm._id}/action`, template))
.set('x-jwt-token', template.users.user1.token)
.send(tempAction)
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
it('A user should not be able to Read an Action for a User-Created Project Form', (done) => {
request(app)
.get(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template))
.set('x-jwt-token', template.users.user1.token)
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
it('A user should not be able to Update an Action for a User-Created Project Form', (done) => {
request(app)
.put(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template))
.set('x-jwt-token', template.users.user1.token)
.send({foo: 'bar'})
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
it('A user should not be able to Read the Index of Actions for a User-Created Project Form', (done) => {
request(app)
.get(hook.alter('url', `/form/${tempForm._id}/action`, template))
.set('x-jwt-token', template.users.user1.token)
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
it('A user should not be able to Delete an Action using incorrect path', (done) => {
let tempForm2 = {
title: 'Temp Form 2',
name: 'tempForm2',
path: 'temp2',
type: 'form',
access: [],
submissionAccess: [],
components: [
{
type: 'textfield',
key: 'bar',
label: 'bar',
inputMask: '',
input: true,
},
],
};
request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(tempForm2)
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
tempForm2 = res.body;
// Store the JWT for future API calls.
template.users.admin.token = res.headers['x-jwt-token'];
request(app)
.delete(hook.alter('url', `/form/${tempForm2._id}/action/${tempAction._id}`, template))
.set('x-jwt-token', template.users.admin.token)
.expect(400)
.end(done);
});
});
it('A user should not be able to Delete an Action for a User-Created Project Form', (done) => {
request(app)
.delete(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template))
.set('x-jwt-token', template.users.user1.token)
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
});
describe('Permissions - Project Level - Anonymous User', () => {
it('An Anonymous user should not be able to Create an Action for a User-Created Project Form', (done) => {
request(app)
.post(hook.alter('url', `/form/${tempForm._id}/action`, template))
.send(tempAction)
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
it('An Anonymous user should not be able to Read an Action for a User-Created Project Form', (done) => {
request(app)
.get(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template))
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
it('An Anonymous user should not be able to Update an Action for a User-Created Project Form', (done) => {
request(app)
.put(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template))
.send({foo: 'bar'})
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
it('An Anonymous user should not be able to Read the Index of Actions for a User-Created Project Form', (done) => {
request(app)
.get(hook.alter('url', `/form/${tempForm._id}/action`, template))
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
it('An Anonymous user should not be able to Delete an Action for a User-Created Project Form', (done) => {
request(app)
.delete(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template))
.expect('Content-Type', /text\/plain/)
.expect(401)
.end(done);
});
});
describe('Test action with custom transform mapping data', () => {
const addFormFields = (isForm) => ({
title: chance.word(),
name: chance.word(),
path: chance.word(),
type: isForm ? 'form' : 'resource',
...(isForm && { noSave: true }),
});
before('Create testing forms', async function () {
const clonedForResourceCreation = { ..._.cloneDeep(testMappingDataForm), ...addFormFields() };
const formResource = await request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(clonedForResourceCreation);
const response = formResource.body;
assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.equal(response.title, clonedForResourceCreation.title);
assert.equal(response.name, clonedForResourceCreation.name);
template.testResourceToSave = response;
const action = {
priority: 10,
name: 'save',
title: 'Save Submission',
settings: {
resource: response._id,
property: '',
fields: {
textField1: '',
textField2: '',
},
transform: "data.textField1 = '123';submission.data.textField1 = '111';data.textField2 = '222'",
},
condition: {
conjunction: '',
conditions: [],
custom: '',
},
submit: true,
handler: ['before'],
method: ['create', 'update'],
};
const clonedForFormCreation = { ..._.cloneDeep(testMappingDataForm), ...addFormFields(true) };
const testForm = await request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(clonedForFormCreation);
const responseTest = testForm.body;
assert(responseTest.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.equal(clonedForFormCreation.title, responseTest.title);
assert.equal(clonedForFormCreation.name, responseTest.name);
template.testFormToSave = responseTest;
const resultAction = await request(app)
.post(hook.alter('url', `/form/${responseTest._id}/action`, template))
.set('x-jwt-token', template.users.admin.token)
.send(action);
const responseAction = resultAction.body;
assert(responseAction.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.equal(action.title, responseAction.title);
assert.equal(action.name, responseAction.name);
});
it('Submit form', (done) => {
request(app)
.post(hook.alter('url', `/form/${template.testFormToSave._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
data: {
textField: 'Test',
},
})
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
const result = res.body;
assert(result.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.deepEqual(result.data, {textField1: '111', textField2: '222' });
done()
});
});
it('Get submissions from submitted form', (done) => {
request(app)
.get(hook.alter('url', `/form/${template.testFormToSave._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
const result = res.body;
assert.deepEqual(result, [], "Should be empty as our submission has been moved to resource form");
done()
});
});
it('Get submissions from connected form', (done) => {
request(app)
.get(hook.alter('url', `/form/${template.testResourceToSave._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
assert(res.body.length, 1);
const result = res.body[0];
assert(result.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.deepEqual(result.data, {textField1: '111', textField2: '222'}, "Should get a transformed submission data from connected form");
done()
});
});
after(function(done) {
delete template.testResourceToSave;
delete template.testFormToSave;
done()
})
});
describe('Test action with custom transform to another resource with new data', () => {
let addFormFields, form, resource;
before(async function () {
addFormFields = (isForm) => ({
title: chance.word(),
name: chance.word(),
path: chance.word(),
type: isForm ? 'form' : 'resource',
...(isForm && { noSave: true }),
});
const formDef = { ..._.cloneDeep(customSaveSubmissionTransformForm), ...addFormFields(true) };
const response = await request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(formDef);
assert(response.body);
assert(response.body.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.equal(response.body.title, formDef.title);
assert.equal(response.body.name, formDef.name);
form = response.body;
const resourceDef = { ..._.cloneDeep(customSaveSubmissionTransformResource), ...addFormFields() };
const resourceResponse = await request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(resourceDef);
assert(resourceResponse.body);
assert(resourceResponse.body.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.equal(resourceResponse.body.title, resourceDef.title);
assert.equal(resourceResponse.body.name, resourceDef.name);
resource = resourceResponse.body;
const action = {
priority: 10,
name: 'save',
title: 'Save Submission',
settings: {
resource: resource._id,
property: '',
fields: {},
transform: `
const highEarner = submission.data.salesBySalesperson.reduce((acc, curr) => curr.sales > acc.sales ? curr : acc);
data = {
month: new Date().toLocaleString('default', { month: 'long' }),
name: highEarner.name,
sales: highEarner.sales
};`,
},
condition: {
conjunction: '',
conditions: [],
custom: '',
},
submit: true,
handler: ['before'],
method: ['create', 'update'],
};
const resultAction = await request(app)
.post(hook.alter('url', `/form/${form._id}/action`, template))
.set('x-jwt-token', template.users.admin.token)
.send(action);
const responseAction = resultAction.body;
assert(responseAction.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.equal(action.title, responseAction.title);
assert.equal(action.name, responseAction.name);
});
it('Submit form', (done) => {
request(app)
.post(hook.alter('url', `/form/${form._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
data: {
salesBySalesperson: [
{
name: "John Doe",
sales: 10000
},
{
name: "Jane Smith",
sales: 10000.03
},
{
name: "Hank Williams",
sales: 10003.5
}
],
submit: true
}
})
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
const result = res.body;
assert(result.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.deepEqual(result.data, {
month: new Date().toLocaleString('default', { month: 'long' }),
name: "Hank Williams",
sales: 10003.5
});
done()
});
});
it('Get submissions from submitted form', (done) => {
request(app)
.get(hook.alter('url', `/form/${form._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
const result = res.body;
assert.deepEqual(result, [], "Should be empty as our submission has been moved to resource form");
done()
});
});
it('Get submissions from connected form', (done) => {
request(app)
.get(hook.alter('url', `/form/${resource._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
assert(res.body.length, 1);
const result = res.body[0];
assert(result.hasOwnProperty('_id'), 'The response should contain an `_id`.');
assert.deepEqual(result.data, {
month: new Date().toLocaleString('default', { month: 'long' }),
name: "Hank Williams",
sales: 10003.5
});
done()
});
});
});
describe('Action MachineNames', () => {
let _action;
const name = chance.word();
let helper;
before(() => {
helper = new Helper(template.users.admin, template);
});
it('Actions expose their machineNames through the api', (done) => {
helper
.form({name: name})
.action({
title: 'Webhook',
name: 'webhook',
handler: ['after'],
method: ['create', 'update', 'delete'],
priority: 1,
settings: {
url: 'example.com',
username: '',
password: '',
},
})
.execute((err, result) => {
if (err) {
return done(err);
}
const action = result.getAction('Webhook');
assert(action.hasOwnProperty('machineName'));
_action = action;
done();
});
});
it('A user can modify their action machineNames', (done) => {
const newMachineName = chance.word();
helper
.action(name, {
_id: _action._id,
machineName: newMachineName,
})
.execute((err, result) => {
if (err) {
return done(err);
}
const action = result.getAction('Webhook');
assert(action.hasOwnProperty('machineName'));
assert.equal(action.machineName, newMachineName);
done();
});
});
});
describe('Webhook Functionality tests', () => {
if (docker) {
return;
}
// The temp form with the add RoleAction for existing submissions.
let webhookForm = {
title: 'Webhook Form',
name: 'webhookform',
path: 'webhookform',
type: 'form',
access: [],
submissionAccess: [],
components: [
{
type: 'textfield',
validate: {
custom: '',
pattern: '',
maxLength: '',
minLength: '',
required: false,
},
defaultValue: '',
multiple: false,
suffix: '',
prefix: '',
placeholder: 'foo',
key: 'firstName',
label: 'First Name',
inputMask: '',
inputType: 'text',
input: true,
},
{
type: 'textfield',
validate: {
custom: '',
pattern: '',
maxLength: '',
minLength: '',
required: false,
},
defaultValue: '',
multiple: false,
suffix: '',
prefix: '',
placeholder: 'foo',
key: 'lastName',
label: 'Last Name',
inputMask: '',
inputType: 'text',
input: true,
},
{
type: 'email',
persistent: true,
unique: false,
protected: false,
defaultValue: '',
suffix: '',
prefix: '',
placeholder: 'Enter your email address',
key: 'email',
label: 'Email',
inputType: 'email',
tableView: true,
input: true,
},
{
type: 'password',
persistent: true,
unique: false,
protected: false,
defaultValue: '',
suffix: '',
prefix: '',
placeholder: 'Enter your password',
key: 'password',
label: 'Password',
inputType: 'password',
tableView: true,
input: true,
},
],
};
let webhookForm1 = {
title: 'Webhook Form 1',
name: 'webhookform1',
path: 'webhookform1',
type: 'form',
access: [],
submissionAccess: [],
components: [
{
type: 'textfield',
validate: {
custom: '',
pattern: '',
maxLength: '',
minLength: '',
required: false,
},
defaultValue: '',
multiple: false,
suffix: '',
prefix: '',
placeholder: 'foo',
key: 'firstName',
label: 'First Name',
inputMask: '',
inputType: 'text',
input: true,
},
{
type: 'textfield',
validate: {
custom: '',
pattern: '',
maxLength: '',
minLength: '',
required: false,
},
defaultValue: '',
multiple: false,
suffix: '',
prefix: '',
placeholder: 'foo',
key: 'lastName',
label: 'Last Name',
inputMask: '',
inputType: 'text',
input: true,
},
{
type: 'email',
persistent: true,
unique: false,
protected: false,
defaultValue: '',
suffix: '',
prefix: '',
placeholder: 'Enter your email address',
key: 'email',
label: 'Email',
inputType: 'email',
tableView: true,
input: true,
},
{
type: 'password',
persistent: true,
unique: false,
protected: false,
defaultValue: '',
suffix: '',
prefix: '',
placeholder: 'Enter your password',
key: 'password',
label: 'Password',
inputType: 'password',
tableView: true,
input: true,
},
],
};
let webhookForm2 = {
title: 'Webhook Form 2',
name: 'webhookform2',
path: 'webhookform2',
type: 'form',
access: [],
submissionAccess: [],
components: [
{
type: 'textfield',
defaultValue: '',
multiple: false,
suffix: '',
prefix: '',
key: 'textfield',
label: 'Text Field',
inputMask: '',
inputType: 'text',
input: true,
}
],
};
let port = 4002;
let webhookSubmission = null;
let webhookHandler = () => {};
let webhookServer = null;
// Create a new server.
const newServer = (ready) => {
const server = http.createServer((request, response) => {
let body = [];
if (request.url === '/plain-text') {
response.setHeader('Content-Type', 'text/plain; charset=utf-8;');
response.write('200 OK');
response.statusCode = 200;
response.end();
}
else {
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
webhookHandler(body ? JSON.parse(body) : body, url.parse(request.url, true));
});
}
});
server.port = port++;
server.url = `http://localhost:${server.port}`;
server.listen(server.port, () => {
hook.alter('webhookServer', server, app, template, (err, server) => {
ready(err, server);
});
});
};
it('Should create the form and action for the webhook tests', (done) => {
newServer((err, server) => {
if (err) {
return done(err);
}
webhookServer = server;
request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(webhookForm)
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
webhookForm = res.body;
template.users.admin.token = res.headers['x-jwt-token'];
request(app)
.post(hook.alter('url', `/form/${webhookForm._id}/action`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
title: 'Webhook',
name: 'webhook',
form: webhookForm._id.toString(),
handler: ['after'],
method: ['create', 'update', 'delete'],
priority: 1,
settings: {
url: server.url,
username: '',
password: '',
},
})
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
});
});
it('Should send a webhook with create data.', (done) => {
webhookHandler = (body) => {
body = hook.alter('webhookBody', body);
assert.equal(body.params.formId, webhookForm._id.toString());
assert.equal(body.request.owner, template.users.admin._id.toString());
assert.equal(body.request.data.email, 'test@example.com');
assert.equal(body.request.data.firstName, 'Test');
assert.equal(body.request.data.lastName, 'Person');
assert(body.request.data.password !== '123testing', 'Passwords must not be visible via webhooks.');
assert.deepEqual(_.pick(body.submission, _.keys(webhookSubmission)), webhookSubmission);
done();
};
request(app)
.post(hook.alter('url', `/form/${webhookForm._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
data: {
firstName: 'Test',
lastName: 'Person',
email: 'test@example.com',
password: '123testing',
},
})
.expect(201)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
webhookSubmission = res.body;
});
});
it('Should be able to get the data from the webhook action.', (done) => {
request(app)
.get(hook.alter('url', `/form/${webhookForm._id}/submission/${webhookSubmission._id}`, template))
.set('x-jwt-token', template.users.admin.token)
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
webhookSubmission = res.body;
assert.equal(res.body.data.email, 'test@example.com');
assert.equal(res.body.data.firstName, 'Test');
assert.equal(res.body.data.lastName, 'Person');
done();
});
});
it('Should hide fields in action settings form if access to them is restricted', (done) => {
request(app)
.get(hook.alter('url', `/form/${webhookForm._id}/actions/save`, template))
.set('x-jwt-token', template.users.admin.token)
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
if (err) {
return done(err);
}
const components = res.body?.settingsForm?.components ?? [];
const actionExecutionComponent = components.find(x=> x.legend ==="Action Execution");
assert.equal(actionExecutionComponent.hidden, true);
done();
});
})
it('Should send a webhook with update data.', (done) => {
webhookHandler = (body) => {
body = hook.alter('webhookBody', body);
assert.equal(body.params.formId, webhookForm._id.toString());
assert.equal(body.request.data.email, 'test@example.com');
assert.equal(body.request.data.firstName, 'Test2');
assert.equal(body.request.data.lastName, 'Person3');
assert.deepEqual(_.pick(body.submission, _.keys(webhookSubmission)), webhookSubmission);
done();
};
request(app)
.put(hook.alter('url', `/form/${webhookForm._id}/submission/${webhookSubmission._id}`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
data: {
firstName: 'Test2',
lastName: 'Person3',
email: 'test@example.com',
},
})
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
webhookSubmission = res.body;
});
});
it('Should send a webhook with deleted data.', (done) => {
webhookHandler = (body, url) => {
body = hook.alter('webhookBody', body);
assert.equal(url.query.formId, webhookForm._id);
assert.equal(url.query.submissionId, webhookSubmission._id);
done();
};
request(app)
.delete(hook.alter('url', `/form/${webhookForm._id}/submission/${webhookSubmission._id}`, template))
.set('x-jwt-token', template.users.admin.token)
.expect(200)
.expect('Content-Type', /json/)
.end((err) => {
if (err) {
return done(err);
}
});
});
it('Should create the form and action for the webhook tests with conditionals', (done) => {
newServer((err, server) => {
if (err) {
return done(err);
}
webhookServer = server;
request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(webhookForm1)
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
webhookForm1 = res.body;
template.users.admin.token = res.headers['x-jwt-token'];
request(app)
.post(hook.alter('url', `/form/${webhookForm1._id}/action`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
title: 'Webhook',
name: 'webhook',
form: webhookForm1._id.toString(),
handler: ['after'],
method: ['create', 'update', 'delete'],
priority: 1,
settings: {
url: server.url,
username: '',
password: '',
},
condition: {
field: 'lastName',
eq: 'equals',
value: '123'
},
})
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
});
});
it('Should send a webhook with create data with conditionals', (done) => {
request(app)
.post(hook.alter('url', `/form/${webhookForm1._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
data: {
firstName: 'testCondition',
lastName: 'Person',
email: 'test@example.com',
password: '123testing',
},
})
.expect(201)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
done();
webhookSubmission = res.body;
});
});
it('Should send a webhook with deleted data with conditionals', (done) => {
webhookHandler = (body, url) => {
body = hook.alter('webhookBody', body);
assert.equal(url.query.formId, webhookForm1._id);
assert.equal(url.query.submissionId, webhookSubmission._id);
done();
};
request(app)
.delete(hook.alter('url', `/form/${webhookForm1._id}/submission/${webhookSubmission._id}`, template))
.set('x-jwt-token', template.users.admin.token)
.expect(200)
.expect('Content-Type', /json/)
.end((err) => {
if (err) {
return done(err);
}
done();
});
});
it('Should create the form and action for the webhook tests with conditionals for submission creation parameter', (done) => {
newServer((err, server) => {
if (err) {
return done(err);
}
webhookServer = server;
request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(webhookForm2)
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
webhookForm2 = res.body;
template.users.admin.token = res.headers['x-jwt-token'];
request(app)
.post(hook.alter('url', `/form/${webhookForm2._id}/action`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
title: 'Webhook',
name: 'webhook',
form: webhookForm2._id.toString(),
handler: ['after'],
method: ['create', 'update', 'delete'],
priority: 1,
settings: {
url: server.url,
username: '',
password: '',
},
condition: {
component: '(submission).created',
operator: 'dateGreaterThan',
value: '2023-07-01T12:00:00.000Z',
},
})
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
});
});
it('Should send a webhook for submission with creation date dateGreaterThan set date', (done) => {
webhookHandler = (body) => {
body = hook.alter('webhookBody', body);
assert.equal(body.params.formId, webhookForm2._id.toString());
assert.equal(body.request.owner, template.users.admin._id.toString());
done();
};
request(app)
.post(hook.alter('url', `/form/${webhookForm2._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
data: {
textfield: ''
},
})
.expect(201)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
return done(err);
}
webhookSubmission = res.body;
});
});
if (app.hasProject) {
describe('Webhook with non JSON response', () => {
if (docker) {
return;
}
// The temp form with the add RoleAction for existing submissions.
let webhookForm2 = {
title: 'Webhook Form 2',
name: 'webhookform2',
path: 'webhookform2',
type: 'form',
access: [],
submissionAccess: [],
components: [
{
type: 'textfield',
validate: {
custom: '',
pattern: '',
maxLength: '',
minLength: '',
required: false,
},
defaultValue: '',
multiple: false,
suffix: '',
prefix: '',
placeholder: 'foo',
key: 'firstName',
label: 'First Name',
inputMask: '',
inputType: 'text',
input: true,
},
{
type: 'textfield',
validate: {
custom: '',
pattern: '',
maxLength: '',
minLength: '',
required: false,
},
defaultValue: '',
multiple: false,
suffix: '',
prefix: '',
placeholder: 'foo',
key: 'lastName',
label: 'Last Name',
inputMask: '',
inputType: 'text',
input: true,
},
{
type: 'email',
persistent: true,
unique: false,
protected: false,
defaultValue: '',
suffix: '',
prefix: '',
placeholder: 'Enter your email address',
key: 'email',
label: 'Email',
inputType: 'email',
tableView: true,
input: true,
},
{
type: 'password',
persistent: true,
unique: false,
protected: false,
defaultValue: '',
suffix: '',
prefix: '',
placeholder: 'Enter your password',
key: 'password',
label: 'Password',
inputType: 'password',
tableView: true,
input: true,
},
],
};
it('Should create the form and action for the webhook tests 2', (done) => {
request(app)
.post(hook.alter('url', '/form', template))
.set('x-jwt-token', template.users.admin.token)
.send(webhookForm2)
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
webhookForm2 = res.body;
template.users.admin.token = res.headers['x-jwt-token'];
request(app)
.post(hook.alter('url', `/form/${webhookForm2._id}/action`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
title: 'Webhook',
name: 'webhook',
form: webhookForm2._id.toString(),
handler: ['after'],
method: ['create', 'update', 'delete'],
priority: 1,
settings: {
url: `${webhookServer.url}/plain-text`,
username: '',
password: '',
block: true
},
})
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) {
return done(err);
}
template.users.admin.token = res.headers['x-jwt-token'];
done();
});
});
});
it('Should send a webhook with create data and receive a 400 error.', (done) => {
request(app)
.post(hook.alter('url', `/form/${webhookForm2._id}/submission`, template))
.set('x-jwt-token', template.users.admin.token)
.send({
data: {
firstName: 'Test',
lastName: 'Person',
email: 'test@example.com',
password: '123testing',
},
})
.expect(400)
.end((err, res) => {
if (err) {
return done(err);
}
assert.equal(res.text.indexOf('invalid json response body'), 0);
done();
});
});
});
}
});
describe('EmailAction Functionality tests', () => {
if (docker) {
return;
}
const adminUser = (token) => {
const user = template.users.formioAdmin ? 'formioAdmin' : 'admin';
if (token) {
template.users[user].token = token;
}
else {
return template.users[user];
}
};
// The temp form with the add RoleAction for existing submissions.
const emailForm = {
title: 'Email Form',
name: 'emailform',
path: 'emailform',
type: 'form',
access: [],
submissionAccess: [],
components: [
{
type: 'textfield',
key: 'firstName',
label: 'First Name',
input: true,
},
{
type: 'textfield',
key: 'lastName',
label: 'Last Name',
input: true,
},
{
type: 'email',
key: 'email',
label: 'Email',
input: true,
},
],
};
// The temp role add action for existing submissions.
const emailAction = {
title: 'Email',
name: 'email',
handler: ['after'],
method: ['create'],
priority: 1,
settings: {},
};
let numTests = 0;
const newEmailTest = (settings, done) => {
numTests++;
settings.transport = 'test';
let te