Skip to content

Commit 346deb1

Browse files
authored
Merge pull request #1908 from ProcessMaker/bugfix/FOUR-28994
FOUR-28994: Fix self-service lock after interstitial redirect
2 parents 958d999 + 71990be commit 346deb1

File tree

2 files changed

+167
-6
lines changed

2 files changed

+167
-6
lines changed

src/components/task.vue

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ export default {
293293
.getTasks(url)
294294
.then((response) => {
295295
this.task = response.data;
296+
this.setSelfService(this.task);
296297
this.linkTask(mounting);
297298
this.checkTaskStatus();
298299
if (
@@ -391,6 +392,7 @@ export default {
391392
}
392393
},
393394
checkTaskStatus() {
395+
this.setSelfService(this.task);
394396
if (
395397
this.task.status == 'COMPLETED' ||
396398
this.task.status == 'CLOSED' ||
@@ -402,13 +404,19 @@ export default {
402404
}
403405
this.prepareTask();
404406
},
405-
setSelfService() {
407+
resolveSelfService(task = this.task) {
408+
if (task) {
409+
return (
410+
_.get(task, 'process_request.status') === 'ACTIVE'
411+
&& Boolean(_.get(task, 'is_self_service', false))
412+
);
413+
}
414+
415+
return Boolean(_.get(window, 'ProcessMaker.isSelfService', false));
416+
},
417+
setSelfService(task = this.task) {
406418
this.$nextTick(() => {
407-
if (window.ProcessMaker.isSelfService) {
408-
this.isSelfService = true;
409-
} else {
410-
this.isSelfService = false;
411-
}
419+
this.isSelfService = this.resolveSelfService(task);
412420
});
413421
},
414422
/**
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const vm = require('vm');
4+
5+
const componentPath = path.join(
6+
process.cwd(),
7+
'src/components/task.vue',
8+
);
9+
10+
const source = fs.readFileSync(componentPath, 'utf8');
11+
12+
function getComponentOptions() {
13+
const scriptMatch = source.match(/<script>([\s\S]*?)<\/script>/);
14+
15+
if (!scriptMatch) {
16+
throw new Error('Unable to find task.vue script block');
17+
}
18+
19+
const executableScript = scriptMatch[1]
20+
.replace(/^import .*$/gm, '')
21+
.replace('export default', 'module.exports =');
22+
23+
const sandbox = {
24+
module: { exports: {} },
25+
exports: {},
26+
window: {
27+
ProcessMaker: {},
28+
},
29+
VueFormRenderer: {},
30+
simpleErrorMessage: {},
31+
Promise,
32+
setTimeout,
33+
clearTimeout,
34+
console,
35+
_: {
36+
get: (target, accessor, defaultValue = null) => {
37+
if (!target || !accessor) {
38+
return defaultValue;
39+
}
40+
41+
return accessor.split('.').reduce((value, key) => {
42+
if (value === undefined || value === null) {
43+
return undefined;
44+
}
45+
46+
return value[key];
47+
}, target) ?? defaultValue;
48+
},
49+
merge: (...args) => Object.assign({}, ...args),
50+
},
51+
};
52+
53+
vm.runInNewContext(executableScript, sandbox, { filename: componentPath });
54+
55+
return {
56+
component: sandbox.module.exports,
57+
sandbox,
58+
};
59+
}
60+
61+
describe('Task self-service lock', () => {
62+
const { component: Task, sandbox } = getComponentOptions();
63+
64+
test('resolveSelfService uses the loaded task when available', () => {
65+
const result = Task.methods.resolveSelfService.call({
66+
task: {
67+
process_request: { status: 'ACTIVE' },
68+
is_self_service: 1,
69+
},
70+
});
71+
72+
expect(result).toBe(true);
73+
});
74+
75+
test('setSelfService prefers the current task over the initial window flag', () => {
76+
const context = {
77+
task: {
78+
process_request: { status: 'ACTIVE' },
79+
is_self_service: 1,
80+
},
81+
isSelfService: false,
82+
resolveSelfService: Task.methods.resolveSelfService,
83+
$nextTick: (callback) => callback(),
84+
};
85+
86+
sandbox.window.ProcessMaker = {
87+
isSelfService: false,
88+
};
89+
90+
Task.methods.setSelfService.call(context, context.task);
91+
92+
expect(context.isSelfService).toBe(true);
93+
});
94+
95+
test('checkTaskStatus refreshes self-service state before rendering the task screen', () => {
96+
const setSelfService = jest.fn();
97+
const prepareTask = jest.fn();
98+
const closeTask = jest.fn();
99+
const screen = { config: [] };
100+
const context = {
101+
task: {
102+
status: 'ACTIVE',
103+
screen,
104+
},
105+
screen: null,
106+
setSelfService,
107+
prepareTask,
108+
closeTask,
109+
};
110+
111+
Task.methods.checkTaskStatus.call(context);
112+
113+
expect(setSelfService).toHaveBeenCalledWith(context.task);
114+
expect(context.screen).toBe(screen);
115+
expect(prepareTask).toHaveBeenCalled();
116+
expect(closeTask).not.toHaveBeenCalled();
117+
});
118+
119+
test('loadTask refreshes self-service state as soon as task data arrives', async () => {
120+
const task = {
121+
id: 223,
122+
process_request: { status: 'ACTIVE' },
123+
is_self_service: 1,
124+
screen: { config: [] },
125+
};
126+
const setSelfService = jest.fn();
127+
const linkTask = jest.fn();
128+
const checkTaskStatus = jest.fn();
129+
const getTasks = jest.fn().mockResolvedValue({ data: task });
130+
const context = {
131+
taskId: 223,
132+
nodeId: 'node_1',
133+
screenVersion: null,
134+
beforeLoadTask: jest.fn().mockResolvedValue(),
135+
$dataProvider: { getTasks },
136+
setSelfService,
137+
linkTask,
138+
checkTaskStatus,
139+
loadingTask: true,
140+
hasErrors: false,
141+
};
142+
143+
await Task.methods.loadTask.call(context, false);
144+
await new Promise((resolve) => setTimeout(resolve, 0));
145+
146+
expect(getTasks).toHaveBeenCalled();
147+
expect(context.task).toBe(task);
148+
expect(setSelfService).toHaveBeenCalledWith(task);
149+
expect(linkTask).toHaveBeenCalledWith(false);
150+
expect(checkTaskStatus).toHaveBeenCalled();
151+
expect(context.loadingTask).toBe(false);
152+
});
153+
});

0 commit comments

Comments
 (0)