@wordpress/abilities 0.1.1-next.dc3f6d3c1.0 → 0.2.1-next.8b30e05b0.0
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/CHANGELOG.md +2 -0
- package/build-types/store/index.d.ts +1 -1
- package/build-types/store/index.d.ts.map +1 -1
- package/package.json +18 -9
- package/build/store/tests/actions.test.js +0 -792
- package/build/store/tests/actions.test.js.map +0 -7
- package/build/store/tests/reducer.test.js +0 -743
- package/build/store/tests/reducer.test.js.map +0 -7
- package/build/store/tests/resolvers.test.js +0 -520
- package/build/store/tests/resolvers.test.js.map +0 -7
- package/build/store/tests/selectors.test.js +0 -349
- package/build/store/tests/selectors.test.js.map +0 -7
- package/build/tests/api.test.js +0 -546
- package/build/tests/api.test.js.map +0 -7
- package/build/tests/validation.test.js +0 -453
- package/build/tests/validation.test.js.map +0 -7
- package/build-module/store/tests/actions.test.js +0 -804
- package/build-module/store/tests/actions.test.js.map +0 -7
- package/build-module/store/tests/reducer.test.js +0 -726
- package/build-module/store/tests/reducer.test.js.map +0 -7
- package/build-module/store/tests/resolvers.test.js +0 -523
- package/build-module/store/tests/resolvers.test.js.map +0 -7
- package/build-module/store/tests/selectors.test.js +0 -352
- package/build-module/store/tests/selectors.test.js.map +0 -7
- package/build-module/tests/api.test.js +0 -530
- package/build-module/tests/api.test.js.map +0 -7
- package/build-module/tests/validation.test.js +0 -451
- package/build-module/tests/validation.test.js.map +0 -7
|
@@ -1,804 +0,0 @@
|
|
|
1
|
-
// packages/abilities/src/store/tests/actions.test.ts
|
|
2
|
-
import { resolveSelect } from "@wordpress/data";
|
|
3
|
-
import {
|
|
4
|
-
receiveAbilities,
|
|
5
|
-
registerAbility,
|
|
6
|
-
unregisterAbility,
|
|
7
|
-
receiveCategories,
|
|
8
|
-
registerAbilityCategory,
|
|
9
|
-
unregisterAbilityCategory
|
|
10
|
-
} from "../actions";
|
|
11
|
-
import {
|
|
12
|
-
RECEIVE_ABILITIES,
|
|
13
|
-
REGISTER_ABILITY,
|
|
14
|
-
UNREGISTER_ABILITY,
|
|
15
|
-
RECEIVE_CATEGORIES,
|
|
16
|
-
REGISTER_ABILITY_CATEGORY,
|
|
17
|
-
UNREGISTER_ABILITY_CATEGORY
|
|
18
|
-
} from "../constants";
|
|
19
|
-
jest.mock("@wordpress/data", () => ({
|
|
20
|
-
resolveSelect: jest.fn()
|
|
21
|
-
}));
|
|
22
|
-
describe("Store Actions", () => {
|
|
23
|
-
describe("receiveAbilities", () => {
|
|
24
|
-
it("should create an action to receive abilities", () => {
|
|
25
|
-
const abilities = [
|
|
26
|
-
{
|
|
27
|
-
name: "test/ability1",
|
|
28
|
-
label: "Test Ability 1",
|
|
29
|
-
description: "First test ability",
|
|
30
|
-
category: "test-category",
|
|
31
|
-
input_schema: { type: "object" },
|
|
32
|
-
output_schema: { type: "object" }
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: "test/ability2",
|
|
36
|
-
label: "Test Ability 2",
|
|
37
|
-
description: "Second test ability",
|
|
38
|
-
category: "test-category",
|
|
39
|
-
input_schema: { type: "object" },
|
|
40
|
-
output_schema: { type: "object" }
|
|
41
|
-
}
|
|
42
|
-
];
|
|
43
|
-
const action = receiveAbilities(abilities);
|
|
44
|
-
expect(action).toEqual({
|
|
45
|
-
type: RECEIVE_ABILITIES,
|
|
46
|
-
abilities
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
it("should handle empty abilities array", () => {
|
|
50
|
-
const abilities = [];
|
|
51
|
-
const action = receiveAbilities(abilities);
|
|
52
|
-
expect(action).toEqual({
|
|
53
|
-
type: RECEIVE_ABILITIES,
|
|
54
|
-
abilities: []
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
describe("registerAbility", () => {
|
|
59
|
-
let mockSelect;
|
|
60
|
-
let mockDispatch;
|
|
61
|
-
beforeEach(() => {
|
|
62
|
-
jest.clearAllMocks();
|
|
63
|
-
const defaultCategories = [
|
|
64
|
-
{
|
|
65
|
-
slug: "test-category",
|
|
66
|
-
label: "Test Category",
|
|
67
|
-
description: "Test category for testing"
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
slug: "data-retrieval",
|
|
71
|
-
label: "Data Retrieval",
|
|
72
|
-
description: "Abilities that retrieve data"
|
|
73
|
-
}
|
|
74
|
-
];
|
|
75
|
-
mockSelect = {
|
|
76
|
-
getAbility: jest.fn().mockReturnValue(null),
|
|
77
|
-
getAbilityCategories: jest.fn().mockReturnValue(defaultCategories),
|
|
78
|
-
getAbilityCategory: jest.fn().mockImplementation((slug) => {
|
|
79
|
-
const categories = {
|
|
80
|
-
"test-category": {
|
|
81
|
-
slug: "test-category",
|
|
82
|
-
label: "Test Category",
|
|
83
|
-
description: "Test category for testing"
|
|
84
|
-
},
|
|
85
|
-
"data-retrieval": {
|
|
86
|
-
slug: "data-retrieval",
|
|
87
|
-
label: "Data Retrieval",
|
|
88
|
-
description: "Abilities that retrieve data"
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
return categories[slug] || null;
|
|
92
|
-
})
|
|
93
|
-
};
|
|
94
|
-
mockDispatch = jest.fn();
|
|
95
|
-
resolveSelect.mockReturnValue({
|
|
96
|
-
getAbilityCategories: jest.fn().mockResolvedValue(defaultCategories)
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
it("should register a valid client ability", async () => {
|
|
100
|
-
const ability = {
|
|
101
|
-
name: "test/ability",
|
|
102
|
-
label: "Test Ability",
|
|
103
|
-
description: "Test ability description",
|
|
104
|
-
category: "test-category",
|
|
105
|
-
input_schema: {
|
|
106
|
-
type: "object",
|
|
107
|
-
properties: {
|
|
108
|
-
message: { type: "string" }
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
output_schema: {
|
|
112
|
-
type: "object",
|
|
113
|
-
properties: {
|
|
114
|
-
success: { type: "boolean" }
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
callback: jest.fn()
|
|
118
|
-
};
|
|
119
|
-
const action = registerAbility(ability);
|
|
120
|
-
await action({ select: mockSelect, dispatch: mockDispatch });
|
|
121
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
122
|
-
type: REGISTER_ABILITY,
|
|
123
|
-
ability
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
it("should register server-side abilities", async () => {
|
|
127
|
-
const ability = {
|
|
128
|
-
name: "test/server-ability",
|
|
129
|
-
label: "Server Ability",
|
|
130
|
-
description: "Server-side ability",
|
|
131
|
-
category: "test-category",
|
|
132
|
-
input_schema: { type: "object" },
|
|
133
|
-
output_schema: { type: "object" }
|
|
134
|
-
};
|
|
135
|
-
const action = registerAbility(ability);
|
|
136
|
-
await action({ select: mockSelect, dispatch: mockDispatch });
|
|
137
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
138
|
-
type: REGISTER_ABILITY,
|
|
139
|
-
ability
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
it("should validate and reject ability without name", async () => {
|
|
143
|
-
const ability = {
|
|
144
|
-
name: "",
|
|
145
|
-
label: "Test Ability",
|
|
146
|
-
description: "Test description",
|
|
147
|
-
category: "test-category",
|
|
148
|
-
callback: jest.fn()
|
|
149
|
-
};
|
|
150
|
-
const action = registerAbility(ability);
|
|
151
|
-
await expect(
|
|
152
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
153
|
-
).rejects.toThrow("Ability name is required");
|
|
154
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
155
|
-
});
|
|
156
|
-
it("should validate and reject ability with invalid name format", async () => {
|
|
157
|
-
const testCases = [
|
|
158
|
-
"invalid",
|
|
159
|
-
// No namespace
|
|
160
|
-
"my-plugin/feature/action",
|
|
161
|
-
// Multiple slashes
|
|
162
|
-
"My-Plugin/feature",
|
|
163
|
-
// Uppercase letters
|
|
164
|
-
"my_plugin/feature",
|
|
165
|
-
// Underscores not allowed
|
|
166
|
-
"my-plugin/feature!",
|
|
167
|
-
// Special characters not allowed
|
|
168
|
-
"my plugin/feature"
|
|
169
|
-
// Spaces not allowed
|
|
170
|
-
];
|
|
171
|
-
for (const invalidName of testCases) {
|
|
172
|
-
const ability = {
|
|
173
|
-
name: invalidName,
|
|
174
|
-
label: "Test Ability",
|
|
175
|
-
description: "Test description",
|
|
176
|
-
category: "test-category",
|
|
177
|
-
callback: jest.fn()
|
|
178
|
-
};
|
|
179
|
-
const action = registerAbility(ability);
|
|
180
|
-
await expect(
|
|
181
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
182
|
-
).rejects.toThrow(
|
|
183
|
-
"Ability name must be a string containing a namespace prefix"
|
|
184
|
-
);
|
|
185
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
186
|
-
mockDispatch.mockClear();
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
it("should validate and reject ability without label", async () => {
|
|
190
|
-
const ability = {
|
|
191
|
-
name: "test/ability",
|
|
192
|
-
label: "",
|
|
193
|
-
description: "Test description",
|
|
194
|
-
category: "test-category",
|
|
195
|
-
callback: jest.fn()
|
|
196
|
-
};
|
|
197
|
-
const action = registerAbility(ability);
|
|
198
|
-
await expect(
|
|
199
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
200
|
-
).rejects.toThrow('Ability "test/ability" must have a label');
|
|
201
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
202
|
-
});
|
|
203
|
-
it("should validate and reject ability without description", async () => {
|
|
204
|
-
const ability = {
|
|
205
|
-
name: "test/ability",
|
|
206
|
-
label: "Test Ability",
|
|
207
|
-
description: "",
|
|
208
|
-
category: "test-category",
|
|
209
|
-
callback: jest.fn()
|
|
210
|
-
};
|
|
211
|
-
const action = registerAbility(ability);
|
|
212
|
-
await expect(
|
|
213
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
214
|
-
).rejects.toThrow(
|
|
215
|
-
'Ability "test/ability" must have a description'
|
|
216
|
-
);
|
|
217
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
218
|
-
});
|
|
219
|
-
it("should validate and reject ability without category", async () => {
|
|
220
|
-
const ability = {
|
|
221
|
-
name: "test/ability",
|
|
222
|
-
label: "Test Ability",
|
|
223
|
-
description: "Test description",
|
|
224
|
-
category: "",
|
|
225
|
-
callback: jest.fn()
|
|
226
|
-
};
|
|
227
|
-
const action = registerAbility(ability);
|
|
228
|
-
await expect(
|
|
229
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
230
|
-
).rejects.toThrow('Ability "test/ability" must have a category');
|
|
231
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
232
|
-
});
|
|
233
|
-
it("should validate and reject ability with invalid category format", async () => {
|
|
234
|
-
const testCases = [
|
|
235
|
-
"Data-Retrieval",
|
|
236
|
-
// Uppercase letters
|
|
237
|
-
"data_retrieval",
|
|
238
|
-
// Underscores not allowed
|
|
239
|
-
"data.retrieval",
|
|
240
|
-
// Dots not allowed
|
|
241
|
-
"data/retrieval",
|
|
242
|
-
// Slashes not allowed
|
|
243
|
-
"-data-retrieval",
|
|
244
|
-
// Leading dash
|
|
245
|
-
"data-retrieval-",
|
|
246
|
-
// Trailing dash
|
|
247
|
-
"data--retrieval"
|
|
248
|
-
// Double dash
|
|
249
|
-
];
|
|
250
|
-
for (const invalidCategory of testCases) {
|
|
251
|
-
const ability = {
|
|
252
|
-
name: "test/ability",
|
|
253
|
-
label: "Test Ability",
|
|
254
|
-
description: "Test description",
|
|
255
|
-
category: invalidCategory,
|
|
256
|
-
callback: jest.fn()
|
|
257
|
-
};
|
|
258
|
-
const action = registerAbility(ability);
|
|
259
|
-
await expect(
|
|
260
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
261
|
-
).rejects.toThrow(
|
|
262
|
-
'Ability "test/ability" has an invalid category. Category must be lowercase alphanumeric with dashes only'
|
|
263
|
-
);
|
|
264
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
265
|
-
mockDispatch.mockClear();
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
it("should accept ability with valid category format", async () => {
|
|
269
|
-
const validCategories = [
|
|
270
|
-
"data-retrieval",
|
|
271
|
-
"user-management",
|
|
272
|
-
"analytics-123",
|
|
273
|
-
"ecommerce"
|
|
274
|
-
];
|
|
275
|
-
for (const validCategory of validCategories) {
|
|
276
|
-
const ability = {
|
|
277
|
-
name: "test/ability-" + validCategory,
|
|
278
|
-
label: "Test Ability",
|
|
279
|
-
description: "Test description",
|
|
280
|
-
category: validCategory,
|
|
281
|
-
callback: jest.fn()
|
|
282
|
-
};
|
|
283
|
-
const categoriesForTest = [
|
|
284
|
-
{
|
|
285
|
-
slug: "test-category",
|
|
286
|
-
label: "Test Category",
|
|
287
|
-
description: "Test category for testing"
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
slug: "data-retrieval",
|
|
291
|
-
label: "Data Retrieval",
|
|
292
|
-
description: "Abilities that retrieve data"
|
|
293
|
-
},
|
|
294
|
-
{
|
|
295
|
-
slug: validCategory,
|
|
296
|
-
label: "Test Category",
|
|
297
|
-
description: "Test"
|
|
298
|
-
}
|
|
299
|
-
];
|
|
300
|
-
mockSelect.getAbilityCategories.mockReturnValue(
|
|
301
|
-
categoriesForTest
|
|
302
|
-
);
|
|
303
|
-
resolveSelect.mockReturnValue({
|
|
304
|
-
getAbilityCategories: jest.fn().mockResolvedValue(categoriesForTest)
|
|
305
|
-
});
|
|
306
|
-
mockSelect.getAbility.mockReturnValue(null);
|
|
307
|
-
mockDispatch.mockClear();
|
|
308
|
-
const action = registerAbility(ability);
|
|
309
|
-
await action({ select: mockSelect, dispatch: mockDispatch });
|
|
310
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
311
|
-
type: REGISTER_ABILITY,
|
|
312
|
-
ability
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
it("should validate and reject ability with non-existent category", async () => {
|
|
317
|
-
mockSelect.getAbilityCategories.mockReturnValue([
|
|
318
|
-
{
|
|
319
|
-
slug: "test-category",
|
|
320
|
-
label: "Test Category",
|
|
321
|
-
description: "Test category for testing"
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
slug: "data-retrieval",
|
|
325
|
-
label: "Data Retrieval",
|
|
326
|
-
description: "Abilities that retrieve data"
|
|
327
|
-
}
|
|
328
|
-
]);
|
|
329
|
-
const ability = {
|
|
330
|
-
name: "test/ability",
|
|
331
|
-
label: "Test Ability",
|
|
332
|
-
description: "Test description",
|
|
333
|
-
category: "non-existent-category",
|
|
334
|
-
callback: jest.fn()
|
|
335
|
-
};
|
|
336
|
-
const action = registerAbility(ability);
|
|
337
|
-
await expect(
|
|
338
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
339
|
-
).rejects.toThrow(
|
|
340
|
-
'Ability "test/ability" references non-existent category "non-existent-category". Please register the category first.'
|
|
341
|
-
);
|
|
342
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
343
|
-
});
|
|
344
|
-
it("should accept ability with existing category", async () => {
|
|
345
|
-
const categoriesForTest = [
|
|
346
|
-
{
|
|
347
|
-
slug: "test-category",
|
|
348
|
-
label: "Test Category",
|
|
349
|
-
description: "Test category for testing"
|
|
350
|
-
},
|
|
351
|
-
{
|
|
352
|
-
slug: "data-retrieval",
|
|
353
|
-
label: "Data Retrieval",
|
|
354
|
-
description: "Abilities that retrieve data"
|
|
355
|
-
}
|
|
356
|
-
];
|
|
357
|
-
mockSelect.getAbilityCategories.mockReturnValue(
|
|
358
|
-
categoriesForTest
|
|
359
|
-
);
|
|
360
|
-
resolveSelect.mockReturnValue({
|
|
361
|
-
getAbilityCategories: jest.fn().mockResolvedValue(categoriesForTest)
|
|
362
|
-
});
|
|
363
|
-
const ability = {
|
|
364
|
-
name: "test/ability",
|
|
365
|
-
label: "Test Ability",
|
|
366
|
-
description: "Test description",
|
|
367
|
-
category: "data-retrieval",
|
|
368
|
-
callback: jest.fn()
|
|
369
|
-
};
|
|
370
|
-
const action = registerAbility(ability);
|
|
371
|
-
await action({ select: mockSelect, dispatch: mockDispatch });
|
|
372
|
-
expect(resolveSelect).toHaveBeenCalledWith("core/abilities");
|
|
373
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
374
|
-
type: REGISTER_ABILITY,
|
|
375
|
-
ability
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
it("should validate and reject ability with invalid callback", async () => {
|
|
379
|
-
const ability = {
|
|
380
|
-
name: "test/ability",
|
|
381
|
-
label: "Test Ability",
|
|
382
|
-
description: "Test description",
|
|
383
|
-
category: "test-category",
|
|
384
|
-
callback: "not a function"
|
|
385
|
-
};
|
|
386
|
-
const action = registerAbility(ability);
|
|
387
|
-
await expect(
|
|
388
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
389
|
-
).rejects.toThrow(
|
|
390
|
-
'Ability "test/ability" has an invalid callback. Callback must be a function'
|
|
391
|
-
);
|
|
392
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
393
|
-
});
|
|
394
|
-
it("should validate and reject already registered ability", async () => {
|
|
395
|
-
const existingAbility = {
|
|
396
|
-
name: "test/ability",
|
|
397
|
-
label: "Existing Ability",
|
|
398
|
-
description: "Already registered",
|
|
399
|
-
category: "test-category"
|
|
400
|
-
};
|
|
401
|
-
mockSelect.getAbility.mockReturnValue(existingAbility);
|
|
402
|
-
const ability = {
|
|
403
|
-
name: "test/ability",
|
|
404
|
-
label: "Test Ability",
|
|
405
|
-
description: "Test description",
|
|
406
|
-
category: "test-category",
|
|
407
|
-
callback: jest.fn()
|
|
408
|
-
};
|
|
409
|
-
const action = registerAbility(ability);
|
|
410
|
-
await expect(
|
|
411
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
412
|
-
).rejects.toThrow('Ability "test/ability" is already registered');
|
|
413
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
414
|
-
});
|
|
415
|
-
it("should load categories using resolveSelect before validation", async () => {
|
|
416
|
-
const categories = [
|
|
417
|
-
{
|
|
418
|
-
slug: "test-category",
|
|
419
|
-
label: "Test Category",
|
|
420
|
-
description: "Test category"
|
|
421
|
-
}
|
|
422
|
-
];
|
|
423
|
-
resolveSelect.mockReturnValue({
|
|
424
|
-
getAbilityCategories: jest.fn().mockResolvedValue(categories)
|
|
425
|
-
});
|
|
426
|
-
const ability = {
|
|
427
|
-
name: "test/ability",
|
|
428
|
-
label: "Test Ability",
|
|
429
|
-
description: "Test description",
|
|
430
|
-
category: "test-category",
|
|
431
|
-
callback: jest.fn()
|
|
432
|
-
};
|
|
433
|
-
const action = registerAbility(ability);
|
|
434
|
-
await action({ select: mockSelect, dispatch: mockDispatch });
|
|
435
|
-
expect(resolveSelect).toHaveBeenCalledWith("core/abilities");
|
|
436
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
437
|
-
type: REGISTER_ABILITY,
|
|
438
|
-
ability
|
|
439
|
-
});
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
describe("unregisterAbility", () => {
|
|
443
|
-
it("should create an action to unregister an ability", () => {
|
|
444
|
-
const abilityName = "test/ability";
|
|
445
|
-
const action = unregisterAbility(abilityName);
|
|
446
|
-
expect(action).toEqual({
|
|
447
|
-
type: UNREGISTER_ABILITY,
|
|
448
|
-
name: abilityName
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
it("should handle valid namespaced ability names", () => {
|
|
452
|
-
const abilityName = "my-plugin/feature-action";
|
|
453
|
-
const action = unregisterAbility(abilityName);
|
|
454
|
-
expect(action).toEqual({
|
|
455
|
-
type: UNREGISTER_ABILITY,
|
|
456
|
-
name: abilityName
|
|
457
|
-
});
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
describe("receiveCategories", () => {
|
|
461
|
-
it("should create an action to receive categories", () => {
|
|
462
|
-
const categories = [
|
|
463
|
-
{
|
|
464
|
-
slug: "data-retrieval",
|
|
465
|
-
label: "Data Retrieval",
|
|
466
|
-
description: "Abilities that retrieve data"
|
|
467
|
-
},
|
|
468
|
-
{
|
|
469
|
-
slug: "user-management",
|
|
470
|
-
label: "User Management",
|
|
471
|
-
description: "Abilities for managing users"
|
|
472
|
-
}
|
|
473
|
-
];
|
|
474
|
-
const action = receiveCategories(categories);
|
|
475
|
-
expect(action).toEqual({
|
|
476
|
-
type: RECEIVE_CATEGORIES,
|
|
477
|
-
categories
|
|
478
|
-
});
|
|
479
|
-
});
|
|
480
|
-
it("should handle empty categories array", () => {
|
|
481
|
-
const categories = [];
|
|
482
|
-
const action = receiveCategories(categories);
|
|
483
|
-
expect(action).toEqual({
|
|
484
|
-
type: RECEIVE_CATEGORIES,
|
|
485
|
-
categories: []
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
});
|
|
489
|
-
describe("registerAbilityCategory", () => {
|
|
490
|
-
let mockSelect;
|
|
491
|
-
let mockDispatch;
|
|
492
|
-
beforeEach(() => {
|
|
493
|
-
jest.clearAllMocks();
|
|
494
|
-
mockSelect = {
|
|
495
|
-
getAbilityCategory: jest.fn().mockReturnValue(null),
|
|
496
|
-
getAbilityCategories: jest.fn().mockReturnValue([])
|
|
497
|
-
};
|
|
498
|
-
mockDispatch = jest.fn();
|
|
499
|
-
resolveSelect.mockReturnValue({
|
|
500
|
-
getAbilityCategories: jest.fn().mockResolvedValue(void 0)
|
|
501
|
-
});
|
|
502
|
-
});
|
|
503
|
-
it("should register a valid category", async () => {
|
|
504
|
-
const slug = "test-category";
|
|
505
|
-
const args = {
|
|
506
|
-
label: "Test Category",
|
|
507
|
-
description: "A test category for testing"
|
|
508
|
-
};
|
|
509
|
-
const action = registerAbilityCategory(slug, args);
|
|
510
|
-
await action({ select: mockSelect, dispatch: mockDispatch });
|
|
511
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
512
|
-
type: REGISTER_ABILITY_CATEGORY,
|
|
513
|
-
category: {
|
|
514
|
-
slug,
|
|
515
|
-
label: args.label,
|
|
516
|
-
description: args.description,
|
|
517
|
-
meta: {
|
|
518
|
-
_clientRegistered: true
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
it("should register a category with meta", async () => {
|
|
524
|
-
const slug = "test-category";
|
|
525
|
-
const args = {
|
|
526
|
-
label: "Test Category",
|
|
527
|
-
description: "A test category",
|
|
528
|
-
meta: { foo: "bar", nested: { key: "value" } }
|
|
529
|
-
};
|
|
530
|
-
const action = registerAbilityCategory(slug, args);
|
|
531
|
-
await action({ select: mockSelect, dispatch: mockDispatch });
|
|
532
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
533
|
-
type: REGISTER_ABILITY_CATEGORY,
|
|
534
|
-
category: {
|
|
535
|
-
slug,
|
|
536
|
-
label: args.label,
|
|
537
|
-
description: args.description,
|
|
538
|
-
meta: {
|
|
539
|
-
...args.meta,
|
|
540
|
-
_clientRegistered: true
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
});
|
|
545
|
-
it("should validate and reject empty slug", async () => {
|
|
546
|
-
const args = {
|
|
547
|
-
label: "Test",
|
|
548
|
-
description: "Test"
|
|
549
|
-
};
|
|
550
|
-
const action = registerAbilityCategory("", args);
|
|
551
|
-
await expect(
|
|
552
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
553
|
-
).rejects.toThrow("Category slug is required");
|
|
554
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
555
|
-
});
|
|
556
|
-
it("should validate and reject invalid slug formats", async () => {
|
|
557
|
-
const testCases = [
|
|
558
|
-
"Data-Retrieval",
|
|
559
|
-
// Uppercase
|
|
560
|
-
"data_retrieval",
|
|
561
|
-
// Underscores
|
|
562
|
-
"data.retrieval",
|
|
563
|
-
// Dots
|
|
564
|
-
"data/retrieval",
|
|
565
|
-
// Slashes
|
|
566
|
-
"-data-retrieval",
|
|
567
|
-
// Leading dash
|
|
568
|
-
"data-retrieval-",
|
|
569
|
-
// Trailing dash
|
|
570
|
-
"data--retrieval",
|
|
571
|
-
// Double dash
|
|
572
|
-
"data retrieval",
|
|
573
|
-
// Spaces
|
|
574
|
-
"data!retrieval"
|
|
575
|
-
// Special characters
|
|
576
|
-
];
|
|
577
|
-
const args = {
|
|
578
|
-
label: "Test",
|
|
579
|
-
description: "Test"
|
|
580
|
-
};
|
|
581
|
-
for (const invalidSlug of testCases) {
|
|
582
|
-
const action = registerAbilityCategory(invalidSlug, args);
|
|
583
|
-
await expect(
|
|
584
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
585
|
-
).rejects.toThrow(
|
|
586
|
-
"Category slug must contain only lowercase alphanumeric characters and dashes"
|
|
587
|
-
);
|
|
588
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
589
|
-
mockDispatch.mockClear();
|
|
590
|
-
}
|
|
591
|
-
});
|
|
592
|
-
it("should accept valid slug formats", async () => {
|
|
593
|
-
const validSlugs = [
|
|
594
|
-
"data-retrieval",
|
|
595
|
-
"user-management",
|
|
596
|
-
"analytics-123",
|
|
597
|
-
"ecommerce",
|
|
598
|
-
"a",
|
|
599
|
-
"123",
|
|
600
|
-
"test-multiple-words-with-dashes"
|
|
601
|
-
];
|
|
602
|
-
const args = {
|
|
603
|
-
label: "Test Category",
|
|
604
|
-
description: "Test description"
|
|
605
|
-
};
|
|
606
|
-
for (const validSlug of validSlugs) {
|
|
607
|
-
mockSelect.getAbilityCategory.mockReturnValue(null);
|
|
608
|
-
mockDispatch.mockClear();
|
|
609
|
-
const action = registerAbilityCategory(validSlug, args);
|
|
610
|
-
await action({ select: mockSelect, dispatch: mockDispatch });
|
|
611
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
612
|
-
type: REGISTER_ABILITY_CATEGORY,
|
|
613
|
-
category: {
|
|
614
|
-
slug: validSlug,
|
|
615
|
-
label: args.label,
|
|
616
|
-
description: args.description,
|
|
617
|
-
meta: {
|
|
618
|
-
_clientRegistered: true
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
it("should validate and reject missing label", async () => {
|
|
625
|
-
const slug = "test-category";
|
|
626
|
-
const args = {
|
|
627
|
-
label: "",
|
|
628
|
-
description: "Test"
|
|
629
|
-
};
|
|
630
|
-
const action = registerAbilityCategory(slug, args);
|
|
631
|
-
await expect(
|
|
632
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
633
|
-
).rejects.toThrow(
|
|
634
|
-
"The category properties must contain a `label` string."
|
|
635
|
-
);
|
|
636
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
637
|
-
});
|
|
638
|
-
it("should validate and reject non-string label", async () => {
|
|
639
|
-
const slug = "test-category";
|
|
640
|
-
const args = {
|
|
641
|
-
label: 123,
|
|
642
|
-
description: "Test"
|
|
643
|
-
};
|
|
644
|
-
const action = registerAbilityCategory(slug, args);
|
|
645
|
-
await expect(
|
|
646
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
647
|
-
).rejects.toThrow(
|
|
648
|
-
"The category properties must contain a `label` string."
|
|
649
|
-
);
|
|
650
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
651
|
-
});
|
|
652
|
-
it("should validate and reject missing description", async () => {
|
|
653
|
-
const slug = "test-category";
|
|
654
|
-
const args = {
|
|
655
|
-
label: "Test",
|
|
656
|
-
description: ""
|
|
657
|
-
};
|
|
658
|
-
const action = registerAbilityCategory(slug, args);
|
|
659
|
-
await expect(
|
|
660
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
661
|
-
).rejects.toThrow(
|
|
662
|
-
"The category properties must contain a `description` string."
|
|
663
|
-
);
|
|
664
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
665
|
-
});
|
|
666
|
-
it("should validate and reject non-string description", async () => {
|
|
667
|
-
const slug = "test-category";
|
|
668
|
-
const args = {
|
|
669
|
-
label: "Test",
|
|
670
|
-
description: 123
|
|
671
|
-
};
|
|
672
|
-
const action = registerAbilityCategory(slug, args);
|
|
673
|
-
await expect(
|
|
674
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
675
|
-
).rejects.toThrow(
|
|
676
|
-
"The category properties must contain a `description` string."
|
|
677
|
-
);
|
|
678
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
679
|
-
});
|
|
680
|
-
it("should validate and reject non-object meta", async () => {
|
|
681
|
-
const slug = "test-category";
|
|
682
|
-
const args = {
|
|
683
|
-
label: "Test",
|
|
684
|
-
description: "Test",
|
|
685
|
-
meta: "invalid"
|
|
686
|
-
};
|
|
687
|
-
const action = registerAbilityCategory(slug, args);
|
|
688
|
-
await expect(
|
|
689
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
690
|
-
).rejects.toThrow(
|
|
691
|
-
"The category properties should provide a valid `meta` object."
|
|
692
|
-
);
|
|
693
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
694
|
-
});
|
|
695
|
-
it("should validate and reject array as meta", async () => {
|
|
696
|
-
const slug = "test-category";
|
|
697
|
-
const args = {
|
|
698
|
-
label: "Test",
|
|
699
|
-
description: "Test",
|
|
700
|
-
meta: ["invalid"]
|
|
701
|
-
};
|
|
702
|
-
const action = registerAbilityCategory(slug, args);
|
|
703
|
-
await expect(
|
|
704
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
705
|
-
).rejects.toThrow(
|
|
706
|
-
"The category properties should provide a valid `meta` object."
|
|
707
|
-
);
|
|
708
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
709
|
-
});
|
|
710
|
-
it("should validate and reject already registered category", async () => {
|
|
711
|
-
const existingCategory = {
|
|
712
|
-
slug: "test-category",
|
|
713
|
-
label: "Existing Category",
|
|
714
|
-
description: "Already registered"
|
|
715
|
-
};
|
|
716
|
-
mockSelect.getAbilityCategory.mockReturnValue(existingCategory);
|
|
717
|
-
const args = {
|
|
718
|
-
label: "Test",
|
|
719
|
-
description: "Test"
|
|
720
|
-
};
|
|
721
|
-
const action = registerAbilityCategory("test-category", args);
|
|
722
|
-
await expect(
|
|
723
|
-
action({ select: mockSelect, dispatch: mockDispatch })
|
|
724
|
-
).rejects.toThrow(
|
|
725
|
-
'Category "test-category" is already registered.'
|
|
726
|
-
);
|
|
727
|
-
expect(mockDispatch).not.toHaveBeenCalled();
|
|
728
|
-
});
|
|
729
|
-
it("should allow registering ability after registering category", async () => {
|
|
730
|
-
const categorySlug = "new-category";
|
|
731
|
-
const categoryArgs = {
|
|
732
|
-
label: "New Category",
|
|
733
|
-
description: "A newly registered category"
|
|
734
|
-
};
|
|
735
|
-
const categoryAction = registerAbilityCategory(
|
|
736
|
-
categorySlug,
|
|
737
|
-
categoryArgs
|
|
738
|
-
);
|
|
739
|
-
await categoryAction({
|
|
740
|
-
select: mockSelect,
|
|
741
|
-
dispatch: mockDispatch
|
|
742
|
-
});
|
|
743
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
744
|
-
type: REGISTER_ABILITY_CATEGORY,
|
|
745
|
-
category: {
|
|
746
|
-
slug: categorySlug,
|
|
747
|
-
label: categoryArgs.label,
|
|
748
|
-
description: categoryArgs.description,
|
|
749
|
-
meta: {
|
|
750
|
-
_clientRegistered: true
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
});
|
|
754
|
-
const categoriesWithNew = [
|
|
755
|
-
{
|
|
756
|
-
slug: categorySlug,
|
|
757
|
-
label: categoryArgs.label,
|
|
758
|
-
description: categoryArgs.description
|
|
759
|
-
}
|
|
760
|
-
];
|
|
761
|
-
mockSelect.getAbilityCategories = jest.fn().mockReturnValue(categoriesWithNew);
|
|
762
|
-
resolveSelect.mockReturnValue({
|
|
763
|
-
getAbilityCategories: jest.fn().mockResolvedValue(categoriesWithNew)
|
|
764
|
-
});
|
|
765
|
-
mockSelect.getAbility = jest.fn().mockReturnValue(null);
|
|
766
|
-
mockDispatch.mockClear();
|
|
767
|
-
const ability = {
|
|
768
|
-
name: "test/ability",
|
|
769
|
-
label: "Test Ability",
|
|
770
|
-
description: "Test description",
|
|
771
|
-
category: categorySlug,
|
|
772
|
-
callback: jest.fn()
|
|
773
|
-
};
|
|
774
|
-
const abilityAction = registerAbility(ability);
|
|
775
|
-
await abilityAction({
|
|
776
|
-
select: mockSelect,
|
|
777
|
-
dispatch: mockDispatch
|
|
778
|
-
});
|
|
779
|
-
expect(mockDispatch).toHaveBeenCalledWith({
|
|
780
|
-
type: REGISTER_ABILITY,
|
|
781
|
-
ability
|
|
782
|
-
});
|
|
783
|
-
});
|
|
784
|
-
});
|
|
785
|
-
describe("unregisterAbilityCategory", () => {
|
|
786
|
-
it("should create an action to unregister a category", () => {
|
|
787
|
-
const slug = "test-category";
|
|
788
|
-
const action = unregisterAbilityCategory(slug);
|
|
789
|
-
expect(action).toEqual({
|
|
790
|
-
type: UNREGISTER_ABILITY_CATEGORY,
|
|
791
|
-
slug
|
|
792
|
-
});
|
|
793
|
-
});
|
|
794
|
-
it("should handle valid category slugs", () => {
|
|
795
|
-
const slug = "data-retrieval";
|
|
796
|
-
const action = unregisterAbilityCategory(slug);
|
|
797
|
-
expect(action).toEqual({
|
|
798
|
-
type: UNREGISTER_ABILITY_CATEGORY,
|
|
799
|
-
slug
|
|
800
|
-
});
|
|
801
|
-
});
|
|
802
|
-
});
|
|
803
|
-
});
|
|
804
|
-
//# sourceMappingURL=actions.test.js.map
|