UNPKG

formio

Version:

The formio server application.

1,527 lines (1,390 loc) • 195 kB
/* 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