@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.
- 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/angular/createTestPloc.d.ts +19 -0
- package/.build/src/angular/createTestPloc.js +21 -0
- package/.build/src/angular/index.d.ts +12 -0
- package/.build/src/angular/index.js +12 -0
- package/.build/src/angular/mockUseCase.d.ts +32 -0
- package/.build/src/angular/mockUseCase.js +40 -0
- package/.build/src/angular/provideTestingCAF.d.ts +29 -0
- package/.build/src/angular/provideTestingCAF.js +26 -0
- package/.build/src/angular/waitForPlocState.d.ts +18 -0
- package/.build/src/angular/waitForPlocState.js +20 -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/vue/createTestPloc.d.ts +19 -0
- package/.build/src/vue/createTestPloc.js +21 -0
- package/.build/src/vue/index.d.ts +12 -0
- package/.build/src/vue/index.js +12 -0
- package/.build/src/vue/mockUseCase.d.ts +34 -0
- package/.build/src/vue/mockUseCase.js +42 -0
- package/.build/src/vue/mountWithCAF.d.ts +31 -0
- package/.build/src/vue/mountWithCAF.js +40 -0
- package/.build/src/vue/waitForPlocState.d.ts +19 -0
- package/.build/src/vue/waitForPlocState.js +21 -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 +598 -0
- package/package.json +127 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './PermissionTestHelpers';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a Ploc instance for React component tests. Same as createMockPloc from core:
|
|
3
|
+
* a Ploc with controllable state and no business logic. Use with renderWithCAF.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { createTestPloc, renderWithCAF } from '@c-a-f/testing/react';
|
|
8
|
+
*
|
|
9
|
+
* const ploc = createTestPloc({ count: 0 });
|
|
10
|
+
* const { getByRole } = renderWithCAF(<Counter />, { plocs: { counter: ploc } });
|
|
11
|
+
* ploc.changeState({ count: 1 });
|
|
12
|
+
* expect(screen.getByText('1')).toBeInTheDocument();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import type { Ploc } from '@c-a-f/core';
|
|
16
|
+
/**
|
|
17
|
+
* Create a test Ploc with initial state. Use changeState() to drive state in tests.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createTestPloc<S>(initialState: S): Ploc<S>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a Ploc instance for React component tests. Same as createMockPloc from core:
|
|
3
|
+
* a Ploc with controllable state and no business logic. Use with renderWithCAF.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { createTestPloc, renderWithCAF } from '@c-a-f/testing/react';
|
|
8
|
+
*
|
|
9
|
+
* const ploc = createTestPloc({ count: 0 });
|
|
10
|
+
* const { getByRole } = renderWithCAF(<Counter />, { plocs: { counter: ploc } });
|
|
11
|
+
* ploc.changeState({ count: 1 });
|
|
12
|
+
* expect(screen.getByText('1')).toBeInTheDocument();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import { createMockPloc } from '../core/PlocTestHelpers';
|
|
16
|
+
/**
|
|
17
|
+
* Create a test Ploc with initial state. Use changeState() to drive state in tests.
|
|
18
|
+
*/
|
|
19
|
+
export function createTestPloc(initialState) {
|
|
20
|
+
return createMockPloc(initialState);
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React testing utilities for CAF.
|
|
3
|
+
*
|
|
4
|
+
* - renderWithCAF: render with CAFProvider (Ploc/UseCase context)
|
|
5
|
+
* - createTestPloc: create a test Ploc with controllable state
|
|
6
|
+
* - waitForPlocState: wait for Ploc state to match a predicate
|
|
7
|
+
* - mockUseCase: mock UseCase (success, error, async, fn)
|
|
8
|
+
*/
|
|
9
|
+
export { renderWithCAF, type RenderWithCAFOptions } from './renderWithCAF';
|
|
10
|
+
export { createTestPloc } from './createTestPloc';
|
|
11
|
+
export { waitForPlocState } from './waitForPlocState';
|
|
12
|
+
export { mockUseCase } from './mockUseCase';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React testing utilities for CAF.
|
|
3
|
+
*
|
|
4
|
+
* - renderWithCAF: render with CAFProvider (Ploc/UseCase context)
|
|
5
|
+
* - createTestPloc: create a test Ploc with controllable state
|
|
6
|
+
* - waitForPlocState: wait for Ploc state to match a predicate
|
|
7
|
+
* - mockUseCase: mock UseCase (success, error, async, fn)
|
|
8
|
+
*/
|
|
9
|
+
export { renderWithCAF } from './renderWithCAF';
|
|
10
|
+
export { createTestPloc } from './createTestPloc';
|
|
11
|
+
export { waitForPlocState } from './waitForPlocState';
|
|
12
|
+
export { mockUseCase } from './mockUseCase';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers to create mock UseCases for React component tests. Use with renderWithCAF.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```tsx
|
|
6
|
+
* import { renderWithCAF, mockUseCase } from '@c-a-f/testing/react';
|
|
7
|
+
*
|
|
8
|
+
* const submit = mockUseCase.success({ id: '1' });
|
|
9
|
+
* renderWithCAF(<Form />, { useCases: { submit } });
|
|
10
|
+
*
|
|
11
|
+
* // Or error
|
|
12
|
+
* const load = mockUseCase.error(new Error('Network error'));
|
|
13
|
+
*
|
|
14
|
+
* // Or custom implementation
|
|
15
|
+
* const search = mockUseCase.fn((query: string) => createSuccessResult([{ id: '1', title: query }]));
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { UseCase, RequestResult } from '@c-a-f/core';
|
|
19
|
+
export declare const mockUseCase: {
|
|
20
|
+
/**
|
|
21
|
+
* UseCase that always returns success with the given data.
|
|
22
|
+
*/
|
|
23
|
+
success<T>(data: T): UseCase<[], T>;
|
|
24
|
+
/**
|
|
25
|
+
* UseCase that always returns the given error.
|
|
26
|
+
*/
|
|
27
|
+
error<T_1 = unknown>(error: Error): UseCase<[], T_1>;
|
|
28
|
+
/**
|
|
29
|
+
* UseCase that resolves with data after an optional delay (for loading-state tests).
|
|
30
|
+
*/
|
|
31
|
+
async<T_2>(data: T_2, delayMs?: number): UseCase<[], T_2>;
|
|
32
|
+
/**
|
|
33
|
+
* UseCase with a custom implementation (same as createMockUseCase).
|
|
34
|
+
*/
|
|
35
|
+
fn<A extends any[], T_3>(implementation: (...args: A) => RequestResult<T_3> | Promise<RequestResult<T_3>>): UseCase<A, T_3>;
|
|
36
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers to create mock UseCases for React component tests. Use with renderWithCAF.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```tsx
|
|
6
|
+
* import { renderWithCAF, mockUseCase } from '@c-a-f/testing/react';
|
|
7
|
+
*
|
|
8
|
+
* const submit = mockUseCase.success({ id: '1' });
|
|
9
|
+
* renderWithCAF(<Form />, { useCases: { submit } });
|
|
10
|
+
*
|
|
11
|
+
* // Or error
|
|
12
|
+
* const load = mockUseCase.error(new Error('Network error'));
|
|
13
|
+
*
|
|
14
|
+
* // Or custom implementation
|
|
15
|
+
* const search = mockUseCase.fn((query: string) => createSuccessResult([{ id: '1', title: query }]));
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { createMockUseCase, createMockUseCaseSuccess, createMockUseCaseError, createMockUseCaseAsync, } from '../core/UseCaseTestHelpers';
|
|
19
|
+
export const mockUseCase = {
|
|
20
|
+
/**
|
|
21
|
+
* UseCase that always returns success with the given data.
|
|
22
|
+
*/
|
|
23
|
+
success(data) {
|
|
24
|
+
return createMockUseCaseSuccess(data);
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* UseCase that always returns the given error.
|
|
28
|
+
*/
|
|
29
|
+
error(error) {
|
|
30
|
+
return createMockUseCaseError(error);
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* UseCase that resolves with data after an optional delay (for loading-state tests).
|
|
34
|
+
*/
|
|
35
|
+
async(data, delayMs = 0) {
|
|
36
|
+
return createMockUseCaseAsync(data, delayMs);
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* UseCase with a custom implementation (same as createMockUseCase).
|
|
40
|
+
*/
|
|
41
|
+
fn(implementation) {
|
|
42
|
+
return createMockUseCase(implementation);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render a React component with CAFProvider so that usePlocFromContext and
|
|
3
|
+
* useUseCaseFromContext work. Use this instead of raw render() when testing
|
|
4
|
+
* components that depend on CAF context.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { renderWithCAF, createTestPloc, mockUseCase } from '@c-a-f/testing/react';
|
|
9
|
+
*
|
|
10
|
+
* const ploc = createTestPloc({ count: 0 });
|
|
11
|
+
* const { getByRole } = renderWithCAF(<Counter />, {
|
|
12
|
+
* plocs: { counter: ploc },
|
|
13
|
+
* useCases: { submit: mockUseCase.success({ id: '1' }) },
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { type ReactElement } from 'react';
|
|
18
|
+
import { type RenderOptions, type RenderResult } from '@testing-library/react';
|
|
19
|
+
import type { Ploc } from '@c-a-f/core';
|
|
20
|
+
import type { UseCase } from '@c-a-f/core';
|
|
21
|
+
export interface RenderWithCAFOptions extends Omit<RenderOptions, 'wrapper'> {
|
|
22
|
+
/** Plocs to provide (keyed by string). */
|
|
23
|
+
plocs?: Record<string, Ploc<unknown>>;
|
|
24
|
+
/** UseCases to provide (keyed by string). */
|
|
25
|
+
useCases?: Record<string, UseCase<any[], any>>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Render a component with CAFProvider so Ploc/UseCase context is available.
|
|
29
|
+
* Returns the same result as React Testing Library's render().
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderWithCAF(ui: ReactElement, options?: RenderWithCAFOptions): RenderResult;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { CAFProvider } from '@c-a-f/infrastructure-react';
|
|
4
|
+
/**
|
|
5
|
+
* Wrapper that provides CAFProvider around the component tree.
|
|
6
|
+
*/
|
|
7
|
+
function createCAFWrapper(plocs, useCases) {
|
|
8
|
+
return function Wrapper({ children }) {
|
|
9
|
+
return (_jsx(CAFProvider, { plocs: plocs ?? {}, useCases: useCases ?? {}, children: children }));
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Render a component with CAFProvider so Ploc/UseCase context is available.
|
|
14
|
+
* Returns the same result as React Testing Library's render().
|
|
15
|
+
*/
|
|
16
|
+
export function renderWithCAF(ui, options = {}) {
|
|
17
|
+
const { plocs, useCases, ...renderOptions } = options;
|
|
18
|
+
const Wrapper = createCAFWrapper(plocs, useCases);
|
|
19
|
+
return render(ui, {
|
|
20
|
+
...renderOptions,
|
|
21
|
+
wrapper: Wrapper,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wait for a Ploc to reach a state that matches a predicate. Useful in React tests
|
|
3
|
+
* after triggering an action that updates the Ploc asynchronously.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { renderWithCAF, createTestPloc, waitForPlocState } from '@c-a-f/testing/react';
|
|
8
|
+
*
|
|
9
|
+
* const ploc = createTestPloc({ loading: true, items: [] });
|
|
10
|
+
* renderWithCAF(<List />, { plocs: { list: ploc } });
|
|
11
|
+
*
|
|
12
|
+
* // Simulate load complete
|
|
13
|
+
* ploc.changeState({ loading: false, items: [{ id: '1' }] });
|
|
14
|
+
* await waitForPlocState(ploc, (state) => !state.loading && state.items.length > 0);
|
|
15
|
+
* expect(screen.getByText('1')).toBeInTheDocument();
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { Ploc } from '@c-a-f/core';
|
|
19
|
+
/**
|
|
20
|
+
* Wait until the Ploc's state satisfies the predicate (or timeout).
|
|
21
|
+
*/
|
|
22
|
+
export declare function waitForPlocState<S>(ploc: Ploc<S>, predicate: (state: S) => boolean, timeoutMs?: number): Promise<S>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wait for a Ploc to reach a state that matches a predicate. Useful in React tests
|
|
3
|
+
* after triggering an action that updates the Ploc asynchronously.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { renderWithCAF, createTestPloc, waitForPlocState } from '@c-a-f/testing/react';
|
|
8
|
+
*
|
|
9
|
+
* const ploc = createTestPloc({ loading: true, items: [] });
|
|
10
|
+
* renderWithCAF(<List />, { plocs: { list: ploc } });
|
|
11
|
+
*
|
|
12
|
+
* // Simulate load complete
|
|
13
|
+
* ploc.changeState({ loading: false, items: [{ id: '1' }] });
|
|
14
|
+
* await waitForPlocState(ploc, (state) => !state.loading && state.items.length > 0);
|
|
15
|
+
* expect(screen.getByText('1')).toBeInTheDocument();
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { waitForStateChange } from '../core/PlocTestHelpers';
|
|
19
|
+
/**
|
|
20
|
+
* Wait until the Ploc's state satisfies the predicate (or timeout).
|
|
21
|
+
*/
|
|
22
|
+
export function waitForPlocState(ploc, predicate, timeoutMs = 5000) {
|
|
23
|
+
return waitForStateChange(ploc, predicate, timeoutMs);
|
|
24
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for Validation.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for testing validators and validation runners.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createMockValidator, createValidationTester } from '@c-a-f/testing/validation';
|
|
9
|
+
* import { ValidationRunner } from '@c-a-f/validation';
|
|
10
|
+
*
|
|
11
|
+
* const mockValidator = createMockValidator((data) => {
|
|
12
|
+
* return data.email && data.email.includes('@');
|
|
13
|
+
* });
|
|
14
|
+
* const tester = createValidationTester(mockValidator);
|
|
15
|
+
*
|
|
16
|
+
* const result = await tester.validate({ email: 'test@example.com' });
|
|
17
|
+
* expect(result.success).toBe(true);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
import type { IValidator, ValidationResult, ValidationError } from '@c-a-f/validation';
|
|
21
|
+
/**
|
|
22
|
+
* Mock Validator implementation for testing.
|
|
23
|
+
*/
|
|
24
|
+
export declare class MockValidator<T = unknown> implements IValidator<T> {
|
|
25
|
+
private validateFn;
|
|
26
|
+
private errorMessage?;
|
|
27
|
+
constructor(validateFn: (data: unknown) => boolean | Promise<boolean>, errorMessage?: string | undefined);
|
|
28
|
+
validate(data: unknown): Promise<ValidationResult>;
|
|
29
|
+
parse(data: unknown): Promise<T>;
|
|
30
|
+
isValid(data: unknown): Promise<boolean>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a mock Validator.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createMockValidator<T = unknown>(validateFn: (data: unknown) => boolean | Promise<boolean>, errorMessage?: string): IValidator<T>;
|
|
36
|
+
/**
|
|
37
|
+
* Validation tester utility.
|
|
38
|
+
*/
|
|
39
|
+
export declare class ValidationTester<T = unknown> {
|
|
40
|
+
readonly validator: IValidator<T>;
|
|
41
|
+
constructor(validator: IValidator<T>);
|
|
42
|
+
/**
|
|
43
|
+
* Validate data.
|
|
44
|
+
*/
|
|
45
|
+
validate(data: unknown): Promise<ValidationResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Parse and validate data.
|
|
48
|
+
*/
|
|
49
|
+
parse(data: unknown): Promise<T>;
|
|
50
|
+
/**
|
|
51
|
+
* Check if data is valid.
|
|
52
|
+
*/
|
|
53
|
+
isValid(data: unknown): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Validate and expect success.
|
|
56
|
+
*/
|
|
57
|
+
expectSuccess(data: unknown): Promise<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Validate and expect failure.
|
|
60
|
+
*/
|
|
61
|
+
expectFailure(data: unknown): Promise<ValidationError[]>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create a Validation tester instance.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createValidationTester<T = unknown>(validator: IValidator<T>): ValidationTester<T>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for Validation.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for testing validators and validation runners.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { createMockValidator, createValidationTester } from '@c-a-f/testing/validation';
|
|
9
|
+
* import { ValidationRunner } from '@c-a-f/validation';
|
|
10
|
+
*
|
|
11
|
+
* const mockValidator = createMockValidator((data) => {
|
|
12
|
+
* return data.email && data.email.includes('@');
|
|
13
|
+
* });
|
|
14
|
+
* const tester = createValidationTester(mockValidator);
|
|
15
|
+
*
|
|
16
|
+
* const result = await tester.validate({ email: 'test@example.com' });
|
|
17
|
+
* expect(result.success).toBe(true);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Mock Validator implementation for testing.
|
|
22
|
+
*/
|
|
23
|
+
export class MockValidator {
|
|
24
|
+
validateFn;
|
|
25
|
+
errorMessage;
|
|
26
|
+
constructor(validateFn, errorMessage) {
|
|
27
|
+
this.validateFn = validateFn;
|
|
28
|
+
this.errorMessage = errorMessage;
|
|
29
|
+
}
|
|
30
|
+
async validate(data) {
|
|
31
|
+
const isValid = await this.validateFn(data);
|
|
32
|
+
if (isValid) {
|
|
33
|
+
return {
|
|
34
|
+
success: true,
|
|
35
|
+
errors: [],
|
|
36
|
+
data: data,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
errors: [
|
|
42
|
+
{
|
|
43
|
+
path: '',
|
|
44
|
+
message: this.errorMessage || 'Validation failed',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async parse(data) {
|
|
50
|
+
const result = await this.validate(data);
|
|
51
|
+
if (!result.success) {
|
|
52
|
+
throw new Error(result.errors.map(e => e.message).join(', '));
|
|
53
|
+
}
|
|
54
|
+
return result.data;
|
|
55
|
+
}
|
|
56
|
+
async isValid(data) {
|
|
57
|
+
return await this.validateFn(data);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create a mock Validator.
|
|
62
|
+
*/
|
|
63
|
+
export function createMockValidator(validateFn, errorMessage) {
|
|
64
|
+
return new MockValidator(validateFn, errorMessage);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validation tester utility.
|
|
68
|
+
*/
|
|
69
|
+
export class ValidationTester {
|
|
70
|
+
validator;
|
|
71
|
+
constructor(validator) {
|
|
72
|
+
this.validator = validator;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validate data.
|
|
76
|
+
*/
|
|
77
|
+
async validate(data) {
|
|
78
|
+
return await this.validator.validate(data);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Parse and validate data.
|
|
82
|
+
*/
|
|
83
|
+
async parse(data) {
|
|
84
|
+
return await this.validator.parse(data);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if data is valid.
|
|
88
|
+
*/
|
|
89
|
+
async isValid(data) {
|
|
90
|
+
return await this.validator.isValid(data);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Validate and expect success.
|
|
94
|
+
*/
|
|
95
|
+
async expectSuccess(data) {
|
|
96
|
+
const result = await this.validate(data);
|
|
97
|
+
if (!result.success) {
|
|
98
|
+
throw new Error(`Expected validation success but got errors: ${result.errors.map(e => e.message).join(', ')}`);
|
|
99
|
+
}
|
|
100
|
+
return result.data;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Validate and expect failure.
|
|
104
|
+
*/
|
|
105
|
+
async expectFailure(data) {
|
|
106
|
+
const result = await this.validate(data);
|
|
107
|
+
if (result.success) {
|
|
108
|
+
throw new Error('Expected validation failure but validation succeeded');
|
|
109
|
+
}
|
|
110
|
+
return result.errors;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create a Validation tester instance.
|
|
115
|
+
*/
|
|
116
|
+
export function createValidationTester(validator) {
|
|
117
|
+
return new ValidationTester(validator);
|
|
118
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ValidationTestHelpers';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ValidationTestHelpers';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a Ploc instance for Vue component tests. Same as createMockPloc from core:
|
|
3
|
+
* a Ploc with controllable state and no business logic. Use with mountWithCAF.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { createTestPloc, mountWithCAF } from '@c-a-f/testing/vue';
|
|
8
|
+
*
|
|
9
|
+
* const ploc = createTestPloc({ count: 0 });
|
|
10
|
+
* const wrapper = mountWithCAF(MyComponent, { plocs: { counter: ploc } });
|
|
11
|
+
* ploc.changeState({ count: 1 });
|
|
12
|
+
* expect(wrapper.text()).toContain('1');
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import type { Ploc } from '@c-a-f/core';
|
|
16
|
+
/**
|
|
17
|
+
* Create a test Ploc with initial state. Use changeState() to drive state in tests.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createTestPloc<S>(initialState: S): Ploc<S>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a Ploc instance for Vue component tests. Same as createMockPloc from core:
|
|
3
|
+
* a Ploc with controllable state and no business logic. Use with mountWithCAF.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { createTestPloc, mountWithCAF } from '@c-a-f/testing/vue';
|
|
8
|
+
*
|
|
9
|
+
* const ploc = createTestPloc({ count: 0 });
|
|
10
|
+
* const wrapper = mountWithCAF(MyComponent, { plocs: { counter: ploc } });
|
|
11
|
+
* ploc.changeState({ count: 1 });
|
|
12
|
+
* expect(wrapper.text()).toContain('1');
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import { createMockPloc } from '../core/PlocTestHelpers';
|
|
16
|
+
/**
|
|
17
|
+
* Create a test Ploc with initial state. Use changeState() to drive state in tests.
|
|
18
|
+
*/
|
|
19
|
+
export function createTestPloc(initialState) {
|
|
20
|
+
return createMockPloc(initialState);
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue testing utilities for CAF.
|
|
3
|
+
*
|
|
4
|
+
* - mountWithCAF: mount with CAF context (Ploc/UseCase provide)
|
|
5
|
+
* - createTestPloc: create a test Ploc with controllable state
|
|
6
|
+
* - waitForPlocState: wait for Ploc state to match a predicate
|
|
7
|
+
* - mockUseCase: mock UseCase (success, error, async, fn)
|
|
8
|
+
*/
|
|
9
|
+
export { mountWithCAF, type MountWithCAFOptions } from './mountWithCAF';
|
|
10
|
+
export { createTestPloc } from './createTestPloc';
|
|
11
|
+
export { waitForPlocState } from './waitForPlocState';
|
|
12
|
+
export { mockUseCase } from './mockUseCase';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue testing utilities for CAF.
|
|
3
|
+
*
|
|
4
|
+
* - mountWithCAF: mount with CAF context (Ploc/UseCase provide)
|
|
5
|
+
* - createTestPloc: create a test Ploc with controllable state
|
|
6
|
+
* - waitForPlocState: wait for Ploc state to match a predicate
|
|
7
|
+
* - mockUseCase: mock UseCase (success, error, async, fn)
|
|
8
|
+
*/
|
|
9
|
+
export { mountWithCAF } from './mountWithCAF';
|
|
10
|
+
export { createTestPloc } from './createTestPloc';
|
|
11
|
+
export { waitForPlocState } from './waitForPlocState';
|
|
12
|
+
export { mockUseCase } from './mockUseCase';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers to create mock UseCases for Vue component tests. Use with mountWithCAF.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { mountWithCAF, mockUseCase } from '@c-a-f/testing/vue';
|
|
7
|
+
*
|
|
8
|
+
* const submit = mockUseCase.success({ id: '1' });
|
|
9
|
+
* mountWithCAF(FormComponent, { useCases: { submit } });
|
|
10
|
+
*
|
|
11
|
+
* const load = mockUseCase.error(new Error('Network error'));
|
|
12
|
+
*
|
|
13
|
+
* const search = mockUseCase.fn((query: string) => createSuccessResult([{ id: '1', title: query }]));
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import type { UseCase, RequestResult } from '@c-a-f/core';
|
|
17
|
+
export declare const mockUseCase: {
|
|
18
|
+
/**
|
|
19
|
+
* UseCase that always returns success with the given data.
|
|
20
|
+
*/
|
|
21
|
+
success<T>(data: T): UseCase<[], T>;
|
|
22
|
+
/**
|
|
23
|
+
* UseCase that always returns the given error.
|
|
24
|
+
*/
|
|
25
|
+
error<T_1 = unknown>(error: Error): UseCase<[], T_1>;
|
|
26
|
+
/**
|
|
27
|
+
* UseCase that resolves with data after an optional delay (for loading-state tests).
|
|
28
|
+
*/
|
|
29
|
+
async<T_2>(data: T_2, delayMs?: number): UseCase<[], T_2>;
|
|
30
|
+
/**
|
|
31
|
+
* UseCase with a custom implementation (same as createMockUseCase).
|
|
32
|
+
*/
|
|
33
|
+
fn<A extends any[], T_3>(implementation: (...args: A) => RequestResult<T_3> | Promise<RequestResult<T_3>>): UseCase<A, T_3>;
|
|
34
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers to create mock UseCases for Vue component tests. Use with mountWithCAF.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { mountWithCAF, mockUseCase } from '@c-a-f/testing/vue';
|
|
7
|
+
*
|
|
8
|
+
* const submit = mockUseCase.success({ id: '1' });
|
|
9
|
+
* mountWithCAF(FormComponent, { useCases: { submit } });
|
|
10
|
+
*
|
|
11
|
+
* const load = mockUseCase.error(new Error('Network error'));
|
|
12
|
+
*
|
|
13
|
+
* const search = mockUseCase.fn((query: string) => createSuccessResult([{ id: '1', title: query }]));
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import { createMockUseCase, createMockUseCaseSuccess, createMockUseCaseError, createMockUseCaseAsync, } from '../core/UseCaseTestHelpers';
|
|
17
|
+
export const mockUseCase = {
|
|
18
|
+
/**
|
|
19
|
+
* UseCase that always returns success with the given data.
|
|
20
|
+
*/
|
|
21
|
+
success(data) {
|
|
22
|
+
return createMockUseCaseSuccess(data);
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* UseCase that always returns the given error.
|
|
26
|
+
*/
|
|
27
|
+
error(error) {
|
|
28
|
+
return createMockUseCaseError(error);
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* UseCase that resolves with data after an optional delay (for loading-state tests).
|
|
32
|
+
*/
|
|
33
|
+
async(data, delayMs = 0) {
|
|
34
|
+
return createMockUseCaseAsync(data, delayMs);
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* UseCase with a custom implementation (same as createMockUseCase).
|
|
38
|
+
*/
|
|
39
|
+
fn(implementation) {
|
|
40
|
+
return createMockUseCase(implementation);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount a Vue component with CAF context (Plocs/UseCases) so that usePlocFromContext
|
|
3
|
+
* and useUseCaseFromContext work. Use this instead of raw mount() when testing
|
|
4
|
+
* components that depend on CAF context.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { mountWithCAF, createTestPloc, mockUseCase } from '@c-a-f/testing/vue';
|
|
9
|
+
* import { mount } from '@vue/test-utils';
|
|
10
|
+
*
|
|
11
|
+
* const ploc = createTestPloc({ count: 0 });
|
|
12
|
+
* const wrapper = mountWithCAF(MyComponent, {
|
|
13
|
+
* plocs: { counter: ploc },
|
|
14
|
+
* useCases: { submit: mockUseCase.success({ id: '1' }) },
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { Component } from 'vue';
|
|
19
|
+
import { type MountingOptions, type VueWrapper } from '@vue/test-utils';
|
|
20
|
+
import type { Ploc, UseCase } from '@c-a-f/core';
|
|
21
|
+
export interface MountWithCAFOptions<Props = unknown> extends MountingOptions<Props> {
|
|
22
|
+
/** Plocs to provide (keyed by string). */
|
|
23
|
+
plocs?: Record<string, Ploc<unknown>>;
|
|
24
|
+
/** UseCases to provide (keyed by string). */
|
|
25
|
+
useCases?: Record<string, UseCase<any[], any>>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Mount a component with CAF context so Ploc/UseCase inject is available.
|
|
29
|
+
* Returns the same VueWrapper as @vue/test-utils mount().
|
|
30
|
+
*/
|
|
31
|
+
export declare function mountWithCAF<ComponentPublicInstance = unknown>(component: Component, options?: MountWithCAFOptions): VueWrapper<ComponentPublicInstance>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount a Vue component with CAF context (Plocs/UseCases) so that usePlocFromContext
|
|
3
|
+
* and useUseCaseFromContext work. Use this instead of raw mount() when testing
|
|
4
|
+
* components that depend on CAF context.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { mountWithCAF, createTestPloc, mockUseCase } from '@c-a-f/testing/vue';
|
|
9
|
+
* import { mount } from '@vue/test-utils';
|
|
10
|
+
*
|
|
11
|
+
* const ploc = createTestPloc({ count: 0 });
|
|
12
|
+
* const wrapper = mountWithCAF(MyComponent, {
|
|
13
|
+
* plocs: { counter: ploc },
|
|
14
|
+
* useCases: { submit: mockUseCase.success({ id: '1' }) },
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { mount } from '@vue/test-utils';
|
|
19
|
+
import { CAFContextKey } from '@c-a-f/infrastructure-vue';
|
|
20
|
+
/**
|
|
21
|
+
* Mount a component with CAF context so Ploc/UseCase inject is available.
|
|
22
|
+
* Returns the same VueWrapper as @vue/test-utils mount().
|
|
23
|
+
*/
|
|
24
|
+
export function mountWithCAF(component, options = {}) {
|
|
25
|
+
const { plocs, useCases, global: globalOptions, ...rest } = options;
|
|
26
|
+
const cafValue = {
|
|
27
|
+
plocs: plocs ?? {},
|
|
28
|
+
useCases: useCases ?? {},
|
|
29
|
+
};
|
|
30
|
+
return mount(component, {
|
|
31
|
+
...rest,
|
|
32
|
+
global: {
|
|
33
|
+
...globalOptions,
|
|
34
|
+
provide: {
|
|
35
|
+
...globalOptions?.provide,
|
|
36
|
+
[CAFContextKey]: cafValue,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|