@c.a.f/testing 1.0.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/.build/__tests__/react/renderWithCAF.spec.d.ts +1 -0
- package/.build/__tests__/react/renderWithCAF.spec.js +53 -0
- package/.build/index.d.ts +1 -0
- package/.build/index.js +1 -0
- package/.build/src/core/IntegrationTestHelpers.d.ts +77 -0
- package/.build/src/core/IntegrationTestHelpers.js +78 -0
- package/.build/src/core/PlocTestHelpers.d.ts +133 -0
- package/.build/src/core/PlocTestHelpers.js +205 -0
- package/.build/src/core/PulseTestHelpers.d.ts +71 -0
- package/.build/src/core/PulseTestHelpers.js +106 -0
- package/.build/src/core/RepositoryTestHelpers.d.ts +48 -0
- package/.build/src/core/RepositoryTestHelpers.js +76 -0
- package/.build/src/core/RouteTestHelpers.d.ts +67 -0
- package/.build/src/core/RouteTestHelpers.js +94 -0
- package/.build/src/core/UseCaseTestHelpers.d.ts +100 -0
- package/.build/src/core/UseCaseTestHelpers.js +161 -0
- package/.build/src/core/index.d.ts +6 -0
- package/.build/src/core/index.js +6 -0
- package/.build/src/i18n/I18nTestHelpers.d.ts +76 -0
- package/.build/src/i18n/I18nTestHelpers.js +122 -0
- package/.build/src/i18n/index.d.ts +1 -0
- package/.build/src/i18n/index.js +1 -0
- package/.build/src/index.d.ts +5 -0
- package/.build/src/index.js +10 -0
- package/.build/src/permission/PermissionTestHelpers.d.ts +75 -0
- package/.build/src/permission/PermissionTestHelpers.js +121 -0
- package/.build/src/permission/index.d.ts +1 -0
- package/.build/src/permission/index.js +1 -0
- package/.build/src/react/createTestPloc.d.ts +19 -0
- package/.build/src/react/createTestPloc.js +21 -0
- package/.build/src/react/index.d.ts +12 -0
- package/.build/src/react/index.js +12 -0
- package/.build/src/react/mockUseCase.d.ts +36 -0
- package/.build/src/react/mockUseCase.js +44 -0
- package/.build/src/react/renderWithCAF.d.ts +31 -0
- package/.build/src/react/renderWithCAF.js +23 -0
- package/.build/src/react/waitForPlocState.d.ts +22 -0
- package/.build/src/react/waitForPlocState.js +24 -0
- package/.build/src/validation/ValidationTestHelpers.d.ts +66 -0
- package/.build/src/validation/ValidationTestHelpers.js +118 -0
- package/.build/src/validation/index.d.ts +1 -0
- package/.build/src/validation/index.js +1 -0
- package/.build/src/workflow/WorkflowTestHelpers.d.ts +75 -0
- package/.build/src/workflow/WorkflowTestHelpers.js +146 -0
- package/.build/src/workflow/index.d.ts +1 -0
- package/.build/src/workflow/index.js +1 -0
- package/.build/vitest.config.d.ts +7 -0
- package/.build/vitest.config.js +6 -0
- package/README.md +503 -0
- package/package.json +87 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for Pulse (reactive values).
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for testing Pulse instances.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createPulseTester, waitForPulseValue } from '@c.a.f/testing/core';
|
|
9
|
+
* import { pulse } from '@c.a.f/core';
|
|
10
|
+
*
|
|
11
|
+
* const count = pulse(0);
|
|
12
|
+
* const tester = createPulseTester(count);
|
|
13
|
+
*
|
|
14
|
+
* count.value = 5;
|
|
15
|
+
* await waitForPulseValue(count, (value) => value === 5);
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export class PulseTester {
|
|
19
|
+
pulse;
|
|
20
|
+
valueHistory = [];
|
|
21
|
+
listener = null;
|
|
22
|
+
constructor(pulse) {
|
|
23
|
+
this.pulse = pulse;
|
|
24
|
+
this.valueHistory.push(pulse.value);
|
|
25
|
+
this.listener = (value) => {
|
|
26
|
+
this.valueHistory.push(value);
|
|
27
|
+
};
|
|
28
|
+
pulse.subscribe(this.listener);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get the current value.
|
|
32
|
+
*/
|
|
33
|
+
getValue() {
|
|
34
|
+
return this.pulse.value;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the value history.
|
|
38
|
+
*/
|
|
39
|
+
getValueHistory() {
|
|
40
|
+
return [...this.valueHistory];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get the initial value.
|
|
44
|
+
*/
|
|
45
|
+
getInitialValue() {
|
|
46
|
+
return this.valueHistory[0];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the last value change.
|
|
50
|
+
*/
|
|
51
|
+
getLastValueChange() {
|
|
52
|
+
return this.valueHistory.length > 1
|
|
53
|
+
? this.valueHistory[this.valueHistory.length - 1]
|
|
54
|
+
: undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get the number of value changes.
|
|
58
|
+
*/
|
|
59
|
+
getValueChangeCount() {
|
|
60
|
+
return Math.max(0, this.valueHistory.length - 1);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Cleanup: unsubscribe from value changes.
|
|
64
|
+
*/
|
|
65
|
+
cleanup() {
|
|
66
|
+
if (this.listener) {
|
|
67
|
+
this.pulse.unsubscribe(this.listener);
|
|
68
|
+
this.listener = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create a Pulse tester instance.
|
|
74
|
+
*/
|
|
75
|
+
export function createPulseTester(pulse) {
|
|
76
|
+
return new PulseTester(pulse);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Wait for a pulse value that matches a predicate.
|
|
80
|
+
*
|
|
81
|
+
* @param pulse The Pulse instance to watch
|
|
82
|
+
* @param predicate Function that returns true when the desired value is reached
|
|
83
|
+
* @param timeout Maximum time to wait in milliseconds (default: 5000)
|
|
84
|
+
* @returns Promise that resolves when the predicate returns true
|
|
85
|
+
*/
|
|
86
|
+
export function waitForPulseValue(pulse, predicate, timeout = 5000) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
// Check current value first
|
|
89
|
+
if (predicate(pulse.value)) {
|
|
90
|
+
resolve(pulse.value);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const listener = (value) => {
|
|
94
|
+
if (predicate(value)) {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
pulse.unsubscribe(listener);
|
|
97
|
+
resolve(value);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const timer = setTimeout(() => {
|
|
101
|
+
pulse.unsubscribe(listener);
|
|
102
|
+
reject(new Error(`Timeout waiting for pulse value (${timeout}ms)`));
|
|
103
|
+
}, timeout);
|
|
104
|
+
pulse.subscribe(listener);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for domain repository interfaces (I*Repository).
|
|
3
|
+
*
|
|
4
|
+
* Provides a generic stub so you can override only the methods your test needs.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createMockRepository } from '@c.a.f/testing/core';
|
|
9
|
+
* import type { IUserRepository } from '../domain';
|
|
10
|
+
*
|
|
11
|
+
* const mockRepo = createMockRepository<IUserRepository>({
|
|
12
|
+
* getUsers: async () => [{ id: '1', name: 'John' }],
|
|
13
|
+
* getUserById: async (id) => ({ id, name: 'User ' + id }),
|
|
14
|
+
* });
|
|
15
|
+
* // getUsers and getUserById are stubbed; other methods return undefined (can be overridden)
|
|
16
|
+
*
|
|
17
|
+
* // Or use an empty stub and assign/spy later
|
|
18
|
+
* const stub = createMockRepository<IUserRepository>();
|
|
19
|
+
* stub.getUsers = async () => [];
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Create a generic mock repository stub.
|
|
24
|
+
* - With no args: returns a proxy that implements any method as async () => undefined.
|
|
25
|
+
* You can assign specific methods or use with a test spy.
|
|
26
|
+
* - With methods: returns an object with those methods; any other method call
|
|
27
|
+
* returns undefined (optional second argument can provide a default).
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const repo = createMockRepository<IUserRepository>({
|
|
32
|
+
* getUsers: async () => [],
|
|
33
|
+
* getUserById: async (id) => ({ id, name: 'Test' }),
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function createMockRepository<T extends Record<string, (...args: any[]) => Promise<any>>>(methods?: Partial<T>): T;
|
|
38
|
+
/**
|
|
39
|
+
* Create an empty repository stub. Every method returns Promise.resolve(undefined).
|
|
40
|
+
* Assign or spy on methods as needed.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const repo = createMockRepositoryStub<IUserRepository>();
|
|
45
|
+
* repo.getUsers = vi.fn().mockResolvedValue([{ id: '1', name: 'John' }]);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function createMockRepositoryStub<T>(): T;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for domain repository interfaces (I*Repository).
|
|
3
|
+
*
|
|
4
|
+
* Provides a generic stub so you can override only the methods your test needs.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createMockRepository } from '@c.a.f/testing/core';
|
|
9
|
+
* import type { IUserRepository } from '../domain';
|
|
10
|
+
*
|
|
11
|
+
* const mockRepo = createMockRepository<IUserRepository>({
|
|
12
|
+
* getUsers: async () => [{ id: '1', name: 'John' }],
|
|
13
|
+
* getUserById: async (id) => ({ id, name: 'User ' + id }),
|
|
14
|
+
* });
|
|
15
|
+
* // getUsers and getUserById are stubbed; other methods return undefined (can be overridden)
|
|
16
|
+
*
|
|
17
|
+
* // Or use an empty stub and assign/spy later
|
|
18
|
+
* const stub = createMockRepository<IUserRepository>();
|
|
19
|
+
* stub.getUsers = async () => [];
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
const defaultAsyncStub = async () => undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Create a generic mock repository stub.
|
|
25
|
+
* - With no args: returns a proxy that implements any method as async () => undefined.
|
|
26
|
+
* You can assign specific methods or use with a test spy.
|
|
27
|
+
* - With methods: returns an object with those methods; any other method call
|
|
28
|
+
* returns undefined (optional second argument can provide a default).
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const repo = createMockRepository<IUserRepository>({
|
|
33
|
+
* getUsers: async () => [],
|
|
34
|
+
* getUserById: async (id) => ({ id, name: 'Test' }),
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function createMockRepository(methods) {
|
|
39
|
+
const base = (methods ?? {});
|
|
40
|
+
return new Proxy(base, {
|
|
41
|
+
get(target, prop) {
|
|
42
|
+
if (prop in target && typeof target[prop] === 'function') {
|
|
43
|
+
return target[prop];
|
|
44
|
+
}
|
|
45
|
+
return defaultAsyncStub;
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create an empty repository stub. Every method returns Promise.resolve(undefined).
|
|
51
|
+
* Assign or spy on methods as needed.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const repo = createMockRepositoryStub<IUserRepository>();
|
|
56
|
+
* repo.getUsers = vi.fn().mockResolvedValue([{ id: '1', name: 'John' }]);
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function createMockRepositoryStub() {
|
|
60
|
+
const target = {};
|
|
61
|
+
return new Proxy(target, {
|
|
62
|
+
get(target, prop) {
|
|
63
|
+
// If property exists on target (was assigned), return it
|
|
64
|
+
if (prop in target) {
|
|
65
|
+
return target[prop];
|
|
66
|
+
}
|
|
67
|
+
// Otherwise return default stub
|
|
68
|
+
return defaultAsyncStub;
|
|
69
|
+
},
|
|
70
|
+
set(target, prop, value) {
|
|
71
|
+
// Allow assignment
|
|
72
|
+
target[prop] = value;
|
|
73
|
+
return true;
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for RouteManager and RouteRepository.
|
|
3
|
+
*
|
|
4
|
+
* Provides mock implementations and utilities for testing routing.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createMockRouteRepository, createRouteManagerTester } from '@c.a.f/testing/core';
|
|
9
|
+
* import { RouteManager } from '@c.a.f/core';
|
|
10
|
+
*
|
|
11
|
+
* const mockRepo = createMockRouteRepository();
|
|
12
|
+
* const routeManager = new RouteManager(mockRepo);
|
|
13
|
+
* const tester = createRouteManagerTester(routeManager);
|
|
14
|
+
*
|
|
15
|
+
* await tester.changeRoute('/dashboard');
|
|
16
|
+
* expect(tester.getCurrentRoute()).toBe('/dashboard');
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import type { RouteRepository, RouteManager } from '@c.a.f/core';
|
|
20
|
+
/**
|
|
21
|
+
* Mock RouteRepository implementation for testing.
|
|
22
|
+
*/
|
|
23
|
+
export declare class MockRouteRepository implements RouteRepository {
|
|
24
|
+
private _currentRoute;
|
|
25
|
+
get currentRoute(): string;
|
|
26
|
+
change(route: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Set the current route directly (for testing).
|
|
29
|
+
*/
|
|
30
|
+
setRoute(route: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Get route change history.
|
|
33
|
+
*/
|
|
34
|
+
getRouteHistory(): string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a mock RouteRepository.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createMockRouteRepository(): MockRouteRepository;
|
|
40
|
+
/**
|
|
41
|
+
* RouteManager tester utility.
|
|
42
|
+
*/
|
|
43
|
+
export declare class RouteManagerTester {
|
|
44
|
+
readonly routeManager: RouteManager;
|
|
45
|
+
private routeHistory;
|
|
46
|
+
constructor(routeManager: RouteManager);
|
|
47
|
+
/**
|
|
48
|
+
* Change route and track it.
|
|
49
|
+
*/
|
|
50
|
+
changeRoute(route: string): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Get the current route.
|
|
53
|
+
*/
|
|
54
|
+
getCurrentRoute(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Get route change history.
|
|
57
|
+
*/
|
|
58
|
+
getRouteHistory(): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Check if user is logged in (based on RouteManager auth options).
|
|
61
|
+
*/
|
|
62
|
+
checkForLoginRoute(): void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create a RouteManager tester instance.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createRouteManagerTester(routeManager: RouteManager): RouteManagerTester;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for RouteManager and RouteRepository.
|
|
3
|
+
*
|
|
4
|
+
* Provides mock implementations and utilities for testing routing.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createMockRouteRepository, createRouteManagerTester } from '@c.a.f/testing/core';
|
|
9
|
+
* import { RouteManager } from '@c.a.f/core';
|
|
10
|
+
*
|
|
11
|
+
* const mockRepo = createMockRouteRepository();
|
|
12
|
+
* const routeManager = new RouteManager(mockRepo);
|
|
13
|
+
* const tester = createRouteManagerTester(routeManager);
|
|
14
|
+
*
|
|
15
|
+
* await tester.changeRoute('/dashboard');
|
|
16
|
+
* expect(tester.getCurrentRoute()).toBe('/dashboard');
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Mock RouteRepository implementation for testing.
|
|
21
|
+
*/
|
|
22
|
+
export class MockRouteRepository {
|
|
23
|
+
_currentRoute = '/';
|
|
24
|
+
get currentRoute() {
|
|
25
|
+
return this._currentRoute;
|
|
26
|
+
}
|
|
27
|
+
change(route) {
|
|
28
|
+
this._currentRoute = route;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Set the current route directly (for testing).
|
|
32
|
+
*/
|
|
33
|
+
setRoute(route) {
|
|
34
|
+
this._currentRoute = route;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get route change history.
|
|
38
|
+
*/
|
|
39
|
+
getRouteHistory() {
|
|
40
|
+
return [this._currentRoute];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a mock RouteRepository.
|
|
45
|
+
*/
|
|
46
|
+
export function createMockRouteRepository() {
|
|
47
|
+
return new MockRouteRepository();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* RouteManager tester utility.
|
|
51
|
+
*/
|
|
52
|
+
export class RouteManagerTester {
|
|
53
|
+
routeManager;
|
|
54
|
+
routeHistory = [];
|
|
55
|
+
constructor(routeManager) {
|
|
56
|
+
this.routeManager = routeManager;
|
|
57
|
+
// Track route changes if possible
|
|
58
|
+
const repo = routeManager.routeRepository;
|
|
59
|
+
if (repo instanceof MockRouteRepository) {
|
|
60
|
+
this.routeHistory.push(repo.currentRoute);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Change route and track it.
|
|
65
|
+
*/
|
|
66
|
+
async changeRoute(route) {
|
|
67
|
+
this.routeManager.changeRoute(route);
|
|
68
|
+
this.routeHistory.push(route);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get the current route.
|
|
72
|
+
*/
|
|
73
|
+
getCurrentRoute() {
|
|
74
|
+
return this.routeManager.routeRepository.currentRoute;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get route change history.
|
|
78
|
+
*/
|
|
79
|
+
getRouteHistory() {
|
|
80
|
+
return [...this.routeHistory];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if user is logged in (based on RouteManager auth options).
|
|
84
|
+
*/
|
|
85
|
+
checkForLoginRoute() {
|
|
86
|
+
this.routeManager.checkForLoginRoute();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Create a RouteManager tester instance.
|
|
91
|
+
*/
|
|
92
|
+
export function createRouteManagerTester(routeManager) {
|
|
93
|
+
return new RouteManagerTester(routeManager);
|
|
94
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for UseCase.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for testing UseCase implementations.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createMockUseCase, createUseCaseTester } from '@c.a.f/testing/core';
|
|
9
|
+
* import { UseCase, RequestResult, pulse } from '@c.a.f/core';
|
|
10
|
+
*
|
|
11
|
+
* // Create a mock use case
|
|
12
|
+
* const mockUseCase = createMockUseCase<User[]>((args) => ({
|
|
13
|
+
* loading: pulse(false),
|
|
14
|
+
* data: pulse([{ id: '1', name: 'John' }]),
|
|
15
|
+
* error: pulse(null! as Error),
|
|
16
|
+
* }));
|
|
17
|
+
*
|
|
18
|
+
* // Test use case
|
|
19
|
+
* const tester = createUseCaseTester(mockUseCase);
|
|
20
|
+
* const result = await tester.execute([], { timeout: 1000 });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import type { UseCase, RequestResult } from '@c.a.f/core';
|
|
24
|
+
/**
|
|
25
|
+
* Mock UseCase implementation for testing.
|
|
26
|
+
*/
|
|
27
|
+
export declare class MockUseCase<A extends any[], T> implements UseCase<A, T> {
|
|
28
|
+
private implementation;
|
|
29
|
+
constructor(implementation: (...args: A) => Promise<RequestResult<T>> | RequestResult<T>);
|
|
30
|
+
execute(...args: A): Promise<RequestResult<T>>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a mock UseCase from an implementation.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createMockUseCase<A extends any[], T>(implementation: (...args: A) => Promise<RequestResult<T>> | RequestResult<T>): UseCase<A, T>;
|
|
36
|
+
/**
|
|
37
|
+
* Create a mock UseCase that always returns success with the given data.
|
|
38
|
+
* Useful for unit tests that do not care about loading/error states.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const useCase = createMockUseCaseSuccess([{ id: '1', name: 'John' }]);
|
|
43
|
+
* const result = await useCase.execute();
|
|
44
|
+
* expect(result.data.value).toEqual([{ id: '1', name: 'John' }]);
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function createMockUseCaseSuccess<T>(data: T): UseCase<[], T>;
|
|
48
|
+
/**
|
|
49
|
+
* Create a mock UseCase that always returns the given error.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const useCase = createMockUseCaseError(new Error('Network failed'));
|
|
54
|
+
* const result = await useCase.execute();
|
|
55
|
+
* expect(result.error.value?.message).toBe('Network failed');
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function createMockUseCaseError<T = unknown>(error: Error): UseCase<[], T>;
|
|
59
|
+
/**
|
|
60
|
+
* Create a mock UseCase that returns loading then success (async).
|
|
61
|
+
* Useful for testing loading states.
|
|
62
|
+
*/
|
|
63
|
+
export declare function createMockUseCaseAsync<T>(data: T, delayMs?: number): UseCase<[], T>;
|
|
64
|
+
/**
|
|
65
|
+
* UseCase tester utility.
|
|
66
|
+
*/
|
|
67
|
+
export declare class UseCaseTester<A extends any[], T> {
|
|
68
|
+
readonly useCase: UseCase<A, T>;
|
|
69
|
+
constructor(useCase: UseCase<A, T>);
|
|
70
|
+
/**
|
|
71
|
+
* Execute the use case and wait for completion.
|
|
72
|
+
*/
|
|
73
|
+
execute(args: A, options?: {
|
|
74
|
+
timeout?: number;
|
|
75
|
+
}): Promise<RequestResult<T>>;
|
|
76
|
+
/**
|
|
77
|
+
* Execute the use case and extract data.
|
|
78
|
+
*/
|
|
79
|
+
executeAndGetData(args: A): Promise<T>;
|
|
80
|
+
/**
|
|
81
|
+
* Execute the use case and check if it succeeds.
|
|
82
|
+
*/
|
|
83
|
+
executeAndCheckSuccess(args: A): Promise<boolean>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create a UseCase tester instance.
|
|
87
|
+
*/
|
|
88
|
+
export declare function createUseCaseTester<A extends any[], T>(useCase: UseCase<A, T>): UseCaseTester<A, T>;
|
|
89
|
+
/**
|
|
90
|
+
* Create a successful RequestResult.
|
|
91
|
+
*/
|
|
92
|
+
export declare function createSuccessResult<T>(data: T): RequestResult<T>;
|
|
93
|
+
/**
|
|
94
|
+
* Create a failed RequestResult.
|
|
95
|
+
*/
|
|
96
|
+
export declare function createErrorResult<T>(error: Error): RequestResult<T>;
|
|
97
|
+
/**
|
|
98
|
+
* Create a loading RequestResult.
|
|
99
|
+
*/
|
|
100
|
+
export declare function createLoadingResult<T>(): RequestResult<T>;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for UseCase.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for testing UseCase implementations.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createMockUseCase, createUseCaseTester } from '@c.a.f/testing/core';
|
|
9
|
+
* import { UseCase, RequestResult, pulse } from '@c.a.f/core';
|
|
10
|
+
*
|
|
11
|
+
* // Create a mock use case
|
|
12
|
+
* const mockUseCase = createMockUseCase<User[]>((args) => ({
|
|
13
|
+
* loading: pulse(false),
|
|
14
|
+
* data: pulse([{ id: '1', name: 'John' }]),
|
|
15
|
+
* error: pulse(null! as Error),
|
|
16
|
+
* }));
|
|
17
|
+
*
|
|
18
|
+
* // Test use case
|
|
19
|
+
* const tester = createUseCaseTester(mockUseCase);
|
|
20
|
+
* const result = await tester.execute([], { timeout: 1000 });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { pulse } from '@c.a.f/core';
|
|
24
|
+
/**
|
|
25
|
+
* Mock UseCase implementation for testing.
|
|
26
|
+
*/
|
|
27
|
+
export class MockUseCase {
|
|
28
|
+
implementation;
|
|
29
|
+
constructor(implementation) {
|
|
30
|
+
this.implementation = implementation;
|
|
31
|
+
}
|
|
32
|
+
async execute(...args) {
|
|
33
|
+
return await this.implementation(...args);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a mock UseCase from an implementation.
|
|
38
|
+
*/
|
|
39
|
+
export function createMockUseCase(implementation) {
|
|
40
|
+
return new MockUseCase(implementation);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a mock UseCase that always returns success with the given data.
|
|
44
|
+
* Useful for unit tests that do not care about loading/error states.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const useCase = createMockUseCaseSuccess([{ id: '1', name: 'John' }]);
|
|
49
|
+
* const result = await useCase.execute();
|
|
50
|
+
* expect(result.data.value).toEqual([{ id: '1', name: 'John' }]);
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function createMockUseCaseSuccess(data) {
|
|
54
|
+
return new MockUseCase(() => createSuccessResult(data));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create a mock UseCase that always returns the given error.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* const useCase = createMockUseCaseError(new Error('Network failed'));
|
|
62
|
+
* const result = await useCase.execute();
|
|
63
|
+
* expect(result.error.value?.message).toBe('Network failed');
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export function createMockUseCaseError(error) {
|
|
67
|
+
return new MockUseCase(() => createErrorResult(error));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a mock UseCase that returns loading then success (async).
|
|
71
|
+
* Useful for testing loading states.
|
|
72
|
+
*/
|
|
73
|
+
export function createMockUseCaseAsync(data, delayMs = 0) {
|
|
74
|
+
return new MockUseCase(() => new Promise((resolve) => {
|
|
75
|
+
if (delayMs <= 0) {
|
|
76
|
+
resolve(createSuccessResult(data));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
setTimeout(() => resolve(createSuccessResult(data)), delayMs);
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* UseCase tester utility.
|
|
85
|
+
*/
|
|
86
|
+
export class UseCaseTester {
|
|
87
|
+
useCase;
|
|
88
|
+
constructor(useCase) {
|
|
89
|
+
this.useCase = useCase;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Execute the use case and wait for completion.
|
|
93
|
+
*/
|
|
94
|
+
async execute(args, options) {
|
|
95
|
+
const timeout = options?.timeout || 5000;
|
|
96
|
+
return Promise.race([
|
|
97
|
+
this.useCase.execute(...args),
|
|
98
|
+
new Promise((_, reject) => {
|
|
99
|
+
setTimeout(() => reject(new Error(`UseCase execution timeout (${timeout}ms)`)), timeout);
|
|
100
|
+
}),
|
|
101
|
+
]);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Execute the use case and extract data.
|
|
105
|
+
*/
|
|
106
|
+
async executeAndGetData(args) {
|
|
107
|
+
const result = await this.execute(args);
|
|
108
|
+
if (result.error.value) {
|
|
109
|
+
throw result.error.value;
|
|
110
|
+
}
|
|
111
|
+
return result.data.value;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Execute the use case and check if it succeeds.
|
|
115
|
+
*/
|
|
116
|
+
async executeAndCheckSuccess(args) {
|
|
117
|
+
try {
|
|
118
|
+
const result = await this.execute(args);
|
|
119
|
+
return !result.error.value;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Create a UseCase tester instance.
|
|
128
|
+
*/
|
|
129
|
+
export function createUseCaseTester(useCase) {
|
|
130
|
+
return new UseCaseTester(useCase);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Create a successful RequestResult.
|
|
134
|
+
*/
|
|
135
|
+
export function createSuccessResult(data) {
|
|
136
|
+
return {
|
|
137
|
+
loading: pulse(false),
|
|
138
|
+
data: pulse(data),
|
|
139
|
+
error: pulse(null),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Create a failed RequestResult.
|
|
144
|
+
*/
|
|
145
|
+
export function createErrorResult(error) {
|
|
146
|
+
return {
|
|
147
|
+
loading: pulse(false),
|
|
148
|
+
data: pulse(null),
|
|
149
|
+
error: pulse(error),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create a loading RequestResult.
|
|
154
|
+
*/
|
|
155
|
+
export function createLoadingResult() {
|
|
156
|
+
return {
|
|
157
|
+
loading: pulse(true),
|
|
158
|
+
data: pulse(null),
|
|
159
|
+
error: pulse(null),
|
|
160
|
+
};
|
|
161
|
+
}
|