Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions src/components/task.vue
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ export default {
.getTasks(url)
.then((response) => {
this.task = response.data;
this.setSelfService(this.task);
this.linkTask(mounting);
this.checkTaskStatus();
if (
Expand Down Expand Up @@ -391,6 +392,7 @@ export default {
}
},
checkTaskStatus() {
this.setSelfService(this.task);
if (
this.task.status == 'COMPLETED' ||
this.task.status == 'CLOSED' ||
Expand All @@ -402,13 +404,19 @@ export default {
}
this.prepareTask();
},
setSelfService() {
resolveSelfService(task = this.task) {
if (task) {
return (
_.get(task, 'process_request.status') === 'ACTIVE'
&& Boolean(_.get(task, 'is_self_service', false))
);
}

return Boolean(_.get(window, 'ProcessMaker.isSelfService', false));
},
setSelfService(task = this.task) {
this.$nextTick(() => {
if (window.ProcessMaker.isSelfService) {
this.isSelfService = true;
} else {
this.isSelfService = false;
}
this.isSelfService = this.resolveSelfService(task);
});
},
/**
Expand Down
153 changes: 153 additions & 0 deletions tests/unit/TaskSelfServiceLock.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
const fs = require('fs');
const path = require('path');
const vm = require('vm');

const componentPath = path.join(
process.cwd(),
'src/components/task.vue',
);

const source = fs.readFileSync(componentPath, 'utf8');

function getComponentOptions() {
const scriptMatch = source.match(/<script>([\s\S]*?)<\/script>/);

if (!scriptMatch) {
throw new Error('Unable to find task.vue script block');
}

const executableScript = scriptMatch[1]
.replace(/^import .*$/gm, '')
.replace('export default', 'module.exports =');

const sandbox = {
module: { exports: {} },
exports: {},
window: {
ProcessMaker: {},
},
VueFormRenderer: {},
simpleErrorMessage: {},
Promise,
setTimeout,
clearTimeout,
console,
_: {
get: (target, accessor, defaultValue = null) => {
if (!target || !accessor) {
return defaultValue;
}

return accessor.split('.').reduce((value, key) => {
if (value === undefined || value === null) {
return undefined;
}

return value[key];
}, target) ?? defaultValue;
},
merge: (...args) => Object.assign({}, ...args),
},
};

vm.runInNewContext(executableScript, sandbox, { filename: componentPath });

return {
component: sandbox.module.exports,
sandbox,
};
}

describe('Task self-service lock', () => {
const { component: Task, sandbox } = getComponentOptions();

test('resolveSelfService uses the loaded task when available', () => {
const result = Task.methods.resolveSelfService.call({
task: {
process_request: { status: 'ACTIVE' },
is_self_service: 1,
},
});

expect(result).toBe(true);
});

test('setSelfService prefers the current task over the initial window flag', () => {
const context = {
task: {
process_request: { status: 'ACTIVE' },
is_self_service: 1,
},
isSelfService: false,
resolveSelfService: Task.methods.resolveSelfService,
$nextTick: (callback) => callback(),
};

sandbox.window.ProcessMaker = {
isSelfService: false,
};

Task.methods.setSelfService.call(context, context.task);

expect(context.isSelfService).toBe(true);
});

test('checkTaskStatus refreshes self-service state before rendering the task screen', () => {
const setSelfService = jest.fn();
const prepareTask = jest.fn();
const closeTask = jest.fn();
const screen = { config: [] };
const context = {
task: {
status: 'ACTIVE',
screen,
},
screen: null,
setSelfService,
prepareTask,
closeTask,
};

Task.methods.checkTaskStatus.call(context);

expect(setSelfService).toHaveBeenCalledWith(context.task);
expect(context.screen).toBe(screen);
expect(prepareTask).toHaveBeenCalled();
expect(closeTask).not.toHaveBeenCalled();
});

test('loadTask refreshes self-service state as soon as task data arrives', async () => {
const task = {
id: 223,
process_request: { status: 'ACTIVE' },
is_self_service: 1,
screen: { config: [] },
};
const setSelfService = jest.fn();
const linkTask = jest.fn();
const checkTaskStatus = jest.fn();
const getTasks = jest.fn().mockResolvedValue({ data: task });
const context = {
taskId: 223,
nodeId: 'node_1',
screenVersion: null,
beforeLoadTask: jest.fn().mockResolvedValue(),
$dataProvider: { getTasks },
setSelfService,
linkTask,
checkTaskStatus,
loadingTask: true,
hasErrors: false,
};

await Task.methods.loadTask.call(context, false);
await new Promise((resolve) => setTimeout(resolve, 0));

expect(getTasks).toHaveBeenCalled();
expect(context.task).toBe(task);
expect(setSelfService).toHaveBeenCalledWith(task);
expect(linkTask).toHaveBeenCalledWith(false);
expect(checkTaskStatus).toHaveBeenCalled();
expect(context.loadingTask).toBe(false);
});
});
Loading