@webex/contact-center 3.12.0-next.122 → 3.12.0-next.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.sdd/manifest.json +1 -1
- package/ai-docs/CONTRACTS.md +1 -1
- package/dist/services/task/TaskManager.js +137 -0
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +51 -23
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/voice/Voice.js +39 -1
- package/dist/services/task/voice/Voice.js.map +1 -1
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +86 -0
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/services/task/TaskManager.ts +194 -1
- package/src/services/task/ai-docs/task-spec.md +3 -1
- package/src/services/task/state-machine/TaskStateMachine.ts +47 -20
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +18 -7
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +49 -9
- package/src/services/task/voice/Voice.ts +44 -2
- package/test/unit/spec/services/task/Task.ts +74 -6
- package/test/unit/spec/services/task/TaskManager.ts +416 -1
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +568 -0
- package/test/unit/spec/services/task/voice/Voice.ts +158 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -15,6 +15,18 @@ const createConfig = () => ({
|
|
|
15
15
|
isRecordingEnabled: true,
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
+
/** Mirrors Task.emitTaskWrapup: publishWrapup spy only runs when context would emit task:wrapup. */
|
|
19
|
+
function createPublishAwareEmitTaskWrapupMock() {
|
|
20
|
+
const publishWrapup = jest.fn();
|
|
21
|
+
const emitTaskWrapup = ({context}: {context: {taskData?: {wrapUpRequired?: boolean}}}) => {
|
|
22
|
+
if (context.taskData?.wrapUpRequired) {
|
|
23
|
+
publishWrapup();
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return {emitTaskWrapup, publishWrapup};
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
describe('Task state machine', () => {
|
|
19
31
|
const startMachine = () => {
|
|
20
32
|
const actor = createActor(createTaskStateMachine(createConfig()));
|
|
@@ -217,6 +229,24 @@ describe('Task state machine', () => {
|
|
|
217
229
|
expect(emitTaskEnd).not.toHaveBeenCalled();
|
|
218
230
|
});
|
|
219
231
|
|
|
232
|
+
it('enters WRAPPING_UP on TASK_WRAPUP while HOLD_INITIATING and emits wrap-up once', () => {
|
|
233
|
+
const emitTaskWrapup = jest.fn();
|
|
234
|
+
const service = createActor(
|
|
235
|
+
createTaskStateMachine({...createConfig(), agentId: 'agent-1'}, {actions: {emitTaskWrapup}})
|
|
236
|
+
);
|
|
237
|
+
service.start();
|
|
238
|
+
const taskData = createActiveMainCallTaskData();
|
|
239
|
+
|
|
240
|
+
enterTransientHoldState(service, TaskState.HOLD_INITIATING, taskData);
|
|
241
|
+
service.send({
|
|
242
|
+
type: TaskEvent.TASK_WRAPUP,
|
|
243
|
+
taskData: {...taskData, wrapUpRequired: true},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
247
|
+
expect(emitTaskWrapup).toHaveBeenCalledTimes(1);
|
|
248
|
+
});
|
|
249
|
+
|
|
220
250
|
it('ignores PARTICIPANT_LEAVE in HOLD_INITIATING when a remote Drop identifies the current agent', () => {
|
|
221
251
|
const {service, emitTaskEnd} = startMachineWithEndSpy();
|
|
222
252
|
const taskData = createActiveMainCallTaskData();
|
|
@@ -1824,6 +1854,201 @@ describe('Task state machine', () => {
|
|
|
1824
1854
|
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
1825
1855
|
});
|
|
1826
1856
|
|
|
1857
|
+
const primeConferencing = (service: ReturnType<typeof startMachine>, taskData: ReturnType<typeof createTaskData>) => {
|
|
1858
|
+
service.send({type: TaskEvent.TASK_INCOMING, taskData});
|
|
1859
|
+
service.send({type: TaskEvent.ASSIGN, taskData});
|
|
1860
|
+
service.send({
|
|
1861
|
+
type: TaskEvent.CONSULT,
|
|
1862
|
+
destination: 'agent-42',
|
|
1863
|
+
destinationType: 'agent',
|
|
1864
|
+
});
|
|
1865
|
+
service.send({type: TaskEvent.CONSULT_SUCCESS, taskData});
|
|
1866
|
+
service.send({type: TaskEvent.MERGE_TO_CONFERENCE});
|
|
1867
|
+
service.send({type: TaskEvent.CONFERENCE_START, taskData});
|
|
1868
|
+
expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
|
|
1869
|
+
};
|
|
1870
|
+
|
|
1871
|
+
const conferenceInProgressTaskData = (overrides: Parameters<typeof createTaskData>[0] = {}) =>
|
|
1872
|
+
createTaskData({
|
|
1873
|
+
wrapUpRequired: true,
|
|
1874
|
+
interaction: {
|
|
1875
|
+
owner: 'agent-1',
|
|
1876
|
+
state: 'conference',
|
|
1877
|
+
mainInteractionId: 'interaction-1',
|
|
1878
|
+
interactionId: 'interaction-1',
|
|
1879
|
+
participants: {
|
|
1880
|
+
'agent-1': {
|
|
1881
|
+
id: 'agent-1',
|
|
1882
|
+
pType: 'Agent',
|
|
1883
|
+
type: 'Agent',
|
|
1884
|
+
hasJoined: true,
|
|
1885
|
+
hasLeft: false,
|
|
1886
|
+
},
|
|
1887
|
+
'agent-2': {
|
|
1888
|
+
id: 'agent-2',
|
|
1889
|
+
pType: 'Agent',
|
|
1890
|
+
type: 'Agent',
|
|
1891
|
+
hasJoined: true,
|
|
1892
|
+
hasLeft: false,
|
|
1893
|
+
},
|
|
1894
|
+
},
|
|
1895
|
+
media: {
|
|
1896
|
+
'interaction-1': {
|
|
1897
|
+
mediaResourceId: 'interaction-1',
|
|
1898
|
+
mediaType: 'telephony',
|
|
1899
|
+
participants: ['agent-1', 'agent-2'],
|
|
1900
|
+
mType: 'mainCall',
|
|
1901
|
+
isHold: false,
|
|
1902
|
+
},
|
|
1903
|
+
},
|
|
1904
|
+
} as any,
|
|
1905
|
+
...overrides,
|
|
1906
|
+
});
|
|
1907
|
+
|
|
1908
|
+
const primeConnectedWithConferenceTaskData = (
|
|
1909
|
+
service: ReturnType<typeof startMachine>,
|
|
1910
|
+
taskData: ReturnType<typeof createTaskData>
|
|
1911
|
+
) => {
|
|
1912
|
+
service.send({type: TaskEvent.TASK_INCOMING, taskData});
|
|
1913
|
+
service.send({type: TaskEvent.ASSIGN, taskData});
|
|
1914
|
+
expect(service.getSnapshot().value).toBe(TaskState.CONNECTED);
|
|
1915
|
+
};
|
|
1916
|
+
|
|
1917
|
+
it('enters WRAPPING_UP on EXIT_CONFERENCE_SUCCESS while CONNECTED with conference in task data', () => {
|
|
1918
|
+
const service = startMachine();
|
|
1919
|
+
const taskData = conferenceInProgressTaskData();
|
|
1920
|
+
|
|
1921
|
+
primeConnectedWithConferenceTaskData(service, taskData);
|
|
1922
|
+
service.send({type: TaskEvent.EXIT_CONFERENCE_SUCCESS, taskData});
|
|
1923
|
+
|
|
1924
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
1925
|
+
expect(service.getSnapshot().context.taskData?.wrapUpRequired).toBe(true);
|
|
1926
|
+
});
|
|
1927
|
+
|
|
1928
|
+
it('emits wrap-up once on EXIT_CONFERENCE_SUCCESS while CONNECTED', () => {
|
|
1929
|
+
const emitTaskWrapup = jest.fn();
|
|
1930
|
+
const service = createActor(
|
|
1931
|
+
createTaskStateMachine(createConfig(), {actions: {emitTaskWrapup}})
|
|
1932
|
+
);
|
|
1933
|
+
service.start();
|
|
1934
|
+
const taskData = conferenceInProgressTaskData();
|
|
1935
|
+
|
|
1936
|
+
primeConnectedWithConferenceTaskData(service, taskData);
|
|
1937
|
+
service.send({type: TaskEvent.EXIT_CONFERENCE_SUCCESS, taskData});
|
|
1938
|
+
|
|
1939
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
1940
|
+
expect(emitTaskWrapup).toHaveBeenCalledTimes(1);
|
|
1941
|
+
});
|
|
1942
|
+
|
|
1943
|
+
it('terminates on EXIT_CONFERENCE_SUCCESS while CONNECTED when wrap-up is not required', () => {
|
|
1944
|
+
const service = startMachine();
|
|
1945
|
+
const taskData = conferenceInProgressTaskData({
|
|
1946
|
+
wrapUpRequired: false,
|
|
1947
|
+
interaction: {
|
|
1948
|
+
owner: 'other-agent',
|
|
1949
|
+
state: 'conference',
|
|
1950
|
+
mainInteractionId: 'interaction-1',
|
|
1951
|
+
interactionId: 'interaction-1',
|
|
1952
|
+
participants: {
|
|
1953
|
+
'agent-1': {
|
|
1954
|
+
id: 'agent-1',
|
|
1955
|
+
pType: 'Agent',
|
|
1956
|
+
type: 'Agent',
|
|
1957
|
+
hasJoined: true,
|
|
1958
|
+
hasLeft: false,
|
|
1959
|
+
isWrapUp: false,
|
|
1960
|
+
},
|
|
1961
|
+
'agent-2': {
|
|
1962
|
+
id: 'agent-2',
|
|
1963
|
+
pType: 'Agent',
|
|
1964
|
+
type: 'Agent',
|
|
1965
|
+
hasJoined: true,
|
|
1966
|
+
hasLeft: false,
|
|
1967
|
+
},
|
|
1968
|
+
},
|
|
1969
|
+
media: {
|
|
1970
|
+
'interaction-1': {
|
|
1971
|
+
mediaResourceId: 'interaction-1',
|
|
1972
|
+
mediaType: 'telephony',
|
|
1973
|
+
participants: ['agent-1', 'agent-2'],
|
|
1974
|
+
mType: 'mainCall',
|
|
1975
|
+
isHold: false,
|
|
1976
|
+
},
|
|
1977
|
+
},
|
|
1978
|
+
} as any,
|
|
1979
|
+
});
|
|
1980
|
+
|
|
1981
|
+
primeConnectedWithConferenceTaskData(service, taskData);
|
|
1982
|
+
service.send({type: TaskEvent.EXIT_CONFERENCE_SUCCESS, taskData});
|
|
1983
|
+
|
|
1984
|
+
expect(service.getSnapshot().value).toBe(TaskState.TERMINATED);
|
|
1985
|
+
});
|
|
1986
|
+
|
|
1987
|
+
it('does not re-emit wrap-up on late TASK_WRAPUP when wrap-up was already published', () => {
|
|
1988
|
+
const {emitTaskWrapup, publishWrapup} = createPublishAwareEmitTaskWrapupMock();
|
|
1989
|
+
const service = createActor(
|
|
1990
|
+
createTaskStateMachine(createConfig(), {actions: {emitTaskWrapup}})
|
|
1991
|
+
);
|
|
1992
|
+
service.start();
|
|
1993
|
+
const taskData = conferenceInProgressTaskData();
|
|
1994
|
+
|
|
1995
|
+
primeConferencing(service, taskData);
|
|
1996
|
+
service.send({
|
|
1997
|
+
type: TaskEvent.EXIT_CONFERENCE_SUCCESS,
|
|
1998
|
+
taskData: {...taskData, wrapUpRequired: true},
|
|
1999
|
+
});
|
|
2000
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2001
|
+
expect(service.getSnapshot().context.taskData?.wrapUpRequired).toBe(true);
|
|
2002
|
+
|
|
2003
|
+
const emitCountAfterExit = publishWrapup.mock.calls.length;
|
|
2004
|
+
service.send({
|
|
2005
|
+
type: TaskEvent.TASK_WRAPUP,
|
|
2006
|
+
taskData: {...taskData, wrapUpRequired: true},
|
|
2007
|
+
});
|
|
2008
|
+
|
|
2009
|
+
expect(publishWrapup.mock.calls.length).toBe(emitCountAfterExit);
|
|
2010
|
+
});
|
|
2011
|
+
|
|
2012
|
+
it('emits wrap-up on late TASK_WRAPUP when wrap-up was not yet published', () => {
|
|
2013
|
+
const {emitTaskWrapup, publishWrapup} = createPublishAwareEmitTaskWrapupMock();
|
|
2014
|
+
const service = createActor(
|
|
2015
|
+
createTaskStateMachine(createConfig(), {actions: {emitTaskWrapup}})
|
|
2016
|
+
);
|
|
2017
|
+
service.start();
|
|
2018
|
+
const taskData = createTaskData({
|
|
2019
|
+
wrapUpRequired: false,
|
|
2020
|
+
interaction: {
|
|
2021
|
+
owner: 'agent-1',
|
|
2022
|
+
state: 'connected',
|
|
2023
|
+
mainInteractionId: 'interaction-1',
|
|
2024
|
+
interactionId: 'interaction-1',
|
|
2025
|
+
participants: {
|
|
2026
|
+
'agent-1': {
|
|
2027
|
+
id: 'agent-1',
|
|
2028
|
+
pType: 'Agent',
|
|
2029
|
+
type: 'Agent',
|
|
2030
|
+
hasJoined: true,
|
|
2031
|
+
hasLeft: false,
|
|
2032
|
+
},
|
|
2033
|
+
},
|
|
2034
|
+
} as any,
|
|
2035
|
+
});
|
|
2036
|
+
|
|
2037
|
+
service.send({type: TaskEvent.TASK_INCOMING, taskData});
|
|
2038
|
+
service.send({type: TaskEvent.ASSIGN, taskData});
|
|
2039
|
+
service.send({type: TaskEvent.TASK_WRAPUP, taskData});
|
|
2040
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2041
|
+
expect(service.getSnapshot().context.taskData?.wrapUpRequired).not.toBe(true);
|
|
2042
|
+
expect(publishWrapup).not.toHaveBeenCalled();
|
|
2043
|
+
|
|
2044
|
+
service.send({
|
|
2045
|
+
type: TaskEvent.TASK_WRAPUP,
|
|
2046
|
+
taskData: {...taskData, wrapUpRequired: true},
|
|
2047
|
+
});
|
|
2048
|
+
|
|
2049
|
+
expect(publishWrapup).toHaveBeenCalledTimes(1);
|
|
2050
|
+
});
|
|
2051
|
+
|
|
1827
2052
|
it('terminates when EXIT_CONFERENCE_SUCCESS is received in conferencing and wrapup is not required', () => {
|
|
1828
2053
|
const service = startMachine();
|
|
1829
2054
|
const taskData = createTaskData({
|
|
@@ -1883,6 +2108,254 @@ describe('Task state machine', () => {
|
|
|
1883
2108
|
expect(service.getSnapshot().value).toBe(TaskState.TERMINATED);
|
|
1884
2109
|
});
|
|
1885
2110
|
|
|
2111
|
+
it('enters WRAPPING_UP on CONFERENCE_END when wrapUpRequired is stamped', () => {
|
|
2112
|
+
const service = startMachine();
|
|
2113
|
+
const taskData = createTaskData({
|
|
2114
|
+
wrapUpRequired: true,
|
|
2115
|
+
interaction: {
|
|
2116
|
+
owner: 'agent-1',
|
|
2117
|
+
state: 'conference',
|
|
2118
|
+
mainInteractionId: 'interaction-1',
|
|
2119
|
+
interactionId: 'interaction-1',
|
|
2120
|
+
participants: {
|
|
2121
|
+
'agent-1': {
|
|
2122
|
+
id: 'agent-1',
|
|
2123
|
+
pType: 'Agent',
|
|
2124
|
+
type: 'Agent',
|
|
2125
|
+
isWrapUp: true,
|
|
2126
|
+
hasJoined: true,
|
|
2127
|
+
hasLeft: true,
|
|
2128
|
+
},
|
|
2129
|
+
},
|
|
2130
|
+
} as any,
|
|
2131
|
+
});
|
|
2132
|
+
|
|
2133
|
+
primeConferencing(service, taskData);
|
|
2134
|
+
service.send({type: TaskEvent.CONFERENCE_END, taskData});
|
|
2135
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2136
|
+
});
|
|
2137
|
+
|
|
2138
|
+
it('emits wrap-up once on CONFERENCE_END when wrap-up is required', () => {
|
|
2139
|
+
const {emitTaskWrapup, publishWrapup} = createPublishAwareEmitTaskWrapupMock();
|
|
2140
|
+
const service = createActor(
|
|
2141
|
+
createTaskStateMachine(createConfig(), {actions: {emitTaskWrapup}})
|
|
2142
|
+
);
|
|
2143
|
+
service.start();
|
|
2144
|
+
const taskData = createTaskData({
|
|
2145
|
+
wrapUpRequired: true,
|
|
2146
|
+
interaction: {
|
|
2147
|
+
owner: 'agent-1',
|
|
2148
|
+
state: 'conference',
|
|
2149
|
+
mainInteractionId: 'interaction-1',
|
|
2150
|
+
interactionId: 'interaction-1',
|
|
2151
|
+
participants: {
|
|
2152
|
+
'agent-1': {
|
|
2153
|
+
id: 'agent-1',
|
|
2154
|
+
pType: 'Agent',
|
|
2155
|
+
type: 'Agent',
|
|
2156
|
+
isWrapUp: true,
|
|
2157
|
+
hasJoined: true,
|
|
2158
|
+
hasLeft: true,
|
|
2159
|
+
},
|
|
2160
|
+
},
|
|
2161
|
+
} as any,
|
|
2162
|
+
});
|
|
2163
|
+
|
|
2164
|
+
primeConferencing(service, taskData);
|
|
2165
|
+
service.send({type: TaskEvent.CONFERENCE_END, taskData});
|
|
2166
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2167
|
+
expect(publishWrapup).toHaveBeenCalledTimes(1);
|
|
2168
|
+
});
|
|
2169
|
+
|
|
2170
|
+
it('emits wrap-up once on TASK_WRAPUP from CONSULTING when wrap-up is required', () => {
|
|
2171
|
+
const {emitTaskWrapup, publishWrapup} = createPublishAwareEmitTaskWrapupMock();
|
|
2172
|
+
const service = createActor(
|
|
2173
|
+
createTaskStateMachine(createConfig(), {actions: {emitTaskWrapup}})
|
|
2174
|
+
);
|
|
2175
|
+
service.start();
|
|
2176
|
+
const taskData = createTaskData({
|
|
2177
|
+
interaction: {owner: 'agent-1', state: 'connected'} as any,
|
|
2178
|
+
});
|
|
2179
|
+
|
|
2180
|
+
service.send({type: TaskEvent.TASK_INCOMING, taskData});
|
|
2181
|
+
service.send({type: TaskEvent.ASSIGN, taskData});
|
|
2182
|
+
service.send({type: TaskEvent.CONSULT, destination: 'agent-2', destinationType: 'agent'});
|
|
2183
|
+
service.send({type: TaskEvent.CONSULT_SUCCESS, taskData});
|
|
2184
|
+
expect(service.getSnapshot().value).toBe(TaskState.CONSULTING);
|
|
2185
|
+
|
|
2186
|
+
service.send({
|
|
2187
|
+
type: TaskEvent.TASK_WRAPUP,
|
|
2188
|
+
taskData: {...taskData, wrapUpRequired: true},
|
|
2189
|
+
});
|
|
2190
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2191
|
+
expect(publishWrapup).toHaveBeenCalledTimes(1);
|
|
2192
|
+
});
|
|
2193
|
+
|
|
2194
|
+
it('emits wrap-up once on TASK_WRAPUP from CONFERENCING when wrap-up is required', () => {
|
|
2195
|
+
const {emitTaskWrapup, publishWrapup} = createPublishAwareEmitTaskWrapupMock();
|
|
2196
|
+
const service = createActor(
|
|
2197
|
+
createTaskStateMachine(createConfig(), {actions: {emitTaskWrapup}})
|
|
2198
|
+
);
|
|
2199
|
+
service.start();
|
|
2200
|
+
const taskData = createTaskData({
|
|
2201
|
+
interaction: {
|
|
2202
|
+
owner: 'agent-1',
|
|
2203
|
+
state: 'conference',
|
|
2204
|
+
mainInteractionId: 'interaction-1',
|
|
2205
|
+
interactionId: 'interaction-1',
|
|
2206
|
+
} as any,
|
|
2207
|
+
});
|
|
2208
|
+
|
|
2209
|
+
primeConferencing(service, taskData);
|
|
2210
|
+
service.send({
|
|
2211
|
+
type: TaskEvent.TASK_WRAPUP,
|
|
2212
|
+
taskData: {...taskData, wrapUpRequired: true},
|
|
2213
|
+
});
|
|
2214
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2215
|
+
expect(publishWrapup).toHaveBeenCalledTimes(1);
|
|
2216
|
+
});
|
|
2217
|
+
|
|
2218
|
+
it('enters WRAPPING_UP on PARTICIPANT_LEAVE when this agent left and wrap-up is required', () => {
|
|
2219
|
+
const service = startMachine();
|
|
2220
|
+
const conferenceData = createTaskData({
|
|
2221
|
+
interaction: {
|
|
2222
|
+
owner: 'agent-1',
|
|
2223
|
+
state: 'conference',
|
|
2224
|
+
mainInteractionId: 'interaction-1',
|
|
2225
|
+
interactionId: 'interaction-1',
|
|
2226
|
+
participants: {
|
|
2227
|
+
'agent-1': {id: 'agent-1', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
2228
|
+
'agent-2': {id: 'agent-2', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
2229
|
+
},
|
|
2230
|
+
} as any,
|
|
2231
|
+
});
|
|
2232
|
+
const leaveData = createTaskData({
|
|
2233
|
+
wrapUpRequired: true,
|
|
2234
|
+
participantId: 'agent-1',
|
|
2235
|
+
interaction: {
|
|
2236
|
+
owner: 'agent-1',
|
|
2237
|
+
state: 'conference',
|
|
2238
|
+
mainInteractionId: 'interaction-1',
|
|
2239
|
+
interactionId: 'interaction-1',
|
|
2240
|
+
participants: {
|
|
2241
|
+
'agent-2': {id: 'agent-2', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
2242
|
+
},
|
|
2243
|
+
} as any,
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
primeConferencing(service, conferenceData);
|
|
2247
|
+
service.send({
|
|
2248
|
+
type: TaskEvent.PARTICIPANT_LEAVE,
|
|
2249
|
+
participantId: 'agent-1',
|
|
2250
|
+
taskData: leaveData,
|
|
2251
|
+
});
|
|
2252
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2253
|
+
});
|
|
2254
|
+
|
|
2255
|
+
it('emits wrap-up once on PARTICIPANT_LEAVE when wrap-up is required', () => {
|
|
2256
|
+
const emitTaskWrapup = jest.fn();
|
|
2257
|
+
const service = createActor(
|
|
2258
|
+
createTaskStateMachine(createConfig(), {actions: {emitTaskWrapup}})
|
|
2259
|
+
);
|
|
2260
|
+
service.start();
|
|
2261
|
+
const conferenceData = createTaskData({
|
|
2262
|
+
interaction: {
|
|
2263
|
+
owner: 'agent-1',
|
|
2264
|
+
state: 'conference',
|
|
2265
|
+
mainInteractionId: 'interaction-1',
|
|
2266
|
+
interactionId: 'interaction-1',
|
|
2267
|
+
participants: {
|
|
2268
|
+
'agent-1': {id: 'agent-1', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
2269
|
+
'agent-2': {id: 'agent-2', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
2270
|
+
},
|
|
2271
|
+
} as any,
|
|
2272
|
+
});
|
|
2273
|
+
const leaveData = createTaskData({
|
|
2274
|
+
wrapUpRequired: true,
|
|
2275
|
+
participantId: 'agent-1',
|
|
2276
|
+
interaction: {
|
|
2277
|
+
owner: 'agent-1',
|
|
2278
|
+
state: 'conference',
|
|
2279
|
+
mainInteractionId: 'interaction-1',
|
|
2280
|
+
interactionId: 'interaction-1',
|
|
2281
|
+
participants: {
|
|
2282
|
+
'agent-2': {id: 'agent-2', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
2283
|
+
},
|
|
2284
|
+
} as any,
|
|
2285
|
+
});
|
|
2286
|
+
|
|
2287
|
+
primeConferencing(service, conferenceData);
|
|
2288
|
+
service.send({
|
|
2289
|
+
type: TaskEvent.PARTICIPANT_LEAVE,
|
|
2290
|
+
participantId: 'agent-1',
|
|
2291
|
+
taskData: leaveData,
|
|
2292
|
+
});
|
|
2293
|
+
|
|
2294
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2295
|
+
expect(emitTaskWrapup).toHaveBeenCalledTimes(1);
|
|
2296
|
+
});
|
|
2297
|
+
|
|
2298
|
+
it('does not wrap remaining agent on PARTICIPANT_LEAVE', () => {
|
|
2299
|
+
const service = startMachine();
|
|
2300
|
+
const conferenceData = createTaskData({
|
|
2301
|
+
interaction: {
|
|
2302
|
+
owner: 'agent-1',
|
|
2303
|
+
state: 'conference',
|
|
2304
|
+
mainInteractionId: 'interaction-1',
|
|
2305
|
+
interactionId: 'interaction-1',
|
|
2306
|
+
participants: {
|
|
2307
|
+
'agent-1': {id: 'agent-1', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
2308
|
+
'agent-2': {id: 'agent-2', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
2309
|
+
c1: {id: 'c1', pType: 'Customer', type: 'Customer', hasJoined: true, hasLeft: false},
|
|
2310
|
+
},
|
|
2311
|
+
media: {
|
|
2312
|
+
'interaction-1': {
|
|
2313
|
+
mediaResourceId: 'interaction-1',
|
|
2314
|
+
mediaType: 'telephony',
|
|
2315
|
+
mediaMgr: 'mm',
|
|
2316
|
+
participants: ['agent-1', 'agent-2', 'c1'],
|
|
2317
|
+
mType: 'mainCall',
|
|
2318
|
+
isHold: false,
|
|
2319
|
+
holdTimestamp: null,
|
|
2320
|
+
},
|
|
2321
|
+
},
|
|
2322
|
+
} as any,
|
|
2323
|
+
});
|
|
2324
|
+
const otherLeft = createTaskData({
|
|
2325
|
+
wrapUpRequired: false,
|
|
2326
|
+
participantId: 'agent-2',
|
|
2327
|
+
interaction: {
|
|
2328
|
+
owner: 'agent-1',
|
|
2329
|
+
state: 'conference',
|
|
2330
|
+
mainInteractionId: 'interaction-1',
|
|
2331
|
+
interactionId: 'interaction-1',
|
|
2332
|
+
participants: {
|
|
2333
|
+
'agent-1': {id: 'agent-1', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
2334
|
+
c1: {id: 'c1', pType: 'Customer', type: 'Customer', hasJoined: true, hasLeft: false},
|
|
2335
|
+
},
|
|
2336
|
+
media: {
|
|
2337
|
+
'interaction-1': {
|
|
2338
|
+
mediaResourceId: 'interaction-1',
|
|
2339
|
+
mediaType: 'telephony',
|
|
2340
|
+
mediaMgr: 'mm',
|
|
2341
|
+
participants: ['agent-1', 'c1'],
|
|
2342
|
+
mType: 'mainCall',
|
|
2343
|
+
isHold: false,
|
|
2344
|
+
holdTimestamp: null,
|
|
2345
|
+
},
|
|
2346
|
+
},
|
|
2347
|
+
} as any,
|
|
2348
|
+
});
|
|
2349
|
+
|
|
2350
|
+
primeConferencing(service, conferenceData);
|
|
2351
|
+
service.send({
|
|
2352
|
+
type: TaskEvent.PARTICIPANT_LEAVE,
|
|
2353
|
+
participantId: 'agent-2',
|
|
2354
|
+
taskData: otherLeft,
|
|
2355
|
+
});
|
|
2356
|
+
expect(service.getSnapshot().value).not.toBe(TaskState.WRAPPING_UP);
|
|
2357
|
+
});
|
|
2358
|
+
|
|
1886
2359
|
it('downgrades to HELD on HOLD_SUCCESS when no other agents remain on task', () => {
|
|
1887
2360
|
const service = startMachine();
|
|
1888
2361
|
const conferenceTaskData = createSingleAgentConferenceTaskData('conference');
|
|
@@ -2471,6 +2944,28 @@ describe('Task state machine', () => {
|
|
|
2471
2944
|
service.send({type: TaskEvent.TASK_WRAPUP, taskData: wrapupTaskData});
|
|
2472
2945
|
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2473
2946
|
});
|
|
2947
|
+
|
|
2948
|
+
it('emits wrap-up once on TASK_WRAPUP from OFFERED when wrapup is required', () => {
|
|
2949
|
+
const {emitTaskWrapup, publishWrapup} = createPublishAwareEmitTaskWrapupMock();
|
|
2950
|
+
const service = createActor(
|
|
2951
|
+
createTaskStateMachine(createConfig(), {actions: {emitTaskWrapup}})
|
|
2952
|
+
);
|
|
2953
|
+
service.start();
|
|
2954
|
+
const offerTaskData = createTaskData({
|
|
2955
|
+
interaction: {outboundType: 'OUTDIAL'} as any,
|
|
2956
|
+
});
|
|
2957
|
+
service.send({type: TaskEvent.TASK_INCOMING, taskData: offerTaskData});
|
|
2958
|
+
expect(service.getSnapshot().value).toBe(TaskState.OFFERED);
|
|
2959
|
+
|
|
2960
|
+
const wrapupTaskData = createTaskData({
|
|
2961
|
+
agentId: 'agent-1',
|
|
2962
|
+
wrapUpRequired: true,
|
|
2963
|
+
interaction: {outboundType: 'OUTDIAL', state: 'wrapUp', isTerminated: true} as any,
|
|
2964
|
+
});
|
|
2965
|
+
service.send({type: TaskEvent.TASK_WRAPUP, taskData: wrapupTaskData});
|
|
2966
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
2967
|
+
expect(publishWrapup).toHaveBeenCalledTimes(1);
|
|
2968
|
+
});
|
|
2474
2969
|
});
|
|
2475
2970
|
|
|
2476
2971
|
describe('OUTBOUND_FAILED handling', () => {
|
|
@@ -3218,6 +3713,79 @@ describe('Task state machine', () => {
|
|
|
3218
3713
|
expect(service.getSnapshot().value).toBe(TaskState.TERMINATED);
|
|
3219
3714
|
});
|
|
3220
3715
|
|
|
3716
|
+
it('emits wrap-up once on TRANSFER_CONFERENCE_SUCCESS from CONSULTING when wrap-up is required', () => {
|
|
3717
|
+
const {emitTaskWrapup, publishWrapup} = createPublishAwareEmitTaskWrapupMock();
|
|
3718
|
+
const service = createActor(
|
|
3719
|
+
createTaskStateMachine({...createConfig(), agentId: 'agent-1'}, {actions: {emitTaskWrapup}})
|
|
3720
|
+
);
|
|
3721
|
+
service.start();
|
|
3722
|
+
const taskData = createTaskData({
|
|
3723
|
+
agentId: 'agent-1',
|
|
3724
|
+
consultingAgentId: 'agent-1',
|
|
3725
|
+
interaction: {
|
|
3726
|
+
owner: 'agent-1',
|
|
3727
|
+
state: 'conference',
|
|
3728
|
+
mainInteractionId: 'interaction-1',
|
|
3729
|
+
interactionId: 'interaction-1',
|
|
3730
|
+
participants: {
|
|
3731
|
+
'agent-1': {id: 'agent-1', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
3732
|
+
'agent-2': {id: 'agent-2', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
3733
|
+
},
|
|
3734
|
+
} as any,
|
|
3735
|
+
});
|
|
3736
|
+
|
|
3737
|
+
service.send({type: TaskEvent.TASK_INCOMING, taskData});
|
|
3738
|
+
service.send({type: TaskEvent.ASSIGN, taskData});
|
|
3739
|
+
service.send({type: TaskEvent.CONSULT, destination: 'agent-2', destinationType: 'agent'});
|
|
3740
|
+
service.send({type: TaskEvent.CONSULT_SUCCESS, taskData});
|
|
3741
|
+
expect(service.getSnapshot().value).toBe(TaskState.CONSULTING);
|
|
3742
|
+
|
|
3743
|
+
service.send({type: TaskEvent.TRANSFER_CONFERENCE});
|
|
3744
|
+
service.send({
|
|
3745
|
+
type: TaskEvent.TRANSFER_CONFERENCE_SUCCESS,
|
|
3746
|
+
taskData: {...taskData, wrapUpRequired: true},
|
|
3747
|
+
});
|
|
3748
|
+
|
|
3749
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
3750
|
+
expect(publishWrapup).toHaveBeenCalledTimes(1);
|
|
3751
|
+
});
|
|
3752
|
+
|
|
3753
|
+
it('emits wrap-up once on TRANSFER_CONFERENCE_SUCCESS from CONFERENCING when wrap-up is required', () => {
|
|
3754
|
+
const {emitTaskWrapup, publishWrapup} = createPublishAwareEmitTaskWrapupMock();
|
|
3755
|
+
const service = createActor(
|
|
3756
|
+
createTaskStateMachine({...createConfig(), agentId: 'agent-1'}, {actions: {emitTaskWrapup}})
|
|
3757
|
+
);
|
|
3758
|
+
service.start();
|
|
3759
|
+
const taskData = createTaskData({
|
|
3760
|
+
agentId: 'agent-1',
|
|
3761
|
+
interaction: {
|
|
3762
|
+
owner: 'agent-1',
|
|
3763
|
+
state: 'conference',
|
|
3764
|
+
mainInteractionId: 'interaction-1',
|
|
3765
|
+
interactionId: 'interaction-1',
|
|
3766
|
+
participants: {
|
|
3767
|
+
'agent-1': {id: 'agent-1', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
3768
|
+
'agent-2': {id: 'agent-2', pType: 'Agent', type: 'Agent', hasJoined: true, hasLeft: false},
|
|
3769
|
+
},
|
|
3770
|
+
} as any,
|
|
3771
|
+
});
|
|
3772
|
+
|
|
3773
|
+
service.send({type: TaskEvent.TASK_INCOMING, taskData});
|
|
3774
|
+
service.send({type: TaskEvent.ASSIGN, taskData});
|
|
3775
|
+
service.send({type: TaskEvent.MERGE_TO_CONFERENCE});
|
|
3776
|
+
service.send({type: TaskEvent.CONFERENCE_START, taskData});
|
|
3777
|
+
expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
|
|
3778
|
+
|
|
3779
|
+
service.send({type: TaskEvent.TRANSFER_CONFERENCE});
|
|
3780
|
+
service.send({
|
|
3781
|
+
type: TaskEvent.TRANSFER_CONFERENCE_SUCCESS,
|
|
3782
|
+
taskData: {...taskData, wrapUpRequired: true},
|
|
3783
|
+
});
|
|
3784
|
+
|
|
3785
|
+
expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
|
|
3786
|
+
expect(publishWrapup).toHaveBeenCalledTimes(1);
|
|
3787
|
+
});
|
|
3788
|
+
|
|
3221
3789
|
it('terminates secondary agent on PARTICIPANT_LEAVE while in CONSULTING during conference', () => {
|
|
3222
3790
|
const service = startAgent2Machine();
|
|
3223
3791
|
setupSecondaryAgentDesktopConsultingFromConference(service);
|