@webitel/api-services 0.1.68 → 0.1.70
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/package.json +3 -1
- package/src/api/clients/_shared/types.ts +25 -0
- package/src/api/clients/caseServiceCatalogs/serviceCatalogs.ts +37 -71
- package/src/api/clients/caseServices/services.ts +43 -57
- package/src/api/clients/index.ts +9 -0
- package/src/api/clients/queues/__tests__/fieldsToSend.spec.ts +135 -0
- package/src/api/clients/queues/__tests__/queueLogs.spec.ts +132 -0
- package/src/api/clients/queues/__tests__/queueMembers.spec.ts +175 -0
- package/src/api/clients/queues/__tests__/queues.spec.ts +130 -0
- package/src/api/clients/queues/defaults/__tests__/queueTypeDefaults.spec.ts +143 -0
- package/src/api/clients/queues/defaults/amd.ts +17 -0
- package/src/api/clients/queues/defaults/defaultQueue.ts +9 -0
- package/src/api/clients/queues/defaults/processing.ts +8 -4
- package/src/api/clients/queues/defaults/queueTypeDefaults.ts +305 -0
- package/src/api/clients/queues/queueAgents.ts +48 -0
- package/src/api/clients/queues/queueBuckets.ts +187 -0
- package/src/api/clients/queues/queueHooks.ts +184 -0
- package/src/api/clients/queues/queueLogs.ts +80 -0
- package/src/api/clients/queues/queueMembers.ts +426 -0
- package/src/api/clients/queues/queueResGroups.ts +176 -0
- package/src/api/clients/queues/queueSkills.ts +183 -0
- package/src/api/clients/queues/queueState.ts +97 -0
- package/src/api/clients/queues/queues.ts +124 -94
- package/src/api/clients/queues/scripts/__tests__/mapResetMembersFilters.spec.ts +132 -0
- package/src/api/clients/queues/scripts/mapResetMembersFilters.ts +52 -0
- package/src/enums/CallReportingStatus/CallReportingStatus.ts +13 -0
- package/src/enums/MemberStopCause/MemberStopCause.ts +13 -0
- package/src/enums/QueueStrategy/QueueStrategy.ts +27 -0
- package/src/enums/QueueType/QueueType.ts +31 -0
- package/src/enums/TimeBaseScore/TimeBaseScore.ts +6 -0
- package/src/enums/TypesResourceStrategy/TypesResourceStrategy.ts +7 -0
- package/src/enums/index.ts +21 -1
- package/src/scripts/index.ts +19 -1
- package/src/scripts/normalizeDatetime/normalizeDatetime.ts +132 -0
- package/src/validations/_shared/isFilled.ts +15 -0
- package/src/validations/_shared/lookup.validations.ts +28 -0
- package/src/validations/_shared/variablePair.validations.ts +18 -0
- package/src/validations/caseService/caseService.validations.ts +15 -0
- package/src/validations/caseServiceCatalog/caseServiceCatalog.validations.ts +23 -0
- package/src/validations/index.ts +14 -0
- package/src/validations/queue/__tests__/queue.validations.spec.ts +159 -0
- package/src/validations/queue/queue.rules.ts +166 -0
- package/src/validations/queue/queue.validations.ts +105 -0
- package/src/validations/queue/queueAmd.validations.ts +22 -0
- package/src/validations/queue/queuePayload.validations.ts +71 -0
- package/src/validations/queue/taskProcessing.validations.ts +21 -0
- package/src/validations/queueBucket/queueBucket.validations.ts +10 -0
- package/src/validations/queueHook/queueHook.validations.ts +10 -0
- package/src/validations/queueMember/memberCommunication.validations.ts +25 -0
- package/src/validations/queueMember/queueMember.validations.ts +38 -0
- package/src/validations/queueResGroup/queueResGroup.validations.ts +15 -0
- package/src/validations/queueSkill/queueSkill.validations.ts +42 -0
- package/types/.tsbuildinfo +1 -1
- package/types/api/clients/_shared/types.d.ts +20 -0
- package/types/api/clients/caseServices/services.d.ts +4 -2
- package/types/api/clients/index.d.ts +9 -0
- package/types/api/clients/queues/defaults/amd.d.ts +17 -0
- package/types/api/clients/queues/defaults/defaultQueue.d.ts +9 -0
- package/types/api/clients/queues/defaults/processing.d.ts +7 -2
- package/types/api/clients/queues/defaults/queueTypeDefaults.d.ts +31 -0
- package/types/api/clients/queues/queueAgents.d.ts +7 -0
- package/types/api/clients/queues/queueBuckets.d.ts +12 -0
- package/types/api/clients/queues/queueHooks.d.ts +12 -0
- package/types/api/clients/queues/queueLogs.d.ts +7 -0
- package/types/api/clients/queues/queueMembers.d.ts +30 -0
- package/types/api/clients/queues/queueResGroups.d.ts +15 -0
- package/types/api/clients/queues/queueSkills.d.ts +12 -0
- package/types/api/clients/queues/queueState.d.ts +8 -0
- package/types/api/clients/queues/queues.d.ts +9 -1
- package/types/api/clients/queues/scripts/mapResetMembersFilters.d.ts +30 -0
- package/types/enums/CallReportingStatus/CallReportingStatus.d.ts +11 -0
- package/types/enums/MemberStopCause/MemberStopCause.d.ts +11 -0
- package/types/enums/QueueStrategy/QueueStrategy.d.ts +21 -0
- package/types/enums/QueueType/QueueType.d.ts +28 -0
- package/types/enums/TimeBaseScore/TimeBaseScore.d.ts +5 -0
- package/types/enums/TypesResourceStrategy/TypesResourceStrategy.d.ts +5 -0
- package/types/enums/index.d.ts +7 -1
- package/types/scripts/index.d.ts +3 -1
- package/types/scripts/normalizeDatetime/normalizeDatetime.d.ts +14 -0
- package/types/validations/_shared/isFilled.d.ts +9 -0
- package/types/validations/_shared/lookup.validations.d.ts +18 -0
- package/types/validations/_shared/variablePair.validations.d.ts +16 -0
- package/types/validations/caseService/caseService.validations.d.ts +20 -0
- package/types/validations/caseServiceCatalog/caseServiceCatalog.validations.d.ts +21 -0
- package/types/validations/index.d.ts +14 -0
- package/types/validations/queue/queue.rules.d.ts +21 -0
- package/types/validations/queue/queue.validations.d.ts +285 -0
- package/types/validations/queue/queueAmd.validations.d.ts +22 -0
- package/types/validations/queue/queuePayload.validations.d.ts +75 -0
- package/types/validations/queue/taskProcessing.validations.d.ts +22 -0
- package/types/validations/queueBucket/queueBucket.validations.d.ts +9 -0
- package/types/validations/queueHook/queueHook.validations.d.ts +10 -0
- package/types/validations/queueMember/memberCommunication.validations.d.ts +16 -0
- package/types/validations/queueMember/queueMember.validations.d.ts +48 -0
- package/types/validations/queueResGroup/queueResGroup.validations.d.ts +11 -0
- package/types/validations/queueSkill/queueSkill.validations.d.ts +34 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const searchAttemptsHistory = vi.fn();
|
|
4
|
+
|
|
5
|
+
vi.mock('@webitel/api-services/gen', () => ({
|
|
6
|
+
getMemberService: () => ({
|
|
7
|
+
searchAttemptsHistory,
|
|
8
|
+
}),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const { QueueLogsAPI } = await import('../queueLogs');
|
|
12
|
+
|
|
13
|
+
const paramsSent = () => searchAttemptsHistory.mock.calls[0][0];
|
|
14
|
+
|
|
15
|
+
describe('QueueLogsAPI.getList', () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
searchAttemptsHistory.mockReset().mockResolvedValue({
|
|
18
|
+
data: {
|
|
19
|
+
items: [],
|
|
20
|
+
next: false,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The range filters are protobuf nested messages, so they only bind as
|
|
27
|
+
* `joined_at.from`. The generated params type calls them `joinedAtFrom`,
|
|
28
|
+
* which the backend would ignore — the request would succeed and quietly
|
|
29
|
+
* return unfiltered rows. That is how the duration filter was broken before
|
|
30
|
+
* this migration, so it is worth pinning.
|
|
31
|
+
*
|
|
32
|
+
* Verified live: `joined_at`, `leaving_at` and `offering_at` all filter with
|
|
33
|
+
* this shape. `duration` does not — the backend accepts and ignores it (see
|
|
34
|
+
* the note in `queueLogs.ts`), so this pins the wire format only, not that
|
|
35
|
+
* the duration filter actually works.
|
|
36
|
+
*/
|
|
37
|
+
it('sends range bounds as dotted keys', async () => {
|
|
38
|
+
await QueueLogsAPI.getList({
|
|
39
|
+
parentId: '7',
|
|
40
|
+
joinedAt: {
|
|
41
|
+
from: 100,
|
|
42
|
+
to: 200,
|
|
43
|
+
},
|
|
44
|
+
duration: {
|
|
45
|
+
from: 5,
|
|
46
|
+
to: 30,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(paramsSent()).toMatchObject({
|
|
51
|
+
'joined_at.from': 100,
|
|
52
|
+
'joined_at.to': 200,
|
|
53
|
+
'duration.from': 5,
|
|
54
|
+
'duration.to': 30,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('does not send the flattened names the generated type declares', async () => {
|
|
59
|
+
await QueueLogsAPI.getList({
|
|
60
|
+
parentId: '7',
|
|
61
|
+
joinedAt: {
|
|
62
|
+
from: 100,
|
|
63
|
+
to: 200,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(paramsSent()).not.toHaveProperty('joinedAtFrom');
|
|
68
|
+
expect(paramsSent()).not.toHaveProperty('joinedAtTo');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('maps the filter names onto the service params', async () => {
|
|
72
|
+
await QueueLogsAPI.getList({
|
|
73
|
+
parentId: '7',
|
|
74
|
+
bucket: [
|
|
75
|
+
1,
|
|
76
|
+
],
|
|
77
|
+
agent: [
|
|
78
|
+
2,
|
|
79
|
+
],
|
|
80
|
+
leavingAt: {
|
|
81
|
+
from: 10,
|
|
82
|
+
to: 20,
|
|
83
|
+
},
|
|
84
|
+
offeringAt: {
|
|
85
|
+
from: 30,
|
|
86
|
+
to: 40,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(paramsSent()).toMatchObject({
|
|
91
|
+
bucketId: [
|
|
92
|
+
1,
|
|
93
|
+
],
|
|
94
|
+
agentId: [
|
|
95
|
+
2,
|
|
96
|
+
],
|
|
97
|
+
'leaving_at.from': 10,
|
|
98
|
+
'leaving_at.to': 20,
|
|
99
|
+
'offering_at.from': 30,
|
|
100
|
+
'offering_at.to': 40,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('scopes the query to its queue and maps search onto q', async () => {
|
|
105
|
+
await QueueLogsAPI.getList({
|
|
106
|
+
parentId: '7',
|
|
107
|
+
search: 'joe',
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// `starToSearch` makes it a prefix search, as everywhere else
|
|
111
|
+
expect(paramsSent()).toMatchObject({
|
|
112
|
+
queueId: [
|
|
113
|
+
'7',
|
|
114
|
+
],
|
|
115
|
+
q: 'joe*',
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('sorts by join time unless told otherwise', async () => {
|
|
120
|
+
await QueueLogsAPI.getList({
|
|
121
|
+
parentId: '7',
|
|
122
|
+
});
|
|
123
|
+
expect(paramsSent().sort).toBe('+joined_at');
|
|
124
|
+
|
|
125
|
+
searchAttemptsHistory.mockClear();
|
|
126
|
+
await QueueLogsAPI.getList({
|
|
127
|
+
parentId: '7',
|
|
128
|
+
sort: '-duration',
|
|
129
|
+
});
|
|
130
|
+
expect(paramsSent().sort).toBe('-duration');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const searchMemberInQueue = vi.fn();
|
|
4
|
+
const resetMembersCount = vi.fn();
|
|
5
|
+
const resetMembers = vi.fn();
|
|
6
|
+
|
|
7
|
+
vi.mock('@webitel/api-services/gen', () => ({
|
|
8
|
+
getMemberService: () => ({
|
|
9
|
+
searchMemberInQueue,
|
|
10
|
+
resetMembersCount,
|
|
11
|
+
resetMembers,
|
|
12
|
+
}),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
const { QueueMembersAPI } = await import('../queueMembers');
|
|
16
|
+
|
|
17
|
+
describe('QueueMembersAPI.getList', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
searchMemberInQueue.mockReset().mockResolvedValue({
|
|
20
|
+
data: {
|
|
21
|
+
items: [],
|
|
22
|
+
next: false,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const paramsSent = () => searchMemberInQueue.mock.calls[0][1];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* `created_at` and `priority` are protobuf nested messages, so they only
|
|
31
|
+
* bind as dotted keys. The generated params type calls them `createdAtFrom`
|
|
32
|
+
* and `priorityFrom`, which the backend ignores — the request succeeds and
|
|
33
|
+
* returns unfiltered rows.
|
|
34
|
+
*/
|
|
35
|
+
it('sends range bounds as dotted keys', async () => {
|
|
36
|
+
await QueueMembersAPI.getList({
|
|
37
|
+
parentId: '7',
|
|
38
|
+
createdAt: {
|
|
39
|
+
from: 100,
|
|
40
|
+
to: 200,
|
|
41
|
+
},
|
|
42
|
+
memberPriority: {
|
|
43
|
+
from: 1,
|
|
44
|
+
to: 9,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(paramsSent()).toMatchObject({
|
|
49
|
+
'created_at.from': 100,
|
|
50
|
+
'created_at.to': 200,
|
|
51
|
+
'priority.from': 1,
|
|
52
|
+
'priority.to': 9,
|
|
53
|
+
});
|
|
54
|
+
expect(paramsSent()).not.toHaveProperty('createdAtFrom');
|
|
55
|
+
expect(paramsSent()).not.toHaveProperty('priorityFrom');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('passes the queue id positionally, as a number', async () => {
|
|
59
|
+
await QueueMembersAPI.getList({
|
|
60
|
+
parentId: '7',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
expect(searchMemberInQueue.mock.calls[0][0]).toBe(7);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/** the datalist names differ from the service's, so the mapping is explicit */
|
|
67
|
+
it('maps the filter names onto the service params', async () => {
|
|
68
|
+
await QueueMembersAPI.getList({
|
|
69
|
+
parentId: '7',
|
|
70
|
+
search: 'joe',
|
|
71
|
+
bucket: [
|
|
72
|
+
1,
|
|
73
|
+
],
|
|
74
|
+
agent: [
|
|
75
|
+
2,
|
|
76
|
+
],
|
|
77
|
+
stopCause: [
|
|
78
|
+
'cancel',
|
|
79
|
+
],
|
|
80
|
+
offeringAt: {
|
|
81
|
+
from: 300,
|
|
82
|
+
to: 400,
|
|
83
|
+
},
|
|
84
|
+
attempts: {
|
|
85
|
+
from: 2,
|
|
86
|
+
to: 5,
|
|
87
|
+
},
|
|
88
|
+
name: 'alice',
|
|
89
|
+
destination: '380',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(paramsSent()).toMatchObject({
|
|
93
|
+
q: 'joe*',
|
|
94
|
+
bucketId: [
|
|
95
|
+
1,
|
|
96
|
+
],
|
|
97
|
+
agentId: [
|
|
98
|
+
2,
|
|
99
|
+
],
|
|
100
|
+
stopCause: [
|
|
101
|
+
'cancel',
|
|
102
|
+
],
|
|
103
|
+
'offering_at.from': 300,
|
|
104
|
+
'offering_at.to': 400,
|
|
105
|
+
'attempts.from': 2,
|
|
106
|
+
'attempts.to': 5,
|
|
107
|
+
name: 'alice',
|
|
108
|
+
destination: '380',
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
/** the pre-migration spellings still work, so both can coexist */
|
|
113
|
+
it('still accepts the legacy filter names', async () => {
|
|
114
|
+
await QueueMembersAPI.getList({
|
|
115
|
+
parentId: '7',
|
|
116
|
+
from: 100,
|
|
117
|
+
to: 200,
|
|
118
|
+
cause: [
|
|
119
|
+
'cancel',
|
|
120
|
+
],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(paramsSent()).toMatchObject({
|
|
124
|
+
'created_at.from': 100,
|
|
125
|
+
'created_at.to': 200,
|
|
126
|
+
stopCause: [
|
|
127
|
+
'cancel',
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('QueueMembersAPI.getQuantity', () => {
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
resetMembersCount.mockReset().mockResolvedValue({
|
|
136
|
+
data: {
|
|
137
|
+
count: 12,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
/** was a hand-built url; the generated call must keep the dotted keys */
|
|
143
|
+
it('counts through the service with dotted range keys', async () => {
|
|
144
|
+
const count = await QueueMembersAPI.getQuantity({
|
|
145
|
+
parentId: '7',
|
|
146
|
+
filters: {
|
|
147
|
+
createdAt: {
|
|
148
|
+
from: 100,
|
|
149
|
+
to: 200,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
expect(count).toBe(12);
|
|
155
|
+
expect(resetMembersCount).toHaveBeenCalledWith(
|
|
156
|
+
'7',
|
|
157
|
+
expect.objectContaining({
|
|
158
|
+
'created_at.from': 100,
|
|
159
|
+
'created_at.to': 200,
|
|
160
|
+
}),
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('reports zero when the service returns no count', async () => {
|
|
165
|
+
resetMembersCount.mockResolvedValue({
|
|
166
|
+
data: {},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await expect(
|
|
170
|
+
QueueMembersAPI.getQuantity({
|
|
171
|
+
parentId: '7',
|
|
172
|
+
}),
|
|
173
|
+
).resolves.toBe(0);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { QueueType } from '../../../../enums';
|
|
4
|
+
|
|
5
|
+
const readQueue = vi.fn();
|
|
6
|
+
const searchQueue = vi.fn();
|
|
7
|
+
|
|
8
|
+
vi.mock('@webitel/api-services/gen', () => ({
|
|
9
|
+
getQueueService: () => ({
|
|
10
|
+
readQueue,
|
|
11
|
+
searchQueue,
|
|
12
|
+
}),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
const { QueuesAPI } = await import('../queues');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* proto3 omits zero values, so an OFFLINE_QUEUE (type `0`) arrives with no
|
|
19
|
+
* `type` key at all. Legacy restored it with a `defaultObject`; these tests
|
|
20
|
+
* pin that the restore survived the migration.
|
|
21
|
+
*
|
|
22
|
+
* The failure mode is silent and severe: without `type`, `getQueueDefaults`
|
|
23
|
+
* falls back to the type-agnostic base, the Params tab renders none of the
|
|
24
|
+
* offline-specific controls, and `queueSchema`'s `superRefine` skips the
|
|
25
|
+
* per-type branch — the form just quietly loses half its behaviour.
|
|
26
|
+
*/
|
|
27
|
+
describe('QueuesAPI.get, on a type-less (offline) response', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
readQueue.mockReset();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('restores type 0 and seeds the offline defaults', async () => {
|
|
33
|
+
readQueue.mockResolvedValue({
|
|
34
|
+
data: {
|
|
35
|
+
id: 239,
|
|
36
|
+
name: 'offline',
|
|
37
|
+
payload: {},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const item = await QueuesAPI.get({
|
|
42
|
+
itemId: 239,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(item.type).toBe(QueueType.OFFLINE_QUEUE);
|
|
46
|
+
// seeded from offlineQueue(), not from the base defaults
|
|
47
|
+
expect(item.payload).toMatchObject({
|
|
48
|
+
maxAttempts: 3,
|
|
49
|
+
originateTimeout: 60,
|
|
50
|
+
minOnlineAgents: 0,
|
|
51
|
+
});
|
|
52
|
+
expect(item.taskProcessing).toBeDefined();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('never overwrites a type the backend did send', async () => {
|
|
56
|
+
readQueue.mockResolvedValue({
|
|
57
|
+
data: {
|
|
58
|
+
id: 15,
|
|
59
|
+
type: QueueType.CHAT_INBOUND_QUEUE,
|
|
60
|
+
payload: {},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const item = await QueuesAPI.get({
|
|
65
|
+
itemId: 15,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(item.type).toBe(QueueType.CHAT_INBOUND_QUEUE);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('QueuesAPI.getList, on type-less rows', () => {
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
searchQueue.mockReset();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The table renders `type`, `active` and `waiting` straight into cells, so
|
|
79
|
+
* an omitted zero shows up as a blank column rather than "Offline queue"/0.
|
|
80
|
+
*/
|
|
81
|
+
it('restores the zero values the table renders', async () => {
|
|
82
|
+
searchQueue.mockResolvedValue({
|
|
83
|
+
data: {
|
|
84
|
+
items: [
|
|
85
|
+
{
|
|
86
|
+
id: 239,
|
|
87
|
+
name: 'offline',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
next: false,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const { items } = await QueuesAPI.getList({});
|
|
95
|
+
|
|
96
|
+
expect(items[0]).toMatchObject({
|
|
97
|
+
type: QueueType.OFFLINE_QUEUE,
|
|
98
|
+
enabled: false,
|
|
99
|
+
active: 0,
|
|
100
|
+
waiting: 0,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('leaves non-zero values alone', async () => {
|
|
105
|
+
searchQueue.mockResolvedValue({
|
|
106
|
+
data: {
|
|
107
|
+
items: [
|
|
108
|
+
{
|
|
109
|
+
id: 15,
|
|
110
|
+
type: QueueType.CHAT_INBOUND_QUEUE,
|
|
111
|
+
enabled: true,
|
|
112
|
+
active: 4,
|
|
113
|
+
priority: 1000,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
next: false,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const { items } = await QueuesAPI.getList({});
|
|
121
|
+
|
|
122
|
+
expect(items[0]).toMatchObject({
|
|
123
|
+
type: QueueType.CHAT_INBOUND_QUEUE,
|
|
124
|
+
enabled: true,
|
|
125
|
+
active: 4,
|
|
126
|
+
priority: 1000,
|
|
127
|
+
waiting: 0,
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { QueueType } from '../../../../../enums';
|
|
4
|
+
import { processing } from '../processing';
|
|
5
|
+
import {
|
|
6
|
+
getQueueDefaults,
|
|
7
|
+
hasQueueTypeDefaults,
|
|
8
|
+
QueueTypeDefaults,
|
|
9
|
+
} from '../queueTypeDefaults';
|
|
10
|
+
|
|
11
|
+
const allQueueTypes = Object.values(QueueType);
|
|
12
|
+
|
|
13
|
+
/** The two types whose legacy schema deliberately has no `taskProcessing`. */
|
|
14
|
+
const typesWithoutTaskProcessing = [
|
|
15
|
+
QueueType.OUTBOUND_IVR_QUEUE,
|
|
16
|
+
QueueType.OUTBOUND_JOB_QUEUE,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
/** The three types whose legacy schema carries `payload.amd`. */
|
|
20
|
+
const typesWithAmd = [
|
|
21
|
+
QueueType.OUTBOUND_IVR_QUEUE,
|
|
22
|
+
QueueType.PROGRESSIVE_DIALER,
|
|
23
|
+
QueueType.PREDICTIVE_DIALER,
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
describe('queue type defaults', () => {
|
|
27
|
+
it('has a factory for every QueueType', () => {
|
|
28
|
+
for (const type of allQueueTypes) {
|
|
29
|
+
expect(QueueTypeDefaults[type], `missing factory for ${type}`).toBeTypeOf(
|
|
30
|
+
'function',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
expect(Object.keys(QueueTypeDefaults)).toHaveLength(allQueueTypes.length);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('stamps each factory with its own type', () => {
|
|
37
|
+
for (const type of allQueueTypes) {
|
|
38
|
+
expect(QueueTypeDefaults[type]().type).toBe(type);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The legacy code used factories precisely so two queues never shared a
|
|
44
|
+
* nested object. A plain object export would let edits on one new queue
|
|
45
|
+
* leak into the next one opened.
|
|
46
|
+
*/
|
|
47
|
+
it('returns a fresh deep object on every call', () => {
|
|
48
|
+
for (const type of allQueueTypes) {
|
|
49
|
+
const a = QueueTypeDefaults[type]();
|
|
50
|
+
const b = QueueTypeDefaults[type]();
|
|
51
|
+
|
|
52
|
+
expect(a).not.toBe(b);
|
|
53
|
+
expect(a).toEqual(b);
|
|
54
|
+
expect(a.payload).not.toBe(b.payload);
|
|
55
|
+
expect(a.variables).not.toBe(b.variables);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('keeps taskProcessing exactly where the legacy schema had it', () => {
|
|
60
|
+
for (const type of allQueueTypes) {
|
|
61
|
+
const defaults = QueueTypeDefaults[type]();
|
|
62
|
+
|
|
63
|
+
if (typesWithoutTaskProcessing.includes(type)) {
|
|
64
|
+
expect(defaults.taskProcessing, `${type}`).toBeUndefined();
|
|
65
|
+
} else {
|
|
66
|
+
expect(defaults.taskProcessing, `${type}`).toBeDefined();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('keeps payload.amd exactly where the legacy schema had it', () => {
|
|
72
|
+
for (const type of allQueueTypes) {
|
|
73
|
+
const { payload } = QueueTypeDefaults[type]();
|
|
74
|
+
|
|
75
|
+
expect(Boolean(payload.amd), `${type}`).toBe(typesWithAmd.includes(type));
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Regression guard: api-services' `processing()` used to omit
|
|
81
|
+
* `prolongationOptions`, which left the Processing tab's prolongation block
|
|
82
|
+
* without Regle `$fields` — no required markers, no error text.
|
|
83
|
+
*/
|
|
84
|
+
it('seeds taskProcessing.prolongationOptions', () => {
|
|
85
|
+
expect(processing().prolongationOptions).toEqual({
|
|
86
|
+
enabled: false,
|
|
87
|
+
isTimeoutRetry: false,
|
|
88
|
+
prolongationTimeSec: 30,
|
|
89
|
+
repeatsNumber: 1,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const defaults = QueueTypeDefaults[QueueType.INBOUND_QUEUE]();
|
|
93
|
+
|
|
94
|
+
expect(defaults.taskProcessing).toMatchObject({
|
|
95
|
+
prolongationOptions: {
|
|
96
|
+
repeatsNumber: 1,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('treats IM_CHAT_QUEUE as CHAT_INBOUND_QUEUE apart from the type', () => {
|
|
102
|
+
const chat = QueueTypeDefaults[QueueType.CHAT_INBOUND_QUEUE]();
|
|
103
|
+
const imChat = QueueTypeDefaults[QueueType.IM_CHAT_QUEUE]();
|
|
104
|
+
|
|
105
|
+
expect({
|
|
106
|
+
...imChat,
|
|
107
|
+
type: chat.type,
|
|
108
|
+
}).toEqual(chat);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('getQueueDefaults', () => {
|
|
113
|
+
it('resolves a numeric type', () => {
|
|
114
|
+
expect(getQueueDefaults(QueueType.PREVIEW_DIALER).type).toBe(
|
|
115
|
+
QueueType.PREVIEW_DIALER,
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
/** The card page reads `?type=` off the route, so it arrives as a string. */
|
|
120
|
+
it('resolves a stringified type from the route query', () => {
|
|
121
|
+
expect(getQueueDefaults(String(QueueType.PREDICTIVE_DIALER)).type).toBe(
|
|
122
|
+
QueueType.PREDICTIVE_DIALER,
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('falls back to the base queue shape for an unknown type', () => {
|
|
127
|
+
const defaults = getQueueDefaults(undefined);
|
|
128
|
+
|
|
129
|
+
expect(defaults.type).toBeUndefined();
|
|
130
|
+
expect(defaults).toMatchObject({
|
|
131
|
+
name: '',
|
|
132
|
+
priority: 0,
|
|
133
|
+
variables: [],
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('reports whether a type has defaults', () => {
|
|
138
|
+
expect(hasQueueTypeDefaults(QueueType.OFFLINE_QUEUE)).toBe(true);
|
|
139
|
+
expect(hasQueueTypeDefaults('4')).toBe(true);
|
|
140
|
+
expect(hasQueueTypeDefaults(42)).toBe(false);
|
|
141
|
+
expect(hasQueueTypeDefaults(undefined)).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const amd = () => ({
|
|
2
|
+
enabled: false,
|
|
3
|
+
ai: false,
|
|
4
|
+
positive: [],
|
|
5
|
+
playback: {},
|
|
6
|
+
allowNotSure: false,
|
|
7
|
+
silenceNotSure: false,
|
|
8
|
+
maxWordLength: 5000,
|
|
9
|
+
maxNumberOfWords: 3,
|
|
10
|
+
betweenWordsSilence: 50,
|
|
11
|
+
minWordLength: 100,
|
|
12
|
+
totalAnalysisTime: 5000,
|
|
13
|
+
silenceThreshold: 256,
|
|
14
|
+
afterGreetingSilence: 800,
|
|
15
|
+
greeting: 1500,
|
|
16
|
+
initialSilence: 2500,
|
|
17
|
+
});
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import deepmerge from 'deepmerge';
|
|
2
2
|
|
|
3
|
-
const processing = (
|
|
3
|
+
export const processing = (overrides = {}) =>
|
|
4
4
|
deepmerge(
|
|
5
5
|
{
|
|
6
6
|
enabled: false,
|
|
7
7
|
formSchema: {},
|
|
8
8
|
sec: 30,
|
|
9
9
|
renewalSec: 15,
|
|
10
|
+
prolongationOptions: {
|
|
11
|
+
enabled: false,
|
|
12
|
+
isTimeoutRetry: false,
|
|
13
|
+
prolongationTimeSec: 30,
|
|
14
|
+
repeatsNumber: 1,
|
|
15
|
+
},
|
|
10
16
|
},
|
|
11
|
-
|
|
17
|
+
overrides,
|
|
12
18
|
);
|
|
13
|
-
|
|
14
|
-
export default processing;
|