@webex/contact-center 3.12.0-next.95 → 3.12.0-next.97

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.
Files changed (34) hide show
  1. package/dist/services/task/TaskUtils.js +51 -3
  2. package/dist/services/task/TaskUtils.js.map +1 -1
  3. package/dist/services/task/state-machine/TaskStateMachine.js +50 -14
  4. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
  5. package/dist/services/task/state-machine/actions.js +57 -33
  6. package/dist/services/task/state-machine/actions.js.map +1 -1
  7. package/dist/services/task/state-machine/guards.js +42 -0
  8. package/dist/services/task/state-machine/guards.js.map +1 -1
  9. package/dist/services/task/state-machine/types.js.map +1 -1
  10. package/dist/services/task/state-machine/uiControlsComputer.js +78 -28
  11. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
  12. package/dist/services/task/voice/Voice.js +5 -3
  13. package/dist/services/task/voice/Voice.js.map +1 -1
  14. package/dist/types/services/task/TaskUtils.d.ts +2 -0
  15. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +72 -22
  16. package/dist/types/services/task/state-machine/guards.d.ts +12 -0
  17. package/dist/types/services/task/state-machine/types.d.ts +2 -0
  18. package/dist/webex.js +1 -1
  19. package/package.json +2 -2
  20. package/src/services/task/TaskUtils.ts +100 -2
  21. package/src/services/task/state-machine/TaskStateMachine.ts +101 -12
  22. package/src/services/task/state-machine/actions.ts +88 -35
  23. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +73 -11
  24. package/src/services/task/state-machine/guards.ts +39 -0
  25. package/src/services/task/state-machine/types.ts +2 -0
  26. package/src/services/task/state-machine/uiControlsComputer.ts +135 -41
  27. package/src/services/task/voice/Voice.ts +6 -3
  28. package/test/unit/spec/services/task/TaskUtils.ts +29 -0
  29. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +826 -0
  30. package/test/unit/spec/services/task/state-machine/guards.ts +158 -0
  31. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +648 -5
  32. package/test/unit/spec/services/task/voice/Voice.ts +18 -0
  33. package/umd/contact-center.min.js +2 -2
  34. package/umd/contact-center.min.js.map +1 -1
@@ -1406,6 +1406,130 @@ describe('Task state machine', () => {
1406
1406
  expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
1407
1407
  });
1408
1408
 
1409
+ it('sets hideBlindTransfer flag and clears consult context on EP-DN AgentConsultConferencing (CAI-8329)', () => {
1410
+ const service = startMachine();
1411
+ const agentId = 'agent-1';
1412
+ const consultMediaId = 'consult-media-1';
1413
+ const consultTaskData = createTaskData({
1414
+ agentId,
1415
+ consultingAgentId: agentId,
1416
+ consultMediaResourceId: consultMediaId,
1417
+ isConsulted: false,
1418
+ interaction: {
1419
+ state: 'consulting',
1420
+ interactionId: 'interaction-1',
1421
+ mainInteractionId: 'interaction-1',
1422
+ owner: agentId,
1423
+ participants: {
1424
+ 'agent-1': {
1425
+ id: 'agent-1',
1426
+ pType: 'Agent',
1427
+ hasJoined: true,
1428
+ hasLeft: false,
1429
+ consultState: 'consulting',
1430
+ isConsulted: false,
1431
+ },
1432
+ '+13159998087': {
1433
+ id: '+13159998087',
1434
+ pType: 'EP-DN',
1435
+ hasLeft: false,
1436
+ },
1437
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasJoined: true, hasLeft: false},
1438
+ },
1439
+ media: {
1440
+ 'interaction-1': {
1441
+ mediaResourceId: 'interaction-1',
1442
+ mType: 'mainCall',
1443
+ participants: ['customer-1', 'agent-1'],
1444
+ isHold: false,
1445
+ },
1446
+ [consultMediaId]: {
1447
+ mediaResourceId: consultMediaId,
1448
+ mType: 'consult',
1449
+ participants: ['+13159998087', 'agent-1'],
1450
+ isHold: false,
1451
+ },
1452
+ },
1453
+ } as any,
1454
+ });
1455
+
1456
+ service.send({type: TaskEvent.TASK_INCOMING, taskData: consultTaskData});
1457
+ service.send({type: TaskEvent.ASSIGN, taskData: consultTaskData});
1458
+ service.send({
1459
+ type: TaskEvent.CONSULT,
1460
+ destination: '+13159998087',
1461
+ destinationType: 'entryPoint',
1462
+ });
1463
+ service.send({type: TaskEvent.CONSULT_SUCCESS, taskData: consultTaskData});
1464
+ service.send({type: TaskEvent.MERGE_TO_CONFERENCE});
1465
+ expect(service.getSnapshot().value).toBe(TaskState.CONF_INITIATING);
1466
+ expect(service.getSnapshot().context.consultInitiator).toBe(true);
1467
+
1468
+ const conferencingTaskData = createTaskData({
1469
+ agentId,
1470
+ consultMediaResourceId: consultMediaId,
1471
+ isConsulted: false,
1472
+ type: 'AgentConsultConferencing' as any,
1473
+ interaction: {
1474
+ state: 'conference',
1475
+ interactionId: 'interaction-1',
1476
+ mainInteractionId: 'interaction-1',
1477
+ owner: agentId,
1478
+ participants: {
1479
+ 'agent-1': {
1480
+ id: 'agent-1',
1481
+ pType: 'Agent',
1482
+ hasJoined: true,
1483
+ hasLeft: false,
1484
+ consultState: 'conferencing',
1485
+ isConsulted: false,
1486
+ },
1487
+ '+13159998087': {
1488
+ id: '+13159998087',
1489
+ pType: 'EP-DN',
1490
+ hasLeft: false,
1491
+ },
1492
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasJoined: true, hasLeft: false},
1493
+ },
1494
+ media: {
1495
+ 'interaction-1': {
1496
+ mediaResourceId: 'interaction-1',
1497
+ mType: 'mainCall',
1498
+ participants: ['customer-1', 'agent-1'],
1499
+ isHold: false,
1500
+ },
1501
+ [consultMediaId]: {
1502
+ mediaResourceId: consultMediaId,
1503
+ mType: 'consult',
1504
+ participants: ['+13159998087', 'agent-1'],
1505
+ isHold: false,
1506
+ },
1507
+ },
1508
+ } as any,
1509
+ });
1510
+
1511
+ service.send({type: TaskEvent.CONFERENCE_START, taskData: conferencingTaskData});
1512
+
1513
+ const snapshot = service.getSnapshot();
1514
+ expect(snapshot.value).toBe(TaskState.CONFERENCING);
1515
+ expect(snapshot.context.consultInitiator).toBe(false);
1516
+ expect(snapshot.context.consultDestinationType).toBe(null);
1517
+ expect(snapshot.context.hideBlindTransferForEpDnPendingMerge).toBe(true);
1518
+ expect(snapshot.context.uiControls.activeLeg).toBe('main');
1519
+ expect(snapshot.context.uiControls.main.transfer).toEqual({
1520
+ isVisible: false,
1521
+ isEnabled: false,
1522
+ });
1523
+ expect(snapshot.context.uiControls.main.exitConference).toEqual({
1524
+ isVisible: true,
1525
+ isEnabled: true,
1526
+ });
1527
+ expect(snapshot.context.uiControls.consult.endConsult).toEqual({
1528
+ isVisible: false,
1529
+ isEnabled: false,
1530
+ });
1531
+ });
1532
+
1409
1533
  it('terminates via wrapup when EXIT_CONFERENCE_SUCCESS is received in conferencing', () => {
1410
1534
  const service = startMachine();
1411
1535
  const taskData = createTaskData({
@@ -1699,6 +1823,248 @@ describe('Task state machine', () => {
1699
1823
 
1700
1824
  });
1701
1825
 
1826
+
1827
+ describe('CONFERENCING state CONSULT_FAILED ANOTHER_CONSULT_IN_PROGRESS (CAI-8329)', () => {
1828
+ it('keeps hideBlindTransfer flag and hides transfer after secondary consult rejected from Agent Desktop', () => {
1829
+ const service = startMachine();
1830
+ const agentId = 'agent-1';
1831
+ const consultMediaId = 'consult-media-1';
1832
+ const baseTaskData = createTaskData({
1833
+ agentId,
1834
+ consultingAgentId: agentId,
1835
+ consultMediaResourceId: consultMediaId,
1836
+ isConsulted: false,
1837
+ interaction: {
1838
+ state: 'conference',
1839
+ interactionId: 'interaction-1',
1840
+ mainInteractionId: 'interaction-1',
1841
+ owner: agentId,
1842
+ participants: {
1843
+ [agentId]: {
1844
+ id: agentId,
1845
+ pType: 'Agent',
1846
+ hasJoined: true,
1847
+ hasLeft: false,
1848
+ consultState: 'conferencing',
1849
+ },
1850
+ '+13159998059': {id: '+13159998059', pType: 'EP-DN', hasLeft: false},
1851
+ '+14696762938': {id: '+14696762938', pType: 'Customer', hasJoined: true, hasLeft: false},
1852
+ },
1853
+ media: {
1854
+ 'interaction-1': {
1855
+ mediaResourceId: 'interaction-1',
1856
+ mType: 'mainCall',
1857
+ participants: ['+14696762938', agentId],
1858
+ isHold: false,
1859
+ },
1860
+ [consultMediaId]: {
1861
+ mediaResourceId: consultMediaId,
1862
+ mType: 'consult',
1863
+ participants: ['+13159998059', agentId],
1864
+ isHold: false,
1865
+ },
1866
+ },
1867
+ } as any,
1868
+ });
1869
+
1870
+ service.send({type: TaskEvent.TASK_INCOMING, taskData: baseTaskData});
1871
+ service.send({type: TaskEvent.ASSIGN, taskData: baseTaskData});
1872
+ service.send({
1873
+ type: TaskEvent.CONSULT,
1874
+ destination: '+13159998059',
1875
+ destinationType: 'entryPoint',
1876
+ });
1877
+ service.send({type: TaskEvent.CONSULT_SUCCESS, taskData: baseTaskData});
1878
+ service.send({type: TaskEvent.MERGE_TO_CONFERENCE});
1879
+ service.send({type: TaskEvent.CONFERENCE_START, taskData: baseTaskData});
1880
+ expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
1881
+ expect(service.getSnapshot().context.hideBlindTransferForEpDnPendingMerge).toBe(true);
1882
+
1883
+ const consultFailedTaskData = createTaskData({
1884
+ agentId,
1885
+ consultingAgentId: agentId,
1886
+ consultMediaResourceId: consultMediaId,
1887
+ isConsulted: false,
1888
+ destinationType: 'Agent',
1889
+ reason: 'ANOTHER_CONSULT_IN_PROGRESS',
1890
+ type: 'AgentConsultFailed' as any,
1891
+ interaction: {
1892
+ state: 'conference',
1893
+ interactionId: 'interaction-1',
1894
+ mainInteractionId: 'interaction-1',
1895
+ owner: agentId,
1896
+ participants: {
1897
+ [agentId]: {
1898
+ id: agentId,
1899
+ pType: 'Agent',
1900
+ hasJoined: true,
1901
+ hasLeft: false,
1902
+ consultState: 'conferencing',
1903
+ },
1904
+ '+13159998059': {id: '+13159998059', pType: 'EP-DN', hasLeft: false},
1905
+ '+14696762938': {id: '+14696762938', pType: 'Customer', hasJoined: true, hasLeft: false},
1906
+ },
1907
+ media: {
1908
+ 'interaction-1': {
1909
+ mediaResourceId: 'interaction-1',
1910
+ mType: 'mainCall',
1911
+ participants: ['+14696762938', agentId],
1912
+ isHold: false,
1913
+ },
1914
+ [consultMediaId]: {
1915
+ mediaResourceId: consultMediaId,
1916
+ mType: 'consult',
1917
+ participants: ['+13159998059', agentId],
1918
+ isHold: false,
1919
+ },
1920
+ },
1921
+ } as any,
1922
+ });
1923
+ service.send({type: TaskEvent.CONSULT_FAILED, taskData: consultFailedTaskData, reason: 'ANOTHER_CONSULT_IN_PROGRESS'});
1924
+
1925
+ const snapshot = service.getSnapshot();
1926
+ expect(snapshot.value).toBe(TaskState.CONFERENCING);
1927
+ expect(snapshot.context.hideBlindTransferForEpDnPendingMerge).toBe(true);
1928
+ expect(snapshot.context.uiControls.main.transfer).toEqual({isVisible: false, isEnabled: false});
1929
+ expect(snapshot.context.uiControls.main.consult).toEqual({isVisible: true, isEnabled: false});
1930
+ });
1931
+ });
1932
+
1933
+ describe('CONFERENCING state TASK_WRAPUP after EP-DN post-call (CAI-8329)', () => {
1934
+ it('transitions to WRAPPING_UP on AgentWrapup while in CONFERENCING post_call', () => {
1935
+ const service = startMachine();
1936
+ const agentId = 'agent-1';
1937
+ const consultMediaId = 'consult-media-1';
1938
+ const baseTaskData = createTaskData({
1939
+ agentId,
1940
+ consultingAgentId: agentId,
1941
+ consultMediaResourceId: consultMediaId,
1942
+ isConsulted: false,
1943
+ interaction: {
1944
+ state: 'conference',
1945
+ interactionId: 'interaction-1',
1946
+ mainInteractionId: 'interaction-1',
1947
+ owner: agentId,
1948
+ participants: {
1949
+ [agentId]: {
1950
+ id: agentId,
1951
+ pType: 'Agent',
1952
+ hasJoined: true,
1953
+ hasLeft: false,
1954
+ consultState: 'conferencing',
1955
+ },
1956
+ '+13159998059': {id: '+13159998059', pType: 'EP-DN', hasLeft: false},
1957
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasJoined: true, hasLeft: false},
1958
+ },
1959
+ media: {
1960
+ 'interaction-1': {
1961
+ mediaResourceId: 'interaction-1',
1962
+ mType: 'mainCall',
1963
+ participants: ['customer-1', agentId],
1964
+ isHold: false,
1965
+ },
1966
+ [consultMediaId]: {
1967
+ mediaResourceId: consultMediaId,
1968
+ mType: 'consult',
1969
+ participants: ['+13159998059', agentId],
1970
+ isHold: false,
1971
+ },
1972
+ },
1973
+ } as any,
1974
+ });
1975
+
1976
+ service.send({type: TaskEvent.TASK_INCOMING, taskData: baseTaskData});
1977
+ service.send({type: TaskEvent.ASSIGN, taskData: baseTaskData});
1978
+ service.send({
1979
+ type: TaskEvent.CONSULT,
1980
+ destination: '+13159998059',
1981
+ destinationType: 'entryPoint',
1982
+ });
1983
+ service.send({type: TaskEvent.CONSULT_SUCCESS, taskData: baseTaskData});
1984
+ service.send({type: TaskEvent.MERGE_TO_CONFERENCE});
1985
+ service.send({type: TaskEvent.CONFERENCE_START, taskData: baseTaskData});
1986
+ expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
1987
+
1988
+ const postCallTaskData = createTaskData({
1989
+ agentId,
1990
+ consultMediaResourceId: consultMediaId,
1991
+ isConsulted: false,
1992
+ type: 'ParticipantPostCallActivity' as any,
1993
+ interaction: {
1994
+ state: 'post_call',
1995
+ interactionId: 'interaction-1',
1996
+ mainInteractionId: 'interaction-1',
1997
+ owner: agentId,
1998
+ isTerminated: false,
1999
+ callProcessingDetails: {hasCustomerLeft: 'true'},
2000
+ participants: {
2001
+ [agentId]: {
2002
+ id: agentId,
2003
+ pType: 'Agent',
2004
+ hasJoined: true,
2005
+ hasLeft: false,
2006
+ consultState: 'conferencing',
2007
+ currentState: 'post_call',
2008
+ },
2009
+ '+13159998059': {id: '+13159998059', pType: 'EP-DN', hasLeft: false},
2010
+ '+14696762938': {id: '+14696762938', pType: 'Customer', hasJoined: true, hasLeft: true},
2011
+ },
2012
+ media: {
2013
+ 'interaction-1': {
2014
+ mediaResourceId: 'interaction-1',
2015
+ mType: 'mainCall',
2016
+ participants: [agentId],
2017
+ isHold: false,
2018
+ },
2019
+ [consultMediaId]: {
2020
+ mediaResourceId: consultMediaId,
2021
+ mType: 'consult',
2022
+ participants: ['+13159998059', agentId],
2023
+ isHold: false,
2024
+ },
2025
+ },
2026
+ } as any,
2027
+ });
2028
+ service.send({type: TaskEvent.PARTICIPANT_LEAVE, taskData: postCallTaskData});
2029
+ expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
2030
+ expect(service.getSnapshot().context.hideBlindTransferForEpDnPendingMerge).toBe(true);
2031
+
2032
+ const wrapupTaskData = createTaskData({
2033
+ agentId,
2034
+ wrapUpRequired: true,
2035
+ type: 'AgentWrapup' as any,
2036
+ interaction: {
2037
+ state: 'post_call',
2038
+ interactionId: 'interaction-1',
2039
+ mainInteractionId: 'interaction-1',
2040
+ owner: agentId,
2041
+ isTerminated: true,
2042
+ participants: {
2043
+ [agentId]: {
2044
+ id: agentId,
2045
+ pType: 'Agent',
2046
+ hasJoined: true,
2047
+ hasLeft: false,
2048
+ consultState: 'consultCompleted',
2049
+ isWrapUp: true,
2050
+ },
2051
+ '+14696762938': {id: '+14696762938', pType: 'Customer', hasJoined: true, hasLeft: true},
2052
+ },
2053
+ media: {
2054
+ 'interaction-1': {
2055
+ mediaResourceId: 'interaction-1',
2056
+ mType: 'mainCall',
2057
+ participants: [],
2058
+ isHold: false,
2059
+ },
2060
+ },
2061
+ } as any,
2062
+ });
2063
+ service.send({type: TaskEvent.TASK_WRAPUP, taskData: wrapupTaskData});
2064
+ expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
2065
+ });
2066
+ });
2067
+
1702
2068
  describe('CONFERENCING state CONSULT_END with terminated interaction', () => {
1703
2069
  it('transitions to WRAPPING_UP when CONSULT_END arrives with isTerminated in CONFERENCING', () => {
1704
2070
  const service = startMachine();
@@ -1822,4 +2188,464 @@ describe('Task state machine', () => {
1822
2188
  expect(service.getSnapshot().value).toBe(TaskState.WRAPPING_UP);
1823
2189
  });
1824
2190
  });
2191
+
2192
+ describe('CONSULTING state TRANSFER_CONFERENCE_SUCCESS desktop-initiated (CAI-8329)', () => {
2193
+ const startAgent2Machine = () => {
2194
+ const actor = createActor(
2195
+ createTaskStateMachine({...createConfig(), agentId: 'agent-2'})
2196
+ );
2197
+ actor.start();
2198
+
2199
+ return actor;
2200
+ };
2201
+
2202
+ const createSecondaryAgentConferenceBaseTaskData = () =>
2203
+ createTaskData({
2204
+ agentId: 'agent-2',
2205
+ isConsulted: true,
2206
+ interactionId: 'interaction-1',
2207
+ mediaResourceId: 'interaction-1',
2208
+ consultMediaResourceId: 'consult-media-1',
2209
+ interaction: {
2210
+ state: 'conference',
2211
+ owner: 'agent-1',
2212
+ mainInteractionId: 'interaction-1',
2213
+ interactionId: 'interaction-1',
2214
+ participants: {
2215
+ 'agent-1': {
2216
+ id: 'agent-1',
2217
+ pType: 'Agent',
2218
+ hasLeft: false,
2219
+ consultState: 'conferencing',
2220
+ },
2221
+ 'agent-2': {
2222
+ id: 'agent-2',
2223
+ pType: 'Agent',
2224
+ hasLeft: false,
2225
+ isConsulted: true,
2226
+ consultState: 'conferencing',
2227
+ },
2228
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasLeft: false},
2229
+ },
2230
+ media: {
2231
+ 'interaction-1': {
2232
+ mediaResourceId: 'interaction-1',
2233
+ mType: 'mainCall',
2234
+ participants: ['agent-1', 'agent-2', 'customer-1'],
2235
+ isHold: false,
2236
+ },
2237
+ },
2238
+ } as any,
2239
+ });
2240
+
2241
+ const createSecondaryAgentConsultingTaskData = () =>
2242
+ createTaskData({
2243
+ agentId: 'agent-2',
2244
+ isConsulted: true,
2245
+ consultingAgentId: 'agent-2',
2246
+ type: 'AgentConferenceTransferred' as any,
2247
+ interactionId: 'interaction-1',
2248
+ mediaResourceId: 'interaction-1',
2249
+ consultMediaResourceId: 'consult-media-1',
2250
+ interaction: {
2251
+ state: 'conference',
2252
+ owner: 'agent-1',
2253
+ mainInteractionId: 'interaction-1',
2254
+ interactionId: 'interaction-1',
2255
+ participants: {
2256
+ 'agent-1': {
2257
+ id: 'agent-1',
2258
+ pType: 'Agent',
2259
+ hasLeft: false,
2260
+ consultState: 'conferencing',
2261
+ },
2262
+ 'agent-2': {
2263
+ id: 'agent-2',
2264
+ pType: 'Agent',
2265
+ hasLeft: false,
2266
+ isConsulted: true,
2267
+ consultState: 'consulting',
2268
+ },
2269
+ 'agent-3': {
2270
+ id: 'agent-3',
2271
+ pType: 'Agent',
2272
+ hasLeft: false,
2273
+ isConsulted: true,
2274
+ consultState: 'consulting',
2275
+ },
2276
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasLeft: false},
2277
+ },
2278
+ media: {
2279
+ 'interaction-1': {
2280
+ mediaResourceId: 'interaction-1',
2281
+ mType: 'mainCall',
2282
+ participants: ['agent-1', 'agent-2', 'customer-1'],
2283
+ isHold: false,
2284
+ },
2285
+ 'consult-media-1': {
2286
+ mediaResourceId: 'consult-media-1',
2287
+ mType: 'consult',
2288
+ participants: ['agent-2', 'agent-3'],
2289
+ isHold: false,
2290
+ },
2291
+ },
2292
+ } as any,
2293
+ });
2294
+
2295
+ const setupSecondaryAgentConsultingFromConference = (service: ReturnType<typeof startAgent2Machine>) => {
2296
+ const conferenceTaskData = createSecondaryAgentConferenceBaseTaskData();
2297
+ const consultingTaskData = createSecondaryAgentConsultingTaskData();
2298
+
2299
+ service.send({type: TaskEvent.TASK_INCOMING, taskData: conferenceTaskData});
2300
+ service.send({type: TaskEvent.ASSIGN, taskData: conferenceTaskData});
2301
+ service.send({type: TaskEvent.CONFERENCE_START, taskData: conferenceTaskData});
2302
+ expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
2303
+
2304
+ service.send({type: TaskEvent.CONSULT, destination: 'agent-3', destinationType: 'agent'});
2305
+ expect(service.getSnapshot().value).toBe(TaskState.CONSULT_INITIATING);
2306
+ service.send({type: TaskEvent.CONSULT_SUCCESS, taskData: consultingTaskData});
2307
+ expect(service.getSnapshot().value).toBe(TaskState.CONSULTING);
2308
+ expect(service.getSnapshot().context.consultInitiator).toBe(true);
2309
+ expect(service.getSnapshot().context.consultFromConference).toBe(true);
2310
+ };
2311
+
2312
+ const setupSecondaryAgentDesktopConsultingFromConference = (
2313
+ service: ReturnType<typeof startAgent2Machine>
2314
+ ) => {
2315
+ const conferenceTaskData = createSecondaryAgentConferenceBaseTaskData();
2316
+ const consultingTaskData = createSecondaryAgentConsultingTaskData();
2317
+ const consultingActivePayload = {
2318
+ ...consultingTaskData,
2319
+ type: 'AgentConsulting' as any,
2320
+ };
2321
+ delete (consultingActivePayload as {consultingAgentId?: string}).consultingAgentId;
2322
+
2323
+ service.send({type: TaskEvent.TASK_INCOMING, taskData: conferenceTaskData});
2324
+ service.send({type: TaskEvent.ASSIGN, taskData: conferenceTaskData});
2325
+ service.send({type: TaskEvent.CONFERENCE_START, taskData: conferenceTaskData});
2326
+ expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
2327
+
2328
+ service.send({
2329
+ type: TaskEvent.CONSULTING_ACTIVE,
2330
+ consultDestinationAgentJoined: true,
2331
+ taskData: consultingActivePayload,
2332
+ });
2333
+ expect(service.getSnapshot().value).toBe(TaskState.CONSULTING);
2334
+ expect(service.getSnapshot().context.consultFromConference).toBe(true);
2335
+ };
2336
+
2337
+ it('terminates secondary agent on true desktop path without widgets CONSULT event', () => {
2338
+ const service = startAgent2Machine();
2339
+ setupSecondaryAgentDesktopConsultingFromConference(service);
2340
+
2341
+ const transferSuccessTaskData = createSecondaryAgentConsultingTaskData();
2342
+ service.send({type: TaskEvent.TRANSFER_CONFERENCE_SUCCESS, taskData: transferSuccessTaskData});
2343
+
2344
+ expect(service.getSnapshot().value).toBe(TaskState.TERMINATED);
2345
+ });
2346
+
2347
+ it('terminates secondary agent on widgets-initiated consult conference transfer from CONSULTING', () => {
2348
+ const service = startAgent2Machine();
2349
+ setupSecondaryAgentConsultingFromConference(service);
2350
+
2351
+ const transferSuccessTaskData = createSecondaryAgentConsultingTaskData();
2352
+ service.send({type: TaskEvent.TRANSFER_CONFERENCE_SUCCESS, taskData: transferSuccessTaskData});
2353
+
2354
+ const snapshot = service.getSnapshot();
2355
+ expect(snapshot.value).toBe(TaskState.TERMINATED);
2356
+ expect(snapshot.context.consultInitiator).toBe(false);
2357
+ expect(snapshot.context.consultFromConference).toBe(false);
2358
+ });
2359
+
2360
+ it('moves consultee to CONFERENCING when primary agent conference-transfers', () => {
2361
+ const startAgent3Machine = () => {
2362
+ const actor = createActor(
2363
+ createTaskStateMachine({...createConfig(), agentId: 'agent-3'})
2364
+ );
2365
+ actor.start();
2366
+
2367
+ return actor;
2368
+ };
2369
+
2370
+ const conferenceTaskData = createTaskData({
2371
+ agentId: 'agent-3',
2372
+ isConsulted: true,
2373
+ interactionId: 'interaction-1',
2374
+ mediaResourceId: 'interaction-1',
2375
+ interaction: {
2376
+ state: 'conference',
2377
+ owner: 'agent-1',
2378
+ mainInteractionId: 'interaction-1',
2379
+ interactionId: 'interaction-1',
2380
+ participants: {
2381
+ 'agent-1': {
2382
+ id: 'agent-1',
2383
+ pType: 'Agent',
2384
+ hasLeft: false,
2385
+ consultState: 'conferencing',
2386
+ },
2387
+ 'agent-2': {
2388
+ id: 'agent-2',
2389
+ pType: 'Agent',
2390
+ hasLeft: false,
2391
+ isConsulted: true,
2392
+ consultState: 'conferencing',
2393
+ },
2394
+ 'agent-3': {
2395
+ id: 'agent-3',
2396
+ pType: 'Agent',
2397
+ hasLeft: false,
2398
+ isConsulted: true,
2399
+ consultState: 'conferencing',
2400
+ },
2401
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasLeft: false},
2402
+ },
2403
+ media: {
2404
+ 'interaction-1': {
2405
+ mediaResourceId: 'interaction-1',
2406
+ mType: 'mainCall',
2407
+ participants: ['agent-1', 'agent-2', 'agent-3', 'customer-1'],
2408
+ isHold: false,
2409
+ },
2410
+ },
2411
+ } as any,
2412
+ });
2413
+
2414
+ const consultingTaskData = createTaskData({
2415
+ agentId: 'agent-3',
2416
+ isConsulted: true,
2417
+ consultingAgentId: 'agent-1',
2418
+ interactionId: 'interaction-1',
2419
+ mediaResourceId: 'interaction-1',
2420
+ consultMediaResourceId: 'consult-media-1',
2421
+ interaction: {
2422
+ state: 'conference',
2423
+ owner: 'agent-1',
2424
+ mainInteractionId: 'interaction-1',
2425
+ interactionId: 'interaction-1',
2426
+ participants: {
2427
+ 'agent-1': {
2428
+ id: 'agent-1',
2429
+ pType: 'Agent',
2430
+ hasLeft: false,
2431
+ consultState: 'consulting',
2432
+ },
2433
+ 'agent-2': {
2434
+ id: 'agent-2',
2435
+ pType: 'Agent',
2436
+ hasLeft: false,
2437
+ isConsulted: true,
2438
+ consultState: 'conferencing',
2439
+ },
2440
+ 'agent-3': {
2441
+ id: 'agent-3',
2442
+ pType: 'Agent',
2443
+ hasLeft: false,
2444
+ isConsulted: true,
2445
+ consultState: 'consulting',
2446
+ },
2447
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasLeft: false},
2448
+ },
2449
+ media: {
2450
+ 'interaction-1': {
2451
+ mediaResourceId: 'interaction-1',
2452
+ mType: 'mainCall',
2453
+ participants: ['agent-1', 'agent-2', 'customer-1'],
2454
+ isHold: false,
2455
+ },
2456
+ 'consult-media-1': {
2457
+ mediaResourceId: 'consult-media-1',
2458
+ mType: 'consult',
2459
+ participants: ['agent-1', 'agent-3'],
2460
+ isHold: false,
2461
+ },
2462
+ },
2463
+ } as any,
2464
+ });
2465
+
2466
+ const transferSuccessTaskData = createTaskData({
2467
+ agentId: 'agent-3',
2468
+ isConsulted: true,
2469
+ consultingAgentId: 'agent-1',
2470
+ wrapUpRequired: false,
2471
+ type: 'AgentConferenceTransferred' as any,
2472
+ interactionId: 'interaction-1',
2473
+ mediaResourceId: 'interaction-1',
2474
+ interaction: {
2475
+ state: 'conference',
2476
+ owner: 'agent-1',
2477
+ mainInteractionId: 'interaction-1',
2478
+ interactionId: 'interaction-1',
2479
+ participants: {
2480
+ 'agent-1': {
2481
+ id: 'agent-1',
2482
+ pType: 'Agent',
2483
+ hasLeft: true,
2484
+ consultState: 'conferencing',
2485
+ },
2486
+ 'agent-2': {
2487
+ id: 'agent-2',
2488
+ pType: 'Agent',
2489
+ hasLeft: false,
2490
+ isConsulted: true,
2491
+ consultState: 'conferencing',
2492
+ },
2493
+ 'agent-3': {
2494
+ id: 'agent-3',
2495
+ pType: 'Agent',
2496
+ hasLeft: false,
2497
+ isConsulted: true,
2498
+ consultState: 'consulting',
2499
+ },
2500
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasLeft: false},
2501
+ },
2502
+ media: {
2503
+ 'interaction-1': {
2504
+ mediaResourceId: 'interaction-1',
2505
+ mType: 'mainCall',
2506
+ participants: ['agent-2', 'agent-3', 'customer-1'],
2507
+ isHold: false,
2508
+ },
2509
+ },
2510
+ } as any,
2511
+ });
2512
+
2513
+ const service = startAgent3Machine();
2514
+ service.send({type: TaskEvent.TASK_INCOMING, taskData: conferenceTaskData});
2515
+ service.send({type: TaskEvent.ASSIGN, taskData: conferenceTaskData});
2516
+ service.send({type: TaskEvent.CONFERENCE_START, taskData: conferenceTaskData});
2517
+ expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
2518
+
2519
+ service.send({
2520
+ type: TaskEvent.CONSULTING_ACTIVE,
2521
+ consultDestinationAgentJoined: true,
2522
+ taskData: consultingTaskData,
2523
+ });
2524
+ expect(service.getSnapshot().value).toBe(TaskState.CONSULTING);
2525
+ expect(service.getSnapshot().context.consultInitiator).toBe(false);
2526
+
2527
+ service.send({type: TaskEvent.TRANSFER_CONFERENCE_SUCCESS, taskData: transferSuccessTaskData});
2528
+ expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
2529
+ });
2530
+
2531
+ it('passive observer stays in CONFERENCING when another agent conference-transfers', () => {
2532
+ const service = startAgent2Machine();
2533
+ const conferenceTaskData = createSecondaryAgentConferenceBaseTaskData();
2534
+ const transferSuccessTaskData = createTaskData({
2535
+ agentId: 'agent-2',
2536
+ isConsulted: true,
2537
+ type: 'AgentConferenceTransferred' as any,
2538
+ interactionId: 'interaction-1',
2539
+ mediaResourceId: 'interaction-1',
2540
+ interaction: {
2541
+ state: 'conference',
2542
+ owner: 'agent-1',
2543
+ mainInteractionId: 'interaction-1',
2544
+ interactionId: 'interaction-1',
2545
+ participants: {
2546
+ 'agent-1': {
2547
+ id: 'agent-1',
2548
+ pType: 'Agent',
2549
+ hasLeft: false,
2550
+ consultState: 'conferencing',
2551
+ },
2552
+ 'agent-2': {
2553
+ id: 'agent-2',
2554
+ pType: 'Agent',
2555
+ hasLeft: false,
2556
+ isConsulted: true,
2557
+ consultState: 'conferencing',
2558
+ },
2559
+ 'agent-3': {
2560
+ id: 'agent-3',
2561
+ pType: 'Agent',
2562
+ hasLeft: false,
2563
+ isConsulted: true,
2564
+ consultState: 'conferencing',
2565
+ },
2566
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasLeft: false},
2567
+ },
2568
+ media: {
2569
+ 'interaction-1': {
2570
+ mediaResourceId: 'interaction-1',
2571
+ mType: 'mainCall',
2572
+ participants: ['agent-1', 'agent-2', 'agent-3', 'customer-1'],
2573
+ isHold: false,
2574
+ },
2575
+ },
2576
+ } as any,
2577
+ });
2578
+
2579
+ service.send({type: TaskEvent.TASK_INCOMING, taskData: conferenceTaskData});
2580
+ service.send({type: TaskEvent.ASSIGN, taskData: conferenceTaskData});
2581
+ service.send({type: TaskEvent.CONFERENCE_START, taskData: conferenceTaskData});
2582
+ expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
2583
+ expect(service.getSnapshot().context.consultInitiator).toBe(false);
2584
+
2585
+ service.send({type: TaskEvent.TRANSFER_CONFERENCE_SUCCESS, taskData: transferSuccessTaskData});
2586
+ expect(service.getSnapshot().value).toBe(TaskState.CONFERENCING);
2587
+ });
2588
+
2589
+ it('terminates secondary agent on widgets-initiated conference transfer regression', () => {
2590
+ const service = startAgent2Machine();
2591
+ setupSecondaryAgentConsultingFromConference(service);
2592
+
2593
+ const transferSuccessTaskData = createSecondaryAgentConsultingTaskData();
2594
+ service.send({type: TaskEvent.TRANSFER_CONFERENCE});
2595
+ service.send({type: TaskEvent.TRANSFER_CONFERENCE_SUCCESS, taskData: transferSuccessTaskData});
2596
+
2597
+ expect(service.getSnapshot().value).toBe(TaskState.TERMINATED);
2598
+ });
2599
+
2600
+ it('terminates secondary agent on PARTICIPANT_LEAVE while in CONSULTING during conference', () => {
2601
+ const service = startAgent2Machine();
2602
+ setupSecondaryAgentDesktopConsultingFromConference(service);
2603
+
2604
+ const selfLeftTaskData = createTaskData({
2605
+ agentId: 'agent-2',
2606
+ isConsulted: true,
2607
+ participantId: 'agent-2',
2608
+ interactionId: 'interaction-1',
2609
+ mediaResourceId: 'interaction-1',
2610
+ interaction: {
2611
+ state: 'conference',
2612
+ owner: 'agent-1',
2613
+ mainInteractionId: 'interaction-1',
2614
+ interactionId: 'interaction-1',
2615
+ participants: {
2616
+ 'agent-1': {
2617
+ id: 'agent-1',
2618
+ pType: 'Agent',
2619
+ hasLeft: false,
2620
+ consultState: 'conferencing',
2621
+ },
2622
+ 'agent-3': {
2623
+ id: 'agent-3',
2624
+ pType: 'Agent',
2625
+ hasLeft: false,
2626
+ isConsulted: true,
2627
+ consultState: 'consulting',
2628
+ },
2629
+ 'customer-1': {id: 'customer-1', pType: 'Customer', hasLeft: false},
2630
+ },
2631
+ media: {
2632
+ 'interaction-1': {
2633
+ mediaResourceId: 'interaction-1',
2634
+ mType: 'mainCall',
2635
+ participants: ['agent-1', 'agent-3', 'customer-1'],
2636
+ isHold: false,
2637
+ },
2638
+ },
2639
+ } as any,
2640
+ });
2641
+
2642
+ service.send({
2643
+ type: TaskEvent.PARTICIPANT_LEAVE,
2644
+ taskData: selfLeftTaskData,
2645
+ participantId: 'agent-2',
2646
+ });
2647
+
2648
+ expect(service.getSnapshot().value).toBe(TaskState.TERMINATED);
2649
+ });
2650
+ });
1825
2651
  });