@c-a-f/testing 1.0.1

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 (70) hide show
  1. package/.build/__tests__/react/renderWithCAF.spec.d.ts +1 -0
  2. package/.build/__tests__/react/renderWithCAF.spec.js +53 -0
  3. package/.build/index.d.ts +1 -0
  4. package/.build/index.js +1 -0
  5. package/.build/src/angular/createTestPloc.d.ts +19 -0
  6. package/.build/src/angular/createTestPloc.js +21 -0
  7. package/.build/src/angular/index.d.ts +12 -0
  8. package/.build/src/angular/index.js +12 -0
  9. package/.build/src/angular/mockUseCase.d.ts +32 -0
  10. package/.build/src/angular/mockUseCase.js +40 -0
  11. package/.build/src/angular/provideTestingCAF.d.ts +29 -0
  12. package/.build/src/angular/provideTestingCAF.js +26 -0
  13. package/.build/src/angular/waitForPlocState.d.ts +18 -0
  14. package/.build/src/angular/waitForPlocState.js +20 -0
  15. package/.build/src/core/IntegrationTestHelpers.d.ts +77 -0
  16. package/.build/src/core/IntegrationTestHelpers.js +78 -0
  17. package/.build/src/core/PlocTestHelpers.d.ts +133 -0
  18. package/.build/src/core/PlocTestHelpers.js +205 -0
  19. package/.build/src/core/PulseTestHelpers.d.ts +71 -0
  20. package/.build/src/core/PulseTestHelpers.js +106 -0
  21. package/.build/src/core/RepositoryTestHelpers.d.ts +48 -0
  22. package/.build/src/core/RepositoryTestHelpers.js +76 -0
  23. package/.build/src/core/RouteTestHelpers.d.ts +67 -0
  24. package/.build/src/core/RouteTestHelpers.js +94 -0
  25. package/.build/src/core/UseCaseTestHelpers.d.ts +100 -0
  26. package/.build/src/core/UseCaseTestHelpers.js +161 -0
  27. package/.build/src/core/index.d.ts +6 -0
  28. package/.build/src/core/index.js +6 -0
  29. package/.build/src/i18n/I18nTestHelpers.d.ts +76 -0
  30. package/.build/src/i18n/I18nTestHelpers.js +122 -0
  31. package/.build/src/i18n/index.d.ts +1 -0
  32. package/.build/src/i18n/index.js +1 -0
  33. package/.build/src/index.d.ts +5 -0
  34. package/.build/src/index.js +10 -0
  35. package/.build/src/permission/PermissionTestHelpers.d.ts +75 -0
  36. package/.build/src/permission/PermissionTestHelpers.js +121 -0
  37. package/.build/src/permission/index.d.ts +1 -0
  38. package/.build/src/permission/index.js +1 -0
  39. package/.build/src/react/createTestPloc.d.ts +19 -0
  40. package/.build/src/react/createTestPloc.js +21 -0
  41. package/.build/src/react/index.d.ts +12 -0
  42. package/.build/src/react/index.js +12 -0
  43. package/.build/src/react/mockUseCase.d.ts +36 -0
  44. package/.build/src/react/mockUseCase.js +44 -0
  45. package/.build/src/react/renderWithCAF.d.ts +31 -0
  46. package/.build/src/react/renderWithCAF.js +23 -0
  47. package/.build/src/react/waitForPlocState.d.ts +22 -0
  48. package/.build/src/react/waitForPlocState.js +24 -0
  49. package/.build/src/validation/ValidationTestHelpers.d.ts +66 -0
  50. package/.build/src/validation/ValidationTestHelpers.js +118 -0
  51. package/.build/src/validation/index.d.ts +1 -0
  52. package/.build/src/validation/index.js +1 -0
  53. package/.build/src/vue/createTestPloc.d.ts +19 -0
  54. package/.build/src/vue/createTestPloc.js +21 -0
  55. package/.build/src/vue/index.d.ts +12 -0
  56. package/.build/src/vue/index.js +12 -0
  57. package/.build/src/vue/mockUseCase.d.ts +34 -0
  58. package/.build/src/vue/mockUseCase.js +42 -0
  59. package/.build/src/vue/mountWithCAF.d.ts +31 -0
  60. package/.build/src/vue/mountWithCAF.js +40 -0
  61. package/.build/src/vue/waitForPlocState.d.ts +19 -0
  62. package/.build/src/vue/waitForPlocState.js +21 -0
  63. package/.build/src/workflow/WorkflowTestHelpers.d.ts +75 -0
  64. package/.build/src/workflow/WorkflowTestHelpers.js +146 -0
  65. package/.build/src/workflow/index.d.ts +1 -0
  66. package/.build/src/workflow/index.js +1 -0
  67. package/.build/vitest.config.d.ts +7 -0
  68. package/.build/vitest.config.js +6 -0
  69. package/README.md +598 -0
  70. package/package.json +127 -0
package/README.md ADDED
@@ -0,0 +1,598 @@
1
+ # @c-a-f/testing
2
+
3
+ Testing utilities and helpers for CAF applications. Provides mocks, test helpers, and utilities for testing UseCase, Ploc, Pulse, Workflow, Permission, I18n, and Validation.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @c-a-f/testing --save-dev
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Core Testing Utilities
14
+
15
+ #### Testing Ploc
16
+
17
+ ```typescript
18
+ import { createPlocTester, waitForStateChange } from '@c-a-f/testing/core';
19
+ import { Ploc } from '@c-a-f/core';
20
+
21
+ class CounterPloc extends Ploc<number> {
22
+ constructor() {
23
+ super(0);
24
+ }
25
+ increment() {
26
+ this.changeState(this.state + 1);
27
+ }
28
+ }
29
+
30
+ describe('CounterPloc', () => {
31
+ it('tracks state changes', () => {
32
+ const ploc = new CounterPloc();
33
+ const tester = createPlocTester(ploc);
34
+
35
+ ploc.increment();
36
+ ploc.increment();
37
+
38
+ expect(tester.getStateChangeCount()).toBe(2);
39
+ expect(tester.getStateHistory()).toEqual([0, 1, 2]);
40
+ expect(tester.getLastStateChange()).toBe(2);
41
+
42
+ tester.cleanup();
43
+ });
44
+
45
+ it('waits for state change', async () => {
46
+ const ploc = new CounterPloc();
47
+
48
+ setTimeout(() => ploc.increment(), 100);
49
+ const finalState = await waitForStateChange(ploc, (state) => state === 1);
50
+
51
+ expect(finalState).toBe(1);
52
+ });
53
+ });
54
+ ```
55
+
56
+ #### Testing Pulse
57
+
58
+ ```typescript
59
+ import { createPulseTester, waitForPulseValue } from '@c-a-f/testing/core';
60
+ import { pulse } from '@c-a-f/core';
61
+
62
+ describe('Pulse', () => {
63
+ it('tracks value changes', () => {
64
+ const count = pulse(0);
65
+ const tester = createPulseTester(count);
66
+
67
+ count.value = 5;
68
+ count.value = 10;
69
+
70
+ expect(tester.getValueChangeCount()).toBe(2);
71
+ expect(tester.getValueHistory()).toEqual([0, 5, 10]);
72
+ expect(tester.getLastValueChange()).toBe(10);
73
+
74
+ tester.cleanup();
75
+ });
76
+
77
+ it('waits for pulse value', async () => {
78
+ const count = pulse(0);
79
+
80
+ setTimeout(() => { count.value = 5; }, 100);
81
+ const finalValue = await waitForPulseValue(count, (value) => value === 5);
82
+
83
+ expect(finalValue).toBe(5);
84
+ });
85
+ });
86
+ ```
87
+
88
+ #### Testing UseCase
89
+
90
+ ```typescript
91
+ import {
92
+ createMockUseCase,
93
+ createMockUseCaseSuccess,
94
+ createMockUseCaseError,
95
+ createUseCaseTester,
96
+ createSuccessResult,
97
+ } from '@c-a-f/testing/core';
98
+ import { UseCase } from '@c-a-f/core';
99
+
100
+ describe('UseCase', () => {
101
+ it('creates and tests mock use case', async () => {
102
+ const mockUseCase = createMockUseCase<[], string>(() =>
103
+ createSuccessResult('result')
104
+ );
105
+ const tester = createUseCaseTester(mockUseCase);
106
+
107
+ const result = await tester.execute([]);
108
+ expect(result.data.value).toBe('result');
109
+ expect(result.error.value).toBeNull();
110
+ });
111
+
112
+ it('uses success shortcut', async () => {
113
+ const useCase = createMockUseCaseSuccess([{ id: '1', name: 'John' }]);
114
+ const result = await useCase.execute();
115
+ expect(result.data.value).toEqual([{ id: '1', name: 'John' }]);
116
+ });
117
+
118
+ it('uses error shortcut', async () => {
119
+ const useCase = createMockUseCaseError(new Error('Failed'));
120
+ const result = await useCase.execute();
121
+ expect(result.error.value?.message).toBe('Failed');
122
+ });
123
+
124
+ it('executes and gets data', async () => {
125
+ const mockUseCase = createMockUseCase<[number], number>((count) =>
126
+ createSuccessResult(count * 2)
127
+ );
128
+ const tester = createUseCaseTester(mockUseCase);
129
+
130
+ const data = await tester.executeAndGetData([5]);
131
+ expect(data).toBe(10);
132
+ });
133
+ });
134
+ ```
135
+
136
+ #### Mock Ploc and state history (snapshot)
137
+
138
+ ```typescript
139
+ import {
140
+ createMockPloc,
141
+ createPlocTester,
142
+ assertStateHistory,
143
+ getStateHistorySnapshot,
144
+ } from '@c-a-f/testing/core';
145
+
146
+ it('mock Ploc and state history', () => {
147
+ const ploc = createMockPloc({ count: 0 });
148
+ const tester = createPlocTester(ploc);
149
+
150
+ ploc.changeState({ count: 1 });
151
+ ploc.changeState({ count: 2 });
152
+
153
+ assertStateHistory(tester, [{ count: 0 }, { count: 1 }, { count: 2 }]);
154
+ expect(getStateHistorySnapshot(tester)).toMatchSnapshot();
155
+ tester.cleanup();
156
+ });
157
+ ```
158
+
159
+ #### Mock Repository (domain I*Repository)
160
+
161
+ ```typescript
162
+ import { createMockRepository, createMockRepositoryStub } from '@c-a-f/testing/core';
163
+ import type { IUserRepository } from '../domain';
164
+
165
+ it('mocks repository', async () => {
166
+ const repo = createMockRepository<IUserRepository>({
167
+ getUsers: async () => [{ id: '1', name: 'John' }],
168
+ getUserById: async (id) => ({ id, name: 'User ' + id }),
169
+ });
170
+
171
+ expect(await repo.getUsers()).toHaveLength(1);
172
+ expect((await repo.getUserById('2'))?.name).toBe('User 2');
173
+ });
174
+
175
+ it('stub and spy', async () => {
176
+ const stub = createMockRepositoryStub<IUserRepository>();
177
+ stub.getUsers = vi.fn().mockResolvedValue([]);
178
+ await stub.getUsers();
179
+ expect(stub.getUsers).toHaveBeenCalledTimes(1);
180
+ });
181
+ ```
182
+
183
+ #### Integration test helpers
184
+
185
+ ```typescript
186
+ import {
187
+ createPlocUseCaseContext,
188
+ flushPromises,
189
+ } from '@c-a-f/testing/core';
190
+
191
+ it('integration context', async () => {
192
+ const { ploc, useCase } = createPlocUseCaseContext(
193
+ { items: [], loading: false },
194
+ [{ id: '1', name: 'Item' }]
195
+ );
196
+ // Use with CAFProvider or pass as props
197
+ expect(ploc.state.loading).toBe(false);
198
+ const result = await useCase.execute();
199
+ expect(result.data.value).toHaveLength(1);
200
+ await flushPromises();
201
+ });
202
+ ```
203
+
204
+ ### React Testing Utilities (`@c-a-f/testing/react`)
205
+
206
+ Use these when testing React components that use `usePlocFromContext`, `useUseCaseFromContext`, or `usePloc` / `useUseCase` with context-provided instances.
207
+
208
+ **Requirements:** `react` and `@testing-library/react` (peer dependencies). Install in your app:
209
+
210
+ ```bash
211
+ npm install react @testing-library/react
212
+ ```
213
+
214
+ #### renderWithCAF
215
+
216
+ Render a component wrapped in `CAFProvider` so Ploc/UseCase context is available:
217
+
218
+ ```tsx
219
+ import React from 'react';
220
+ import { screen } from '@testing-library/react';
221
+ import { renderWithCAF, createTestPloc, mockUseCase } from '@c-a-f/testing/react';
222
+ import { usePlocFromContext, usePloc } from '@c-a-f/infrastructure-react';
223
+
224
+ const Counter = () => {
225
+ const ploc = usePlocFromContext('counter');
226
+ if (!ploc) return null;
227
+ const [state] = usePloc(ploc);
228
+ return <span data-testid="count">{state.count}</span>;
229
+ };
230
+
231
+ it('renders with CAF context', () => {
232
+ const ploc = createTestPloc({ count: 5 });
233
+ renderWithCAF(<Counter />, { plocs: { counter: ploc } });
234
+ expect(screen.getByTestId('count')).toHaveTextContent('5');
235
+ });
236
+ ```
237
+
238
+ #### createTestPloc
239
+
240
+ Create a Ploc with controllable state (no business logic). Same idea as `createMockPloc` from core, for use in React tests:
241
+
242
+ ```tsx
243
+ const ploc = createTestPloc({ count: 0 });
244
+ renderWithCAF(<Counter />, { plocs: { counter: ploc } });
245
+ ploc.changeState({ count: 1 });
246
+ expect(screen.getByTestId('count')).toHaveTextContent('1');
247
+ ```
248
+
249
+ #### waitForPlocState
250
+
251
+ Wait for a Ploc to reach a state matching a predicate (e.g. after an async update):
252
+
253
+ ```tsx
254
+ const ploc = createTestPloc({ loading: true, items: [] });
255
+ renderWithCAF(<List />, { plocs: { list: ploc } });
256
+ ploc.changeState({ loading: false, items: [{ id: '1' }] });
257
+ await waitForPlocState(ploc, (state) => !state.loading && state.items.length > 0);
258
+ expect(screen.getByText('1')).toBeInTheDocument();
259
+ ```
260
+
261
+ #### mockUseCase
262
+
263
+ Create mock UseCases for context:
264
+
265
+ ```tsx
266
+ const submit = mockUseCase.success({ id: '1' });
267
+ const load = mockUseCase.error(new Error('Network error'));
268
+ const search = mockUseCase.async([{ id: '1' }], 50); // resolves after 50ms
269
+
270
+ renderWithCAF(<Form />, { useCases: { submit, load, search } });
271
+ ```
272
+
273
+ - `mockUseCase.success(data)` — always returns success with `data`
274
+ - `mockUseCase.error(error)` — always returns `error`
275
+ - `mockUseCase.async(data, delayMs?)` — resolves with `data` after optional delay
276
+ - `mockUseCase.fn(implementation)` — custom implementation (same as `createMockUseCase` from core)
277
+
278
+ ### Vue Testing Utilities (`@c-a-f/testing/vue`)
279
+
280
+ Use these when testing Vue components that use `usePlocFromContext`, `useUseCaseFromContext`, or `useCAFContext`.
281
+
282
+ **Requirements:** `vue` and `@vue/test-utils` (peer dependencies). Install in your app:
283
+
284
+ ```bash
285
+ npm install vue @vue/test-utils
286
+ ```
287
+
288
+ #### mountWithCAF
289
+
290
+ Mount a component with CAF context (Plocs/UseCases) so inject works:
291
+
292
+ ```ts
293
+ import { mount } from '@vue/test-utils';
294
+ import { mountWithCAF, createTestPloc, mockUseCase } from '@c-a-f/testing/vue';
295
+ import { usePlocFromContext, usePloc } from '@c-a-f/infrastructure-vue';
296
+
297
+ const Counter = defineComponent({
298
+ setup() {
299
+ const ploc = usePlocFromContext('counter');
300
+ const [state] = usePloc(ploc!);
301
+ return () => h('span', { 'data-testid': 'count' }, state.count);
302
+ },
303
+ });
304
+
305
+ it('renders with CAF context', () => {
306
+ const ploc = createTestPloc({ count: 5 });
307
+ const wrapper = mountWithCAF(Counter, { plocs: { counter: ploc } });
308
+ expect(wrapper.get('[data-testid="count"]').text()).toBe('5');
309
+ });
310
+ ```
311
+
312
+ #### createTestPloc / waitForPlocState / mockUseCase
313
+
314
+ Same as React: use `createTestPloc`, `waitForPlocState`, and `mockUseCase` with `mountWithCAF` for Vue component tests.
315
+
316
+ ### Angular Testing Utilities (`@c-a-f/testing/angular`)
317
+
318
+ Use these when testing Angular components that use `injectPlocFromContext`, `injectUseCaseFromContext`, or `injectCAFContext`.
319
+
320
+ **Requirements:** `@angular/core` and `@c-a-f/infrastructure-angular` (peer/dependency). Install in your app:
321
+
322
+ ```bash
323
+ npm install @angular/core @c-a-f/infrastructure-angular
324
+ ```
325
+
326
+ #### provideTestingCAF
327
+
328
+ Provide CAF context in `TestBed` so injection works:
329
+
330
+ ```ts
331
+ import { TestBed } from '@angular/core/testing';
332
+ import { provideTestingCAF, createTestPloc, mockUseCase } from '@c-a-f/testing/angular';
333
+
334
+ const ploc = createTestPloc({ count: 0 });
335
+ await TestBed.configureTestingModule({
336
+ imports: [MyComponent],
337
+ providers: [
338
+ provideTestingCAF({
339
+ plocs: { counter: ploc },
340
+ useCases: { submit: mockUseCase.success({ id: '1' }) },
341
+ }),
342
+ ],
343
+ }).compileComponents();
344
+
345
+ const fixture = TestBed.createComponent(MyComponent);
346
+ ```
347
+
348
+ #### createTestPloc / waitForPlocState / mockUseCase
349
+
350
+ Same as React/Vue: use `createTestPloc`, `waitForPlocState`, and `mockUseCase` with `provideTestingCAF` in your Angular tests.
351
+
352
+ #### Testing RouteManager
353
+
354
+ ```typescript
355
+ import { createMockRouteRepository, createRouteManagerTester } from '@c-a-f/testing/core';
356
+ import { RouteManager } from '@c-a-f/core';
357
+
358
+ describe('RouteManager', () => {
359
+ it('tracks route changes', async () => {
360
+ const mockRepo = createMockRouteRepository();
361
+ const routeManager = new RouteManager(mockRepo);
362
+ const tester = createRouteManagerTester(routeManager);
363
+
364
+ await tester.changeRoute('/dashboard');
365
+ expect(tester.getCurrentRoute()).toBe('/dashboard');
366
+ expect(tester.getRouteHistory()).toContain('/dashboard');
367
+ });
368
+ });
369
+ ```
370
+
371
+ ### Workflow Testing Utilities
372
+
373
+ ```typescript
374
+ import { createWorkflowTester, waitForWorkflowState } from '@c-a-f/testing/workflow';
375
+ import { WorkflowManager, WorkflowDefinition } from '@c-a-f/workflow';
376
+
377
+ describe('Workflow', () => {
378
+ it('tracks workflow state changes', async () => {
379
+ const workflow = new WorkflowManager(definition);
380
+ const tester = createWorkflowTester(workflow);
381
+
382
+ await tester.dispatch('approve');
383
+ await waitForWorkflowState(workflow, 'approved');
384
+
385
+ expect(tester.getCurrentState()).toBe('approved');
386
+ expect(tester.getStateHistory().length).toBeGreaterThan(1);
387
+
388
+ tester.cleanup();
389
+ });
390
+
391
+ it('waits for final state', async () => {
392
+ const workflow = new WorkflowManager(definition);
393
+
394
+ await workflow.dispatch('complete');
395
+ const finalState = await waitForFinalState(workflow);
396
+
397
+ expect(finalState.isFinal).toBe(true);
398
+ });
399
+ });
400
+ ```
401
+
402
+ ### Permission Testing Utilities
403
+
404
+ ```typescript
405
+ import { createMockPermissionChecker, createPermissionTester } from '@c-a-f/testing/permission';
406
+ import { PermissionManager } from '@c-a-f/permission';
407
+
408
+ describe('Permission', () => {
409
+ it('tests permission checking', async () => {
410
+ const mockChecker = createMockPermissionChecker(['user.edit', 'post.create']);
411
+ const manager = new PermissionManager(mockChecker);
412
+ const tester = createPermissionTester(manager);
413
+
414
+ expect(await tester.hasPermission('user.edit')).toBe(true);
415
+ expect(await tester.hasPermission('user.delete')).toBe(false);
416
+ expect(await tester.hasAnyPermission(['user.edit', 'user.delete'])).toBe(true);
417
+ });
418
+
419
+ it('modifies permissions dynamically', () => {
420
+ const mockChecker = createMockPermissionChecker(['user.view']);
421
+
422
+ mockChecker.addPermission('user.edit');
423
+ expect(mockChecker.check('user.edit').granted).toBe(true);
424
+
425
+ mockChecker.removePermission('user.view');
426
+ expect(mockChecker.check('user.view').granted).toBe(false);
427
+ });
428
+ });
429
+ ```
430
+
431
+ ### I18n Testing Utilities
432
+
433
+ ```typescript
434
+ import { createMockTranslator, createTranslationTester } from '@c-a-f/testing/i18n';
435
+ import { TranslationManager } from '@c-a-f/i18n';
436
+
437
+ describe('I18n', () => {
438
+ it('tests translation', () => {
439
+ const mockTranslator = createMockTranslator({
440
+ en: { greeting: 'Hello', welcome: 'Welcome {{name}}' },
441
+ fa: { greeting: 'سلام', welcome: 'خوش آمدید {{name}}' },
442
+ });
443
+ const manager = new TranslationManager(mockTranslator);
444
+ const tester = createTranslationTester(manager);
445
+
446
+ expect(tester.t('greeting')).toBe('Hello');
447
+ expect(tester.translateWithValues('welcome', { name: 'John' })).toBe('Welcome John');
448
+ });
449
+
450
+ it('tests language switching', async () => {
451
+ const mockTranslator = createMockTranslator({
452
+ en: { greeting: 'Hello' },
453
+ fa: { greeting: 'سلام' },
454
+ });
455
+ const manager = new TranslationManager(mockTranslator);
456
+ const tester = createTranslationTester(manager);
457
+
458
+ expect(tester.getCurrentLanguage()).toBe('en');
459
+ await tester.changeLanguage('fa');
460
+ expect(tester.getCurrentLanguage()).toBe('fa');
461
+ expect(tester.t('greeting')).toBe('سلام');
462
+ });
463
+ });
464
+ ```
465
+
466
+ ### Validation Testing Utilities
467
+
468
+ ```typescript
469
+ import { createMockValidator, createValidationTester } from '@c-a-f/testing/validation';
470
+
471
+ describe('Validation', () => {
472
+ it('tests validation', async () => {
473
+ const mockValidator = createMockValidator((data: any) => {
474
+ return data.email && data.email.includes('@');
475
+ });
476
+ const tester = createValidationTester(mockValidator);
477
+
478
+ const successResult = await tester.expectSuccess({ email: 'test@example.com' });
479
+ expect(successResult.email).toBe('test@example.com');
480
+
481
+ const errors = await tester.expectFailure({ email: 'invalid' });
482
+ expect(errors.length).toBeGreaterThan(0);
483
+ });
484
+ });
485
+ ```
486
+
487
+ ## Exports
488
+
489
+ ### Core Testing (`@c-a-f/testing/core`)
490
+
491
+ - `PlocTester` — Tester for Ploc instances
492
+ - `createPlocTester` — Create a Ploc tester
493
+ - `createMockPloc` — Create a Ploc with controllable state (no logic)
494
+ - `MockPloc` — Concrete Ploc class for tests
495
+ - `waitForStateChange` — Wait for state change matching predicate
496
+ - `waitForStateChanges` — Wait for specific number of state changes
497
+ - `assertStateHistory` — Assert state history matches expected (snapshot-style)
498
+ - `getStateHistorySnapshot` / `getStateHistorySnapshotJson` — Snapshot state history
499
+ - `PulseTester` — Tester for Pulse instances
500
+ - `createPulseTester` — Create a Pulse tester
501
+ - `waitForPulseValue` — Wait for pulse value matching predicate
502
+ - `MockUseCase` — Mock UseCase implementation
503
+ - `createMockUseCase` — Create a mock UseCase
504
+ - `createMockUseCaseSuccess` — Mock UseCase that returns success with data
505
+ - `createMockUseCaseError` — Mock UseCase that returns an error
506
+ - `createMockUseCaseAsync` — Mock UseCase that resolves after optional delay
507
+ - `UseCaseTester` — Tester for UseCase instances
508
+ - `createUseCaseTester` — Create a UseCase tester
509
+ - `createSuccessResult` — Create successful RequestResult
510
+ - `createErrorResult` — Create failed RequestResult
511
+ - `createLoadingResult` — Create loading RequestResult
512
+ - `createMockRepository` — Generic stub for domain I*Repository interfaces
513
+ - `createMockRepositoryStub` — Empty repository stub (assign or spy methods)
514
+ - `flushPromises` — Resolve pending microtasks in integration tests
515
+ - `runWithFakeTimers` — Placeholder for running code with fake timers
516
+ - `createPlocUseCaseContext` — Minimal Ploc + UseCase context for integration tests
517
+ - `MockRouteRepository` — Mock RouteRepository implementation
518
+ - `createMockRouteRepository` — Create a mock RouteRepository
519
+ - `RouteManagerTester` — Tester for RouteManager instances
520
+ - `createRouteManagerTester` — Create a RouteManager tester
521
+
522
+ ### Workflow Testing (`@c-a-f/testing/workflow`)
523
+
524
+ - `WorkflowTester` — Tester for WorkflowManager instances
525
+ - `createWorkflowTester` — Create a Workflow tester
526
+ - `waitForWorkflowState` — Wait for workflow to reach specific state
527
+ - `waitForFinalState` — Wait for workflow to reach final state
528
+
529
+ ### Permission Testing (`@c-a-f/testing/permission`)
530
+
531
+ - `MockPermissionChecker` — Mock PermissionChecker implementation
532
+ - `createMockPermissionChecker` — Create a mock PermissionChecker
533
+ - `PermissionTester` — Tester for PermissionManager instances
534
+ - `createPermissionTester` — Create a Permission tester
535
+
536
+ ### I18n Testing (`@c-a-f/testing/i18n`)
537
+
538
+ - `MockTranslator` — Mock Translator implementation
539
+ - `createMockTranslator` — Create a mock Translator
540
+ - `TranslationTester` — Tester for TranslationManager instances
541
+ - `createTranslationTester` — Create a Translation tester
542
+
543
+ ### Validation Testing (`@c-a-f/testing/validation`)
544
+
545
+ - `MockValidator` — Mock Validator implementation
546
+ - `createMockValidator` — Create a mock Validator
547
+ - `ValidationTester` — Tester for Validator instances
548
+ - `createValidationTester` — Create a Validation tester
549
+ - `expectSuccess` — Validate and expect success
550
+ - `expectFailure` — Validate and expect failure
551
+
552
+ ### React Testing (`@c-a-f/testing/react`)
553
+
554
+ - `renderWithCAF` — Render with CAFProvider (Ploc/UseCase context)
555
+ - `RenderWithCAFOptions` — Options for renderWithCAF (plocs, useCases, and RTL options)
556
+ - `createTestPloc` — Create a test Ploc with controllable state
557
+ - `waitForPlocState` — Wait for Ploc state to match a predicate
558
+ - `mockUseCase` — Mock UseCase helpers: `success`, `error`, `async`, `fn`
559
+
560
+ ### Vue Testing (`@c-a-f/testing/vue`)
561
+
562
+ - `mountWithCAF` — Mount with CAF context (Ploc/UseCase provide)
563
+ - `MountWithCAFOptions` — Options for mountWithCAF (plocs, useCases, and Vue Test Utils options)
564
+ - `createTestPloc` — Create a test Ploc with controllable state
565
+ - `waitForPlocState` — Wait for Ploc state to match a predicate
566
+ - `mockUseCase` — Mock UseCase helpers: `success`, `error`, `async`, `fn`
567
+
568
+ ### Angular Testing (`@c-a-f/testing/angular`)
569
+
570
+ - `provideTestingCAF` — Provide CAF context for TestBed (plocs, useCases)
571
+ - `ProvideTestingCAFConfig` — Config for provideTestingCAF
572
+ - `createTestPloc` — Create a test Ploc with controllable state
573
+ - `waitForPlocState` — Wait for Ploc state to match a predicate
574
+ - `mockUseCase` — Mock UseCase helpers: `success`, `error`, `async`, `fn`
575
+
576
+ ## Dependencies
577
+
578
+ - `@c-a-f/core` — Core primitives
579
+ - `@c-a-f/infrastructure-react` — React provider (for `@c-a-f/testing/react`)
580
+ - `@c-a-f/infrastructure-vue` — Vue provider (for `@c-a-f/testing/vue`)
581
+ - `@c-a-f/infrastructure-angular` — Angular provider (for `@c-a-f/testing/angular`)
582
+ - `@c-a-f/workflow` — Workflow package (for workflow testing utilities)
583
+ - `@c-a-f/permission` — Permission package (for permission testing utilities)
584
+ - `@c-a-f/i18n` — I18n package (for i18n testing utilities)
585
+ - `@c-a-f/validation` — Validation package (for validation testing utilities)
586
+
587
+ **Framework testing:** Optional peer dependencies per entry point:
588
+ - `@c-a-f/testing/react`: `react`, `@testing-library/react`
589
+ - `@c-a-f/testing/vue`: `vue`, `@vue/test-utils`
590
+ - `@c-a-f/testing/angular`: `@angular/core`
591
+
592
+ ## Dev Dependencies
593
+
594
+ - `vitest` — Testing framework (optional, works with any testing framework)
595
+
596
+ ## License
597
+
598
+ MIT