import fs from 'node:fs';
import factory from './helpers/factory.js';
import testHelpers from './helpers/testHelpers.js';
import types from './helpers/types.js';
import { Serializer, TypeResolver, deserialize, map } from 'moddle-context-serializer';
const lanesSource = factory.resource('lanes.bpmn');
const subProcessSource = factory.resource('sub-process.bpmn');
const twoProcessesSource = factory.resource('two-executable-processes.bpmn');
const conditionAndEscalationSource = factory.resource('condition-and-escalation.bpmn');
const eventDefinitionSource = factory.resource('bound-error-and-timer.bpmn');
const typeResolver = TypeResolver(types);
describe('moddle context serializer', () => {
let lanesModdleContext,
lanesModdleContextFromCallBack,
subProcessModdleContext,
eventDefinitionModdleContext,
twoProcessesModdleContext,
conditionAndEscalationModdleContext,
signalEventModdleContext,
messageFlowModdleContext,
compensationContext,
timersModdleContext,
conditionalFlows;
before(async () => {
lanesModdleContext = await testHelpers.moddleContext(lanesSource);
lanesModdleContextFromCallBack = JSON.parse(fs.readFileSync('./test/resources/lanes-old-callback-context.json', 'utf8'));
subProcessModdleContext = await testHelpers.moddleContext(subProcessSource);
eventDefinitionModdleContext = await testHelpers.moddleContext(eventDefinitionSource);
conditionAndEscalationModdleContext = await testHelpers.moddleContext(conditionAndEscalationSource);
twoProcessesModdleContext = await testHelpers.moddleContext(twoProcessesSource);
signalEventModdleContext = await testHelpers.moddleContext(factory.resource('signal-event.bpmn'));
messageFlowModdleContext = await testHelpers.moddleContext(factory.resource('message-flows.bpmn'));
compensationContext = await testHelpers.moddleContext(factory.resource('bound-compensation.bpmn'));
timersModdleContext = await testHelpers.moddleContext(factory.resource('timers.bpmn'));
conditionalFlows = await testHelpers.moddleContext(factory.resource('conditional-flows.bpmn'));
});
describe('TypeResolver(types[, extender])', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let serializer;
before(() => {
serializer = Serializer(lanesModdleContext, typeResolver);
});
it('holds definition id, type, and name', () => {
expect(serializer).to.have.property('id', 'Definitions_1');
expect(serializer).to.have.property('type', 'bpmn:Definitions');
expect(serializer).to.have.property('name', 'Lanes');
});
it('returns the expected api', () => {
expect(serializer).to.have.property('getActivities').that.is.a('function');
expect(serializer).to.have.property('getActivityById').that.is.a('function');
expect(serializer).to.have.property('getAssociations').that.is.a('function');
expect(serializer).to.have.property('getAssociationById').that.is.a('function');
expect(serializer).to.have.property('getDataObjects').that.is.a('function');
expect(serializer).to.have.property('getInboundSequenceFlows').that.is.a('function');
expect(serializer).to.have.property('getMessageFlows').that.is.a('function');
expect(serializer).to.have.property('getOutboundSequenceFlows').that.is.a('function');
expect(serializer).to.have.property('getProcessById').that.is.a('function');
expect(serializer).to.have.property('getProcesses').that.is.a('function');
expect(serializer).to.have.property('getExecutableProcesses').that.is.a('function');
expect(serializer).to.have.property('getSequenceFlowById').that.is.a('function');
expect(serializer).to.have.property('getSequenceFlows').that.is.a('function');
expect(serializer).to.have.property('serialize').that.is.a('function');
});
it('executes passed extender function with default type mapping', async () => {
function Escalation() {}
function IntermediateThrowEvent() {}
function EscalationEventDefinition() {}
const myTypeResolver = TypeResolver(types, (activityTypes) => {
activityTypes['bpmn:Escalation'] = Escalation;
activityTypes['bpmn:IntermediateThrowEvent'] = IntermediateThrowEvent;
activityTypes['bpmn:EscalationEventDefinition'] = EscalationEventDefinition;
});
const moddleContext = await testHelpers.moddleContext(factory.resource('escalation.bpmn'));
const extendedSerializer = Serializer(moddleContext, myTypeResolver);
const event = extendedSerializer.getActivityById('intermediateThrowEvent_1');
expect(event).to.be.ok;
expect(event).to.have.property('Behaviour', IntermediateThrowEvent);
expect(event).to.have.property('behaviour').with.property('eventDefinitions').with.length(1);
expect(event.behaviour.eventDefinitions[0]).to.have.property('Behaviour', EscalationEventDefinition);
const escalation = extendedSerializer.getActivityById('escalation_1');
expect(escalation).to.be.ok;
expect(escalation).to.have.property('Behaviour', Escalation);
expect(escalation.behaviour).to.have.property('escalationCode', '10');
});
});
describe('serialize()', () => {
it('returns stringified copy of mapped context', () => {
const serializer = Serializer(subProcessModdleContext, typeResolver);
const serialized = JSON.parse(serializer.serialize());
expect(serialized).to.have.property('id').that.is.ok;
expect(serialized).to.have.property('type').that.is.ok;
expect(serialized).to.have.property('activities').that.is.an('array');
expect(serialized).to.have.property('dataObjects').that.is.an('array');
expect(serialized).to.have.property('definition').that.is.an('object').and.ok;
expect(serialized).to.have.property('messageFlows').that.is.an('array');
expect(serialized).to.have.property('processes').that.is.an('array');
expect(serialized).to.have.property('sequenceFlows').that.is.an('array');
});
it('(backward compability) returns stringified copy of mapped old callback context', () => {
const serializer = Serializer(lanesModdleContextFromCallBack, typeResolver);
const serialized = JSON.parse(serializer.serialize());
expect(serialized).to.have.property('id').that.is.ok;
expect(serialized).to.have.property('type').that.is.ok;
expect(serialized).to.have.property('activities').that.is.an('array');
expect(serialized).to.have.property('dataObjects').that.is.an('array');
expect(serialized).to.have.property('definition').that.is.an('object').and.ok;
expect(serialized).to.have.property('messageFlows').that.is.an('array');
expect(serialized).to.have.property('processes').that.is.an('array');
expect(serialized).to.have.property('sequenceFlows').that.is.an('array');
});
});
describe('deserialize(deserializedContext, typeResolver)', () => {
it('holds definition id, type, and name', () => {
const serializer = Serializer(lanesModdleContext, typeResolver);
const deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
expect(deserialized).to.have.property('id', 'Definitions_1');
expect(deserialized).to.have.property('type', 'bpmn:Definitions');
expect(deserialized).to.have.property('name', 'Lanes');
});
it('(backward compability) holds definition id, type, and name', () => {
const serializer = Serializer(lanesModdleContextFromCallBack, typeResolver);
const deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
expect(deserialized).to.have.property('id', 'Definitions_1');
expect(deserialized).to.have.property('type', 'bpmn:Definitions');
expect(deserialized).to.have.property('name', 'Lanes');
});
it('has the expected api', () => {
const serializer = Serializer(subProcessModdleContext, typeResolver);
const deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
expect(deserialized).to.have.property('getActivities').that.is.a('function');
expect(deserialized).to.have.property('getActivities').that.is.a('function');
expect(deserialized).to.have.property('getActivityById').that.is.a('function');
expect(deserialized).to.have.property('getDataObjects').that.is.a('function');
expect(deserialized).to.have.property('getInboundSequenceFlows').that.is.a('function');
expect(deserialized).to.have.property('getMessageFlows').that.is.a('function');
expect(deserialized).to.have.property('getOutboundSequenceFlows').that.is.a('function');
expect(deserialized).to.have.property('getProcessById').that.is.a('function');
expect(deserialized).to.have.property('getProcesses').that.is.a('function');
expect(deserialized).to.have.property('getExecutableProcesses').that.is.a('function');
expect(deserialized).to.have.property('getSequenceFlowById').that.is.a('function');
expect(deserialized).to.have.property('getSequenceFlows').that.is.a('function');
expect(deserialized).to.have.property('serialize').that.is.a('function');
});
it('has entities with Behaviour', () => {
let serializer = Serializer(eventDefinitionModdleContext, typeResolver);
let deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
deserialized.getProcesses().forEach(assertEntity);
deserialized.getActivities().forEach(assertEntity);
deserialized.getDataObjects().forEach(assertEntity);
deserialized.getMessageFlows().forEach(assertEntity);
serializer = Serializer(lanesModdleContext, typeResolver);
deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
deserialized.getProcesses().forEach(assertEntity);
deserialized.getActivities().forEach(assertEntity);
deserialized.getDataObjects().forEach(assertEntity);
deserialized.getMessageFlows().forEach(assertEntity);
serializer = Serializer(subProcessModdleContext, typeResolver);
deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
deserialized.getProcesses().forEach(assertEntity);
deserialized.getActivities().forEach(assertEntity);
deserialized.getDataObjects().forEach(assertEntity);
deserialized.getMessageFlows().forEach(assertEntity);
});
});
describe('map(moddleContext[, extendFn])', () => {
it('returns mapped context', () => {
const mapped = map(subProcessModdleContext);
expect(mapped).to.have.property('definition').that.is.an('object');
expect(mapped).to.have.property('activities').that.is.an('array');
expect(mapped).to.have.property('associations').that.is.an('array');
expect(mapped).to.have.property('dataObjects').that.is.an('array');
expect(mapped).to.have.property('dataStores').that.is.an('array');
expect(mapped).to.have.property('messageFlows').that.is.an('array');
expect(mapped).to.have.property('processes').that.is.an('array');
expect(mapped).to.have.property('sequenceFlows').that.is.an('array');
expect(mapped).to.have.property('scripts').that.is.an('array');
expect(mapped).to.have.property('timers').that.is.an('array');
});
});
describe('getProcesses()', () => {
it('returns processes', () => {
const serializer = Serializer(lanesModdleContext, typeResolver);
const processes = serializer.getProcesses();
expect(processes).to.have.length(2);
processes.forEach(assertEntity);
});
it('returns processes only', () => {
const serializer = Serializer(subProcessModdleContext, typeResolver);
const processes = serializer.getProcesses();
expect(processes).to.have.length(1);
});
it('isExecutable is available in behaviour', () => {
const serializer = Serializer(lanesModdleContext, typeResolver);
const processes = serializer.getProcesses();
expect(processes).to.have.length(2);
expect(processes[0]).to.have.property('behaviour').with.property('isExecutable', true);
expect(processes[1]).to.have.property('behaviour').with.property('isExecutable', false);
});
it('(backward compability) isExecutable is available in behaviour', () => {
const serializer = Serializer(lanesModdleContextFromCallBack, typeResolver);
const processes = serializer.getProcesses();
expect(processes).to.have.length(2);
expect(processes[0]).to.have.property('behaviour').with.property('isExecutable', true);
expect(processes[1]).to.have.property('behaviour').with.property('isExecutable', false);
});
it('returns only processes', () => {
const serializer = Serializer(subProcessModdleContext, typeResolver);
const processes = serializer.getProcesses();
expect(processes).to.have.length(1);
expect(processes[0]).to.have.property('type', 'bpmn:Process');
});
});
describe('getExecutableProcesses()', () => {
it('returns executable processes', () => {
const serializer = Serializer(lanesModdleContext, typeResolver);
const executableProcesses = serializer.getExecutableProcesses();
const processes = serializer.getProcesses();
expect(executableProcesses).to.have.length(1);
expect(processes).to.have.length(2);
expect(executableProcesses[0]).to.have.property('id', 'mainProcess');
expect(executableProcesses[0]).to.have.property('behaviour').with.property('isExecutable', true);
});
it('returns all executable processes', () => {
const serializer = Serializer(twoProcessesModdleContext, typeResolver);
const processes = serializer.getExecutableProcesses();
expect(processes).to.have.length(2);
expect(processes[0]).to.have.property('behaviour').with.property('isExecutable', true);
expect(processes[1]).to.have.property('behaviour').with.property('isExecutable', true);
});
});
describe('getProcessesById(id)', () => {
it('returns process with id', () => {
const serializer = Serializer(lanesModdleContext, typeResolver);
expect(serializer.getProcessById('mainProcess')).to.be.ok;
expect(serializer.getProcessById('participantProcess')).to.be.ok;
});
it('returns nothing if not found', () => {
const serializer = Serializer(subProcessModdleContext, typeResolver);
expect(serializer.getProcessById('subProcess')).to.not.be.ok;
});
});
describe('getActivities([scopeId])', () => {
it('returns all activities', () => {
const serializer = Serializer(subProcessModdleContext, typeResolver);
const activities = serializer.getActivities();
expect(activities).to.have.length(5);
activities.forEach(assertEntity);
});
it('with scope returns activities for given process or sub process', () => {
const serializer = Serializer(subProcessModdleContext, typeResolver);
const processActivities = serializer.getActivities('mainProcess');
expect(processActivities).to.have.length(3);
processActivities.forEach((activity) => {
assertEntity(activity);
expect(activity.parent).to.have.property('id', 'mainProcess');
});
const subProcessActivities = serializer.getActivities('subProcess');
expect(subProcessActivities).to.have.length(2);
subProcessActivities.forEach((activity) => {
assertEntity(activity);
expect(activity.parent).to.have.property('id', 'subProcess');
});
});
});
describe('getSequenceFlows([scopeId])', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let serializer;
before(() => {
serializer = Serializer(lanesModdleContext, typeResolver);
});
it('returns all sequence flows', () => {
expect(serializer.getSequenceFlows()).to.have.length(6);
});
it('flows have the expected properties', () => {
const flows = serializer.getSequenceFlows();
flows.forEach(assertSequenceFlow);
});
it('with scope id returns sequence flows belonging to scope', () => {
expect(serializer.getSequenceFlows('mainProcess')).to.have.length(3);
expect(serializer.getSequenceFlows('participantProcess')).to.have.length(3);
});
});
describe('getSequenceFlowById(id)', () => {
it('returns sequence flow with id', () => {
const serializer = Serializer(lanesModdleContext, typeResolver);
expect(serializer.getSequenceFlowById('flow1')).to.be.ok;
expect(serializer.getSequenceFlowById('flow-p-1')).to.be.ok;
});
it('returns nothing if not found', () => {
const serializer = Serializer(subProcessModdleContext, typeResolver);
expect(serializer.getSequenceFlowById('subProcess')).to.not.be.ok;
});
});
describe('getOutboundSequenceFlows(activityId)', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let serializer;
before(async () => {
serializer = Serializer(await testHelpers.moddleContext(factory.valid()), typeResolver);
});
it('returns activity outbound sequence flows', () => {
const flows = serializer.getOutboundSequenceFlows('theStart');
expect(flows).to.have.length(1);
flows.forEach(assertSequenceFlow);
});
it('returns all outbound', () => {
const flows = serializer.getOutboundSequenceFlows('decision');
expect(flows).to.have.length(2);
});
it('empty array if non found', () => {
const flows = serializer.getOutboundSequenceFlows('end1');
expect(flows).to.have.length(0);
});
it('marks default sequence flows', () => {
const flows = serializer.getOutboundSequenceFlows('decision');
expect(flows).to.have.length(2);
expect(flows[0]).to.have.property('isDefault', true);
});
it('conditional flow returns condition in behaviour', () => {
const flows = serializer.getOutboundSequenceFlows('decision');
expect(flows).to.have.length(2);
expect(flows[1]).to.have.property('behaviour').with.property('conditionExpression');
});
it('contains only sequence flows', () => {
const flows = Serializer(lanesModdleContext, typeResolver).getOutboundSequenceFlows('task1');
expect(flows).to.have.length(1);
expect(flows[0]).to.have.property('type', 'bpmn:SequenceFlow');
});
it('default flow is returned on task', () => {
const flows = Serializer(conditionalFlows, typeResolver).getOutboundSequenceFlows('task1');
expect(flows).to.have.length(3);
expect(flows.some((f) => f.isDefault)).to.be.true;
});
});
describe('getInboundSequenceFlows()', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(async () => {
contextMapper = Serializer(await testHelpers.moddleContext(factory.valid()), typeResolver);
});
it('returns activity inbound sequence flows', () => {
const flows = contextMapper.getInboundSequenceFlows('end2');
expect(flows).to.have.length(1);
flows.forEach(assertSequenceFlow);
});
it('empty array if non found', () => {
const flows = contextMapper.getInboundSequenceFlows('theStart');
expect(flows).to.have.length(0);
});
it('returns inbound for sub process', () => {
const flows = Serializer(subProcessModdleContext, typeResolver).getInboundSequenceFlows('subProcess');
expect(flows).to.have.length(1);
});
it('main process doesn´t return inbound for sub process', () => {
const flows = Serializer(subProcessModdleContext, typeResolver).getInboundSequenceFlows('mainProcess');
expect(flows).to.have.length(0);
});
it('returns sequence flows only', () => {
const flows = Serializer(lanesModdleContext, typeResolver).getInboundSequenceFlows('intermediate');
expect(flows).to.have.length(1);
expect(flows[0]).to.have.property('type', 'bpmn:SequenceFlow');
});
});
describe('getMessageFlows()', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let laneSerializer;
before(() => {
laneSerializer = Serializer(lanesModdleContext, typeResolver);
});
it('with scope returns all outbound message flows from scope (process)', () => {
const flows = laneSerializer.getMessageFlows('mainProcess');
expect(flows).to.have.length(1);
expect(flows[0]).to.have.property('type', 'bpmn:MessageFlow');
expect(flows[0]).to.have.property('Behaviour', types.MessageFlow);
});
it('without scope returns all message flows', () => {
const flows = laneSerializer.getMessageFlows();
expect(flows).to.have.length(2);
expect(flows[0]).to.have.property('type', 'bpmn:MessageFlow');
expect(flows[0]).to.have.property('id', 'fromMainTaskMessageFlow');
expect(flows[0]).to.have.property('source').that.eql({
processId: 'mainProcess',
id: 'task1',
});
expect(flows[0]).to.have.property('target').that.eql({
processId: 'participantProcess',
id: 'messageStartEvent',
});
expect(flows[0]).to.have.property('Behaviour', types.MessageFlow);
expect(flows[1]).to.have.property('type', 'bpmn:MessageFlow');
expect(flows[1]).to.have.property('id', 'fromCompleteTaskMessageFlow');
expect(flows[1]).to.have.property('source').that.eql({
processId: 'participantProcess',
id: 'completeTask',
});
expect(flows[1]).to.have.property('target').that.eql({
processId: 'mainProcess',
id: 'intermediate',
});
expect(flows[1]).to.have.property('Behaviour', types.MessageFlow);
});
it('message flow targeting empty participant is ok', async () => {
const source = `
`;
const moddleContext = await testHelpers.moddleContext(source);
const serializer = Serializer(moddleContext, typeResolver);
const messageFlows = serializer.getMessageFlows();
expect(messageFlows).to.have.length(2);
const [fromMain, fromParticipant] = messageFlows;
expect(fromMain).to.have.property('source').that.eql({
processId: 'mainProcess',
id: 'send',
});
expect(fromMain).to.have.property('target').that.eql({
participantId: 'part2',
participantName: 'Participant',
processId: 'participantProcess',
});
expect(fromParticipant).to.have.property('source').that.eql({
participantId: 'part2',
participantName: 'Participant',
processId: 'participantProcess',
});
expect(fromParticipant).to.have.property('target').that.eql({
processId: 'mainProcess',
id: 'receive',
});
});
it('message flow targeting non-existing element doesn´t throw', async () => {
const source = `
`;
const moddleContext = await testHelpers.moddleContext(source);
const serializer = Serializer(moddleContext, typeResolver);
const messageFlows = serializer.getMessageFlows();
expect(messageFlows).to.have.length(1);
const [fromMain] = messageFlows;
expect(fromMain).to.have.property('source').that.eql({
processId: 'mainProcess',
id: 'send',
});
expect(fromMain).to.have.property('target', undefined);
});
});
describe('getAssociations(scopeId)', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let serializer;
before(() => {
serializer = Serializer(compensationContext, typeResolver);
});
it('with scope returns associations for scope (process)', () => {
const flows = serializer.getAssociations('Process_0');
expect(flows).to.have.length(2);
expect(flows[0]).to.have.property('type', 'bpmn:Association');
expect(flows[0]).to.have.property('Behaviour', types.Association);
expect(flows[1]).to.have.property('type', 'bpmn:Association');
expect(flows[1]).to.have.property('Behaviour', types.Association);
});
it('without scope returns all associations from scope (process)', () => {
const flows = serializer.getAssociations();
expect(flows).to.have.length(2);
expect(flows[0]).to.have.property('type', 'bpmn:Association');
expect(flows[0]).to.have.property('Behaviour', types.Association);
expect(flows[1]).to.have.property('type', 'bpmn:Association');
expect(flows[1]).to.have.property('Behaviour', types.Association);
});
it('with unknown scope returns no associations', () => {
const flows = serializer.getAssociations('Def');
expect(flows).to.have.length(0);
});
});
describe('getExtendContext()', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let serializer;
before(() => {
serializer = Serializer(compensationContext, typeResolver);
});
it('returns scripts and timers', () => {
const context = serializer.getExtendContext();
expect(context).to.have.property('scripts').that.is.an('array');
expect(context).to.have.property('timers').that.is.an('array');
});
it('and add functions', () => {
const context = serializer.getExtendContext();
expect(context).to.have.property('addScript').that.is.a('function');
expect(context).to.have.property('addTimer').that.is.a('function');
});
it('add function with empty body is ok, but to what end?', () => {
const context = serializer.getExtendContext();
context.addScript('foo', {});
context.addTimer('bar', {});
expect(context.scripts).to.have.length(1);
expect(context.timers).to.have.length(1);
});
});
describe('Service implementation', () => {
const source = `
`;
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(async () => {
const moddleContext = await testHelpers.moddleContext(source);
contextMapper = Serializer(moddleContext, typeResolver);
});
it('ServiceTask receives service implementation', () => {
const activity = contextMapper.getActivityById('service');
expect(activity.behaviour).to.have.property('Service', types.ServiceImplementation);
});
it('ServiceTask without implementation receives no implementation', () => {
const activity = contextMapper.getActivityById('dummyService');
expect(activity.behaviour).to.not.have.property('Service');
});
it('SendTask receives service implementation', () => {
const activity = contextMapper.getActivityById('send');
expect(activity.behaviour).to.have.property('Service', types.ServiceImplementation);
});
it('BusinessRuleTask receives service implementation', () => {
const activity = contextMapper.getActivityById('rule');
expect(activity.behaviour).to.have.property('Service', types.ServiceImplementation);
});
it('UserTask receives service implementation', () => {
const activity = contextMapper.getActivityById('user');
expect(activity.behaviour).to.have.property('Service', types.ServiceImplementation);
});
});
describe('Task loop', () => {
const source = `
\${environment.variables.maxCardinality}
\${environment.services.completed}
\${environment.services.completed}
`;
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(async () => {
const moddleContext = await testHelpers.moddleContext(source);
contextMapper = Serializer(moddleContext, typeResolver);
});
it('MultiInstanceLoopCharacteristics is added to task behaviour', () => {
const activity = contextMapper.getActivityById('multiInstance');
expect(activity.behaviour).to.have.property('loopCharacteristics').with.property('Behaviour', types.MultiInstanceLoopCharacteristics);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property(
'loopCardinality',
'${environment.variables.maxCardinality}',
);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property('completionCondition', '${environment.services.completed}');
});
it('MultiInstanceLoopCharacteristics is deserializable', async () => {
const serializer = Serializer(await testHelpers.moddleContext(source), typeResolver);
const deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
const activity = deserialized.getActivityById('multiInstance');
expect(activity.behaviour).to.have.property('loopCharacteristics').with.property('Behaviour', types.MultiInstanceLoopCharacteristics);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property(
'loopCardinality',
'${environment.variables.maxCardinality}',
);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property('completionCondition', '${environment.services.completed}');
});
it('StandardLoopCharacteristics is added to task behaviour', () => {
const activity = contextMapper.getActivityById('standardLoop');
expect(activity.behaviour).to.have.property('loopCharacteristics').with.property('Behaviour', types.StandardLoopCharacteristics);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property('testBefore', true);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property('loopMaximum', 3);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property('loopCondition', '${environment.services.completed}');
});
it('StandardLoopCharacteristics is deserializable', async () => {
const serializer = Serializer(await testHelpers.moddleContext(source), typeResolver);
const deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
const activity = deserialized.getActivityById('standardLoop');
expect(activity.behaviour).to.have.property('loopCharacteristics').with.property('Behaviour', types.StandardLoopCharacteristics);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property('testBefore', true);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property('loopMaximum', 3);
expect(activity.behaviour.loopCharacteristics.behaviour).to.have.property('loopCondition', '${environment.services.completed}');
});
});
['subProcess', 'transaction'].forEach((type) => {
describe(type, () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(async () => {
const source = `
<${type} id="activity">
${type}>
<${type} id="empty-activity">
activityInput
myDataRef
activityOutput
myDataRef
${type}>
`;
const moddleContext = await testHelpers.moddleContext(source);
contextMapper = Serializer(moddleContext, typeResolver);
});
it(`${type} delivers the right amount of children`, () => {
const activity = contextMapper.getActivityById('activity');
expect(contextMapper.getActivities(activity.id)).to.have.length(3);
expect(contextMapper.getSequenceFlows(activity.id)).to.have.length(2);
});
it(`empty ${type} delivers the right amount of children`, () => {
const activity = contextMapper.getActivityById('empty-activity');
expect(contextMapper.getActivities(activity.id)).to.have.length(0);
expect(contextMapper.getSequenceFlows(activity.id)).to.have.length(0);
});
});
});
describe('bpmn:Error', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let serializer;
before(async () => {
const moddleContext = await testHelpers.moddleContext(factory.resource('bound-error.bpmn'));
serializer = Serializer(moddleContext, typeResolver);
});
it('getActivityById(errorId) returns error', () => {
const error = serializer.getActivityById('Error_0');
expect(error).to.be.ok;
expect(error).to.have.property('type', 'bpmn:Error');
expect(error).to.have.property('name', 'InputError');
expect(error).to.have.property('Behaviour', types.BpmnError);
expect(error).to.have.property('parent').that.eql({ id: 'Definitions_1', type: 'bpmn:Definitions' });
expect(error).to.have.property('behaviour').that.include({ errorCode: '404' });
});
it('deserialized getActivityById(errorId) returns error', () => {
const deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
const error = deserialized.getActivityById('Error_0');
expect(error).to.be.ok;
expect(error).to.have.property('type', 'bpmn:Error');
expect(error).to.have.property('name', 'InputError');
expect(error).to.have.property('Behaviour', types.BpmnError);
expect(error).to.have.property('parent').that.eql({ id: 'Definitions_1', type: 'bpmn:Definitions' });
expect(error).to.have.property('behaviour').that.include({ errorCode: '404' });
});
it('error reference is stored with activity', () => {
const activity = serializer.getActivityById('errorEvent');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('behaviour').with.property('errorRef').with.property('id', 'Error_0');
});
it('deserialized error reference is stored with activity', () => {
const deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
const activity = deserialized.getActivityById('errorEvent');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('behaviour').with.property('errorRef').with.property('id', 'Error_0');
});
});
describe('event definitions', () => {
describe('bpmn:TimerEventDefinition', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(() => {
contextMapper = Serializer(eventDefinitionModdleContext, typeResolver);
});
it('time duration is stored with activity', () => {
const activity = contextMapper.getActivityById('timerEvent');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.TimerEventDefinition);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('behaviour').with.property('timeDuration', 'PT0.05S');
});
it('can be deserialized', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const activity = deserialized.getActivityById('timerEvent');
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.TimerEventDefinition);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('behaviour').with.property('timeDuration', 'PT0.05S');
});
});
describe('bpmn:ConditionalEventDefinition', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(() => {
contextMapper = Serializer(conditionAndEscalationModdleContext, typeResolver);
});
it('condition expression is stored with activity', () => {
const activity = contextMapper.getActivityById('conditionalBoundaryEvent');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.ConditionalEventDefinition);
expect(activity.behaviour.eventDefinitions[0])
.to.have.property('behaviour')
.with.property('expression', '${environment.variables.conditionMet}');
expect(activity.behaviour.eventDefinitions[0].behaviour).to.have.property('condition').that.include({
$type: 'bpmn:FormalExpression',
body: '${environment.variables.conditionMet}',
});
});
it('can be deserialized', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const activity = deserialized.getActivityById('conditionalBoundaryEvent');
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.ConditionalEventDefinition);
expect(activity.behaviour.eventDefinitions[0])
.to.have.property('behaviour')
.with.property('expression', '${environment.variables.conditionMet}');
expect(activity.behaviour.eventDefinitions[0].behaviour).to.have.property('condition').that.include({
$type: 'bpmn:FormalExpression',
body: '${environment.variables.conditionMet}',
});
});
});
describe('bpmn:BoundaryEvent', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(() => {
contextMapper = Serializer(eventDefinitionModdleContext, typeResolver);
});
it('boundary event has attached to', () => {
const activity = contextMapper.getActivityById('errorEvent');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('attachedTo').that.include({
id: 'scriptTask',
type: 'bpmn:ScriptTask',
});
});
it('can be deserialized', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const activity = deserialized.getActivityById('errorEvent');
expect(activity.behaviour).to.have.property('attachedTo').that.include({
id: 'scriptTask',
type: 'bpmn:ScriptTask',
});
});
});
describe('bpmn:ErrorEventDefinition', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(() => {
contextMapper = Serializer(eventDefinitionModdleContext, typeResolver);
});
it('boundary event has event definition with error', () => {
const activity = contextMapper.getActivityById('errorEvent');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.ErrorEventDefinition);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('behaviour').with.property('errorRef');
expect(activity.behaviour.eventDefinitions[0].behaviour.errorRef).to.include({
type: 'bpmn:Error',
id: 'Error_0',
});
});
it('can be deserialized', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const activity = deserialized.getActivityById('errorEvent');
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.ErrorEventDefinition);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('behaviour').with.property('errorRef');
expect(activity.behaviour.eventDefinitions[0].behaviour.errorRef).to.include({
type: 'bpmn:Error',
id: 'Error_0',
});
});
});
it('returns id of event definition', () => {
const contextMapper = Serializer(timersModdleContext, typeResolver);
const activity = contextMapper.getActivityById('start-cycle');
const [ed] = activity.behaviour.eventDefinitions;
expect(ed).to.have.property('id').that.is.ok;
});
});
describe('bpmn:Signal', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(() => {
contextMapper = Serializer(signalEventModdleContext, typeResolver);
});
it('Signal is mapped with name and definition as parent', () => {
const signal = contextMapper.getActivityById('Signal_0');
expect(signal).to.be.ok;
expect(signal).to.have.property('name', 'Semaphore');
expect(signal).to.have.property('parent').that.eql({
id: 'Definitions_0',
type: 'bpmn:Definitions',
});
});
it('can be deserialized', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const signal = deserialized.getActivityById('Signal_0');
expect(signal).to.be.ok;
expect(signal).to.have.property('name', 'Semaphore');
expect(signal).to.have.property('parent').that.eql({
id: 'Definitions_0',
type: 'bpmn:Definitions',
});
});
});
describe('bpmn:Association', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let serializer;
before(() => {
serializer = Serializer(compensationContext, typeResolver);
});
it('association has target and source ref', () => {
const association = serializer.getAssociationById('association_0');
expect(association).to.be.ok;
expect(association).to.have.property('Behaviour').that.equal(types.Association);
expect(association).to.have.property('sourceId', 'compensation');
expect(association).to.have.property('targetId', 'undoService');
expect(association).to.have.property('parent').that.eql({
id: 'Process_0',
type: 'bpmn:Process',
});
});
it('association is deserializable', () => {
const serialized = serializer.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const association = deserialized.getAssociationById('association_0');
expect(association).to.be.ok;
expect(association).to.have.property('Behaviour').that.equal(types.Association);
expect(association).to.have.property('sourceId', 'compensation');
expect(association).to.have.property('targetId', 'undoService');
expect(association).to.have.property('parent').that.eql({
id: 'Process_0',
type: 'bpmn:Process',
});
});
it('getOutboundAssociations(activityId) returns outbound associations', () => {
const associations = serializer.getOutboundAssociations('compensation');
expect(associations).to.have.length(1);
const association = associations[0];
expect(association).to.be.ok;
expect(association).to.have.property('sourceId', 'compensation');
expect(association).to.have.property('targetId', 'undoService');
expect(association).to.have.property('parent').that.eql({
id: 'Process_0',
type: 'bpmn:Process',
});
});
it('getInboundAssociations(activityId) returns inbound associations', () => {
const associations = serializer.getInboundAssociations('undoService');
expect(associations).to.have.length(1);
const association = associations[0];
expect(association).to.be.ok;
expect(association).to.have.property('sourceId', 'compensation');
expect(association).to.have.property('targetId', 'undoService');
expect(association).to.have.property('parent').that.eql({
id: 'Process_0',
type: 'bpmn:Process',
});
});
});
describe('bpmn:ReceiveTask', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(async () => {
const source = `
`;
const moddleContext = await testHelpers.moddleContext(source);
contextMapper = Serializer(moddleContext, typeResolver);
});
it('has reference to message', () => {
const task = contextMapper.getActivityById('receive');
expect(task).to.be.ok;
expect(task).to.have.property('behaviour');
expect(task.behaviour).to.have.property('messageRef');
expect(task.behaviour).to.have.property('messageRef').that.eql({ id: 'Message1', name: 'My message', type: 'bpmn:Message' });
expect(task).to.have.property('Behaviour').that.equal(types.ReceiveTask);
});
it('can be deserialized', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const task = deserialized.getActivityById('receive');
expect(task).to.be.ok;
expect(task).to.have.property('behaviour');
expect(task.behaviour).to.have.property('messageRef');
expect(task.behaviour).to.have.property('messageRef').that.eql({ id: 'Message1', name: 'My message', type: 'bpmn:Message' });
expect(task).to.have.property('Behaviour').that.equal(types.ReceiveTask);
});
});
describe('bpmn:SendTask', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(async () => {
const source = `
`;
const moddleContext = await testHelpers.moddleContext(source);
contextMapper = Serializer(moddleContext, typeResolver);
});
it('has implementation and reference to message', () => {
const task = contextMapper.getActivityById('send');
expect(task).to.be.ok;
expect(task).to.have.property('behaviour');
expect(task.behaviour).to.have.property('Service', types.ServiceImplementation);
expect(task.behaviour).to.have.property('messageRef');
expect(task.behaviour).to.have.property('messageRef').that.eql({ id: 'Message1', name: 'My message', type: 'bpmn:Message' });
expect(task).to.have.property('Behaviour').that.equal(types.SendTask);
});
it('can be deserialized', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const task = deserialized.getActivityById('send');
expect(task).to.be.ok;
expect(task).to.have.property('behaviour');
expect(task.behaviour).to.have.property('Service', types.ServiceImplementation);
expect(task.behaviour).to.have.property('messageRef');
expect(task.behaviour).to.have.property('messageRef').that.eql({ id: 'Message1', name: 'My message', type: 'bpmn:Message' });
expect(task).to.have.property('Behaviour').that.equal(types.SendTask);
});
});
describe('escalation', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(() => {
contextMapper = Serializer(conditionAndEscalationModdleContext, typeResolver);
});
it('escalation event definition is stored with activity', () => {
const activity = contextMapper.getActivityById('end');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.EscalationEventDefinition);
expect(activity.behaviour.eventDefinitions[0].behaviour).to.have.property('escalationRef').with.property('id', 'Escalation_0');
});
it('can be deserialized', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const activity = deserialized.getActivityById('end');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.EscalationEventDefinition);
expect(activity.behaviour.eventDefinitions[0].behaviour).to.have.property('escalationRef').with.property('id', 'Escalation_0');
});
it('escalation element has escalation name and code', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const escalation = deserialized.getActivityById('Escalation_0');
expect(escalation).to.be.ok;
expect(escalation.behaviour).to.have.property('name', 'Escalate');
expect(escalation.behaviour).to.have.property('escalationCode', '404');
});
it('escalation sub process element has triggered by event property', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const activity = deserialized.getActivityById('escalationSubProcess');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('triggeredByEvent', true);
});
it('escalation start element has non-interupting property, escalation name and code', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const activity = deserialized.getActivityById('startEscalation');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('isInterrupting', false);
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.EscalationEventDefinition);
expect(activity.behaviour.eventDefinitions[0].behaviour).to.have.property('escalationRef').with.property('id', 'Escalation_0');
});
});
describe('signal event', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let contextMapper;
before(() => {
contextMapper = Serializer(signalEventModdleContext, typeResolver);
});
it('signal event definition is stored with activity', () => {
const activity = contextMapper.getActivityById('signal');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.SignalEventDefinition);
expect(activity.behaviour.eventDefinitions[0].behaviour).to.have.property('signalRef').with.property('id', 'Signal_0');
});
it('can be deserialized', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const activity = deserialized.getActivityById('signal');
expect(activity).to.be.ok;
expect(activity.behaviour).to.have.property('eventDefinitions').with.length(1);
expect(activity.behaviour.eventDefinitions[0]).to.have.property('Behaviour', types.SignalEventDefinition);
expect(activity.behaviour.eventDefinitions[0].behaviour).to.have.property('signalRef').with.property('id', 'Signal_0');
});
it('signal element has escalation name and code', () => {
const serialized = contextMapper.serialize();
const deserialized = deserialize(JSON.parse(serialized), typeResolver);
const escalation = deserialized.getActivityById('Signal_0');
expect(escalation).to.be.ok;
expect(escalation.behaviour).to.have.property('name', 'Semaphore');
});
});
describe('bpmn:property', () => {
it('activity with bpmn:Property returns reference to dataObject by reference', async () => {
const moddleContext = await testHelpers.moddleContext(factory.resource('engine-issue-139.bpmn'));
const serialized = Serializer(moddleContext, typeResolver);
const activity = serialized.getActivityById('Activity_0ksziuo');
const properties = activity.behaviour.properties;
expect(properties).to.be.an('object').with.property('values').with.length(1);
expect(properties).to.have.property('Behaviour', types.Properties);
expect(properties.values[0]).to.have.property('id');
expect(properties.values[0]).to.have.property('type');
expect(properties.values[0].behaviour).to.have.property('dataInput').with.property('association').with.property('source');
expect(properties.values[0].behaviour.dataInput.association.source).to.have.property('dataObject').with.property('id');
expect(properties.values[0].behaviour.dataInput.association.source, 'no dataStore').to.not.have.property('dataStore');
expect(properties.values[0].behaviour).to.not.have.property('dataOutput');
});
it('activity with bpmn:Property returns reference to dataObject directly', async () => {
const source = `
DataObject_1
prop_1
prop_1
DataObject_2
`;
const moddleContext = await testHelpers.moddleContext(source);
expect(moddleContext.warnings).to.be.empty;
const serialized = Serializer(moddleContext, typeResolver);
const activity = serialized.getActivityById('task');
const properties = activity.behaviour.properties;
expect(properties).to.be.an('object').with.property('values').with.length(1);
expect(properties).to.have.property('Behaviour', types.Properties);
expect(properties.values[0]).to.have.property('id');
expect(properties.values[0]).to.have.property('type');
expect(properties.values[0].behaviour).to.have.property('dataInput').with.property('association').with.property('source');
expect(properties.values[0].behaviour.dataInput.association.source)
.to.have.property('dataObject')
.with.property('id', 'DataObject_1');
expect(properties.values[0].behaviour.dataInput.association.source, 'no dataStore').to.not.have.property('dataStore');
expect(properties.values[0].behaviour).to.have.property('dataOutput');
expect(properties.values[0].behaviour.dataOutput.association.target)
.to.have.property('dataObject')
.with.property('id', 'DataObject_2');
expect(properties.values[0].behaviour.dataOutput.association.target, 'no dataStore').to.not.have.property('dataStore');
});
});
describe('bpmn:dataStoreReference', () => {
it('activity with bpmn:Property associated with dataStoreReference', async () => {
const source = `
DataStoreReference_1
ds-prop
`;
const moddleContext = await testHelpers.moddleContext(source);
expect(moddleContext.warnings).to.be.empty;
const serialized = Serializer(moddleContext, typeResolver);
const activity = serialized.getActivityById('task');
const properties = activity.behaviour.properties;
expect(properties).to.be.an('object').with.property('values').with.length(1);
expect(properties).to.have.property('Behaviour', types.Properties);
expect(properties.values[0]).to.have.property('id');
expect(properties.values[0]).to.have.property('type');
expect(properties.values[0].behaviour, 'dataInput association')
.to.have.property('dataInput')
.with.property('association')
.with.property('source');
expect(properties.values[0].behaviour.dataInput.association.source, 'dataStoreReference')
.to.have.property('dataStore')
.with.property('id', 'DataStoreReference_1');
});
it('activity with bpmn:Property associated with dataStoreReference and dataStore', async () => {
const source = `
DataStoreReference_1
ds-prop
`;
const moddleContext = await testHelpers.moddleContext(source);
expect(moddleContext.warnings, JSON.stringify(moddleContext.warnings)).to.be.empty;
const serialized = Serializer(moddleContext, typeResolver);
const activity = serialized.getActivityById('task');
const properties = activity.behaviour.properties;
expect(properties).to.be.an('object').with.property('values').with.length(1);
expect(properties).to.have.property('Behaviour', types.Properties);
expect(properties.values[0]).to.have.property('id');
expect(properties.values[0]).to.have.property('type');
expect(properties.values[0].behaviour, 'dataInput association')
.to.have.property('dataInput')
.with.property('association')
.with.property('source');
const inputSource = properties.values[0].behaviour.dataInput.association.source;
expect(inputSource, 'dataStore').to.have.property('dataStore').with.property('id', 'datastore');
expect(inputSource.dataStore).to.have.property('isUnlimited', false);
expect(inputSource.dataStore).to.have.property('capacity', 100);
expect(inputSource, 'no dataObject').to.not.have.property('dataObject');
});
it('activity with bpmn:Property associated with dataStore', async () => {
const source = `
datastore
ds-prop
`;
const moddleContext = await testHelpers.moddleContext(source);
expect(moddleContext.warnings, JSON.stringify(moddleContext.warnings)).to.be.empty;
const serialized = Serializer(moddleContext, typeResolver);
const activity = serialized.getActivityById('task');
const properties = activity.behaviour.properties;
expect(properties).to.be.an('object').with.property('values').with.length(1);
expect(properties).to.have.property('Behaviour', types.Properties);
expect(properties.values[0]).to.have.property('id');
expect(properties.values[0]).to.have.property('type');
expect(properties.values[0].behaviour, 'dataInput association')
.to.have.property('dataInput')
.with.property('association')
.with.property('source');
const inputSource = properties.values[0].behaviour.dataInput.association.source;
expect(inputSource, 'dataStore').to.have.property('dataStore').with.property('id', 'datastore');
expect(inputSource.dataStore).to.have.property('isUnlimited', false);
expect(inputSource.dataStore).to.have.property('capacity', 100);
expect(inputSource, 'no dataObject').to.not.have.property('dataObject');
});
it('activity with bpmn:Property output returns reference to dataStoreReference', async () => {
const source = `
ds-prop
DataStoreReference_1
`;
const moddleContext = await testHelpers.moddleContext(source);
expect(moddleContext.warnings).to.be.empty;
const serialized = Serializer(moddleContext, typeResolver);
const activity = serialized.getActivityById('task');
const properties = activity.behaviour.properties;
expect(properties).to.be.an('object').with.property('values').with.length(1);
expect(properties).to.have.property('Behaviour', types.Properties);
expect(properties.values[0]).to.have.property('id');
expect(properties.values[0]).to.have.property('type');
expect(properties.values[0].behaviour, 'dataOutput association')
.to.have.property('dataOutput')
.with.property('association')
.with.property('source');
expect(properties.values[0].behaviour.dataOutput.association.target, 'dataStoreReference')
.to.have.property('dataStore')
.with.property('id', 'DataStoreReference_1');
});
it('activity with bpmn:InputOutputSpecification returns reference to dataStoreReference', async () => {
const source = `
`;
const moddleContext = await testHelpers.moddleContext(source);
expect(moddleContext.warnings).to.be.empty;
const serialized = Serializer(moddleContext, typeResolver);
const activity = serialized.getActivityById('task');
const ioSpecification = activity.behaviour.ioSpecification;
expect(ioSpecification.behaviour).to.be.an('object').with.property('dataInputs').with.length(1);
expect(ioSpecification.behaviour.dataInputs[0].behaviour.association.source.dataStore).to.deep.equal({
$type: 'bpmn:DataStoreReference',
id: 'DataStoreReference_1',
});
expect(ioSpecification.behaviour).to.be.an('object').with.property('dataOutputs').with.length(1);
expect(ioSpecification.behaviour.dataOutputs[0].behaviour.association.target.dataStore).to.deep.equal({
$type: 'bpmn:DataStoreReference',
id: 'DataStoreReference_1',
});
});
it('deserializes dataStores', async () => {
const source = `
DataStoreReference_1
ds-prop
DataStoreReference_2
ds-prop
`;
const moddleContext = await testHelpers.moddleContext(source);
const serializer = Serializer(moddleContext, typeResolver);
let serialized = serializer.serialize();
const parsed = JSON.parse(serialized);
expect(parsed).to.have.property('dataStores').with.length(3);
expect(parsed.dataStores[0]).to.have.property('id', 'DataStoreReference_1');
expect(parsed.dataStores[1]).to.have.property('id', 'DataStoreReference_2');
expect(parsed.dataStores[2]).to.have.property('id', 'datastore');
let deserialized = deserialize(JSON.parse(serialized), typeResolver);
expect(deserialized.getDataStoreReferences()).to.have.length(3);
expect(deserialized.getDataStores())
.to.have.length(1)
.and.deep.equal([
{
id: 'datastore',
type: 'bpmn:DataStore',
Behaviour: types.DataStore,
behaviour: {
$type: 'bpmn:DataStore',
capacity: 100,
id: 'datastore',
isUnlimited: false,
},
parent: {
id: 'Definitions_1',
type: 'bpmn:Definitions',
},
references: [
{
behaviour: {
$type: 'bpmn:DataStoreReference',
id: 'DataStoreReference_2',
},
id: 'DataStoreReference_2',
type: 'bpmn:DataStoreReference',
},
],
},
]);
expect(deserialized.getDataStoreById('datastore')).to.have.property('id', 'datastore');
serialized = deserialized.serialize();
expect(JSON.parse(serialized)).to.have.property('dataStores').with.length(3);
deserialized = deserialize(JSON.parse(serialized), typeResolver);
const dataStores = deserialized.getDataStoreReferences();
expect(dataStores).to.have.property('length', 3);
expect(dataStores[0]).to.have.property('id', 'DataStoreReference_1');
expect(dataStores[0]).to.have.property('Behaviour', types.DataStoreReference);
expect(dataStores[1]).to.have.property('id', 'DataStoreReference_2');
expect(dataStores[1]).to.have.property('Behaviour', types.DataStoreReference);
expect(dataStores[2]).to.have.property('id', 'datastore');
expect(dataStores[2]).to.have.property('Behaviour', types.DataStore);
});
it('getDataStoreReferences() returns all dataStoreReferences', async () => {
const source = `
DataStoreReference_1
ds-prop
`;
const moddleContext = await testHelpers.moddleContext(source);
expect(moddleContext.warnings).to.be.empty;
const serializer = Serializer(moddleContext, typeResolver);
const elements = serializer.getDataStoreReferences();
expect(elements).to.have.length(3);
elements.forEach((elm) => {
expect(elm).to.have.property('id').that.is.ok;
expect(elm).to.have.property('parent').that.is.ok;
expect(elm.parent).to.have.property('id').that.is.ok;
expect(elm.parent).to.have.property('type').that.is.ok;
});
});
it('getDataStoreReferences(scopeId) returns dataStoreReferences within scope', async () => {
const source = `
DataStoreReference_1
ds-prop
`;
const moddleContext = await testHelpers.moddleContext(source);
expect(moddleContext.warnings).to.be.empty;
const serializer = Serializer(moddleContext, typeResolver);
const elements = serializer.getDataStoreReferences('task');
expect(elements).to.have.length(2);
elements.forEach((elm) => {
expect(elm).to.have.property('id').that.is.ok;
expect(elm).to.have.property('parent').that.is.ok;
expect(elm.parent).to.have.property('id').to.equal('task');
expect(elm.parent).to.have.property('type', 'bpmn:SubProcess');
});
});
it('getDataStoreReferenceById(referenceId) returns dataStoreReference or undefined', async () => {
const source = `
DataStoreReference_1
ds-prop
`;
const moddleContext = await testHelpers.moddleContext(source);
const serializer = Serializer(moddleContext, typeResolver);
expect(serializer.getDataStoreReferenceById('DataStoreReference_2')).to.be.undefined;
const dataStore = serializer.getDataStoreReferenceById('DataStoreReference_1');
expect(dataStore).to.have.property('id', 'DataStoreReference_1');
expect(dataStore).to.have.property('parent').that.deep.equal({
id: 'Process_1',
type: 'bpmn:Process',
});
expect(dataStore).to.have.property('Behaviour', types.DataStoreReference);
});
it('getDataStoreById(storeId) returns dataStore or undefined', async () => {
const source = `
DataStoreReference_1
ds-prop
`;
const moddleContext = await testHelpers.moddleContext(source);
const serializer = Serializer(moddleContext, typeResolver);
expect(serializer.getDataStoreReferenceById('DataStoreReference_2')).to.be.undefined;
expect(serializer.getDataStores())
.to.have.length(1)
.and.deep.equal([
{
id: 'datastore',
type: 'bpmn:DataStore',
name: 'My data',
Behaviour: types.DataStore,
behaviour: {
$type: 'bpmn:DataStore',
name: 'My data',
capacity: 100,
id: 'datastore',
isUnlimited: false,
},
parent: {
id: 'Definitions_1',
type: 'bpmn:Definitions',
},
references: [
{
behaviour: {
$type: 'bpmn:DataStoreReference',
id: 'DataStoreReference_1',
},
id: 'DataStoreReference_1',
type: 'bpmn:DataStoreReference',
},
],
},
]);
expect(serializer.getDataStoreById('datastore')).to.have.property('id', 'datastore');
expect(serializer.getDataStoreById('DataStoreReference_1')).to.be.undefined;
});
});
describe('humans', () => {
it('human performer and potential owner is returned in behaviour', async () => {
const source = `
pal
user(pal), group(users)
`;
const context = Serializer(await testHelpers.moddleContext(source), typeResolver);
const task = context.getActivityById('task');
expect(task.behaviour).to.have.property('resources').that.has.length(2);
expect(task.behaviour.resources[0]).to.have.property('type', 'bpmn:HumanPerformer');
expect(task.behaviour.resources[0]).to.have.property('expression', 'pal');
expect(task.behaviour.resources[0]).to.have.property('behaviour').with.property('$type');
expect(task.behaviour.resources[1]).to.have.property('type', 'bpmn:PotentialOwner');
expect(task.behaviour.resources[1]).to.have.property('expression', 'user(pal), group(users)');
expect(task.behaviour.resources[1]).to.have.property('behaviour').with.property('$type');
});
it('human performer and potential owner without expression is still returned in behaviour', async () => {
const source = `
`;
const context = Serializer(await testHelpers.moddleContext(source), typeResolver);
const task = context.getActivityById('task');
expect(task.behaviour).to.have.property('resources').that.has.length(2);
expect(task.behaviour.resources[0]).to.have.property('type', 'bpmn:HumanPerformer');
expect(task.behaviour.resources[0].expression).to.be.undefined;
expect(task.behaviour.resources[0]).to.have.property('behaviour').with.property('$type');
expect(task.behaviour.resources[1]).to.have.property('type', 'bpmn:PotentialOwner');
expect(task.behaviour.resources[1].expression).to.be.undefined;
expect(task.behaviour.resources[1]).to.have.property('behaviour').with.property('$type');
});
it('extended assignee is returned in behaviour', async () => {
const source = `
`;
const moddleOptions = { camunda: testHelpers.getCamundaExtension() };
const context = Serializer(await testHelpers.moddleContext(source, moddleOptions), typeResolver);
const task = context.getActivityById('task');
expect(task.behaviour).to.have.property('assignee', 'pal');
});
});
describe('bpmn:MessageFlow', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let serializer;
before(() => {
serializer = Serializer(messageFlowModdleContext, typeResolver);
});
it('can target lane', () => {
const [toLaneFlow] = serializer.getMessageFlows();
expect(toLaneFlow).to.be.ok;
expect(toLaneFlow).to.have.property('target').with.property('processId', 'participantProcess');
expect(toLaneFlow.target).to.not.have.property('id');
});
it('can target activity', () => {
const [, toActivityFlow] = serializer.getMessageFlows();
expect(toActivityFlow).to.be.ok;
expect(toActivityFlow).to.have.property('target').with.property('processId', 'mainProcess');
expect(toActivityFlow.target).to.have.property('id', 'intermediate');
});
});
describe('bpmn:Collaboration', () => {
/** @type {import('moddle-context-serializer').SerializableContext} */
let serializer;
before(async () => {
const source = `
`;
const moddleContext = await testHelpers.moddleContext(source);
serializer = Serializer(moddleContext, typeResolver);
});
it('participants are stored in elements', () => {
expect(serializer.elements.participants).to.deep.equal([
{
id: 'lane1',
type: 'bpmn:Participant',
name: 'Main',
processId: 'mainProcess',
parent: {
id: 'Collaboration_0',
type: 'bpmn:Collaboration',
},
},
{
id: 'part2',
type: 'bpmn:Participant',
name: 'Participant',
processId: 'participantProcess',
parent: {
id: 'Collaboration_0',
type: 'bpmn:Collaboration',
},
},
{
id: 'part3',
type: 'bpmn:Participant',
name: 'Peer 1',
processId: 'mainProcess',
parent: {
id: 'Collaboration_1',
type: 'bpmn:Collaboration',
},
},
{
id: 'part4',
type: 'bpmn:Participant',
name: 'Peer 2',
processId: 'participantProcess',
parent: {
id: 'Collaboration_1',
type: 'bpmn:Collaboration',
},
},
]);
});
it('participants are deserialized', () => {
const deserialized = deserialize(JSON.parse(serializer.serialize()), typeResolver);
expect(deserialized.elements.participants).to.have.length(4);
for (const p of deserialized.elements.participants) {
expect(p).to.have.property('id');
expect(p).to.have.property('parent').with.property('id');
}
});
it('collaboration message flows get participant information', () => {
const [mflow1, mflow2] = serializer.getMessageFlows();
expect(mflow1).to.have.property('source').that.deep.equal({
participantId: 'lane1',
participantName: 'Main',
processId: 'mainProcess',
});
expect(mflow1).to.have.property('target').that.deep.equal({
participantId: 'part2',
participantName: 'Participant',
processId: 'participantProcess',
});
expect(mflow2).to.have.property('source').that.deep.equal({
participantId: 'part4',
participantName: 'Peer 2',
processId: 'participantProcess',
});
expect(mflow2).to.have.property('target').that.deep.equal({
participantId: 'part3',
participantName: 'Peer 1',
processId: 'mainProcess',
});
});
});
});
describe('type resolver', () => {
it('throws if type is not found', () => {
const resolver = TypeResolver({});
expect(() => {
resolver({ type: 'bpmn:Unknown' });
}).to.throw(/Unknown activity/i);
});
});
function assertEntity(activity) {
const { type, behaviour } = activity;
expect(activity, type).to.have.property('id').that.is.ok;
expect(activity, type).to.have.property('type').that.is.ok;
expect(activity, `${type}.Behaviour`).to.have.property('Behaviour').that.is.a('function');
expect(activity, type).to.have.property('parent').that.is.ok.and.an('object');
expect(activity.parent, `${type}.parent`).to.have.property('id').that.is.ok;
expect(activity.parent, `${type}.parent`).to.have.property('type').that.is.ok;
expect(activity).to.have.property('behaviour').that.is.ok;
if (behaviour.loopCharacteristics) {
expect(behaviour.loopCharacteristics, `${type}.loopCharacteristics`).to.have.property(
'Behaviour',
types.MultiInstanceLoopCharacteristics,
);
}
if (behaviour.ioSpecification) {
expect(behaviour.ioSpecification, `${type}.ioSpecification`).to.have.property('Behaviour', types.InputOutputSpecification);
}
if (behaviour.eventDefinitions) {
behaviour.eventDefinitions.forEach((def) => {
expect(def, `${type}.eventDefinitions.${def.type}`).to.have.property('Behaviour').that.is.a('function');
});
}
if (behaviour.eventDefinitions) {
behaviour.eventDefinitions.forEach((def) => {
expect(def, `${type}.eventDefinitions.${def.type}`).to.have.property('Behaviour').that.is.a('function');
});
}
}
function assertSequenceFlow(flow) {
expect(flow).to.have.property('id').that.is.ok;
expect(flow).to.have.property('type').that.is.ok;
expect(flow).to.have.property('Behaviour', types.SequenceFlow);
expect(flow).to.have.property('sourceId').that.is.ok;
expect(flow).to.have.property('targetId').that.is.ok;
expect(flow).to.have.property('parent').that.is.ok.and.an('object');
expect(flow.parent).to.have.property('id').that.is.ok;
expect(flow.parent).to.have.property('type', 'bpmn:Process');
}