@frontmcp/sdk 0.3.1 → 0.4.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/LICENSE +201 -0
- package/README.md +192 -164
- package/package.json +7 -4
- package/src/__test-utils__/fixtures/hook.fixtures.d.ts +46 -0
- package/src/__test-utils__/fixtures/hook.fixtures.js +114 -0
- package/src/__test-utils__/fixtures/hook.fixtures.js.map +1 -0
- package/src/__test-utils__/fixtures/index.d.ts +7 -0
- package/src/__test-utils__/fixtures/index.js +11 -0
- package/src/__test-utils__/fixtures/index.js.map +1 -0
- package/src/__test-utils__/fixtures/plugin.fixtures.d.ts +46 -0
- package/src/__test-utils__/fixtures/plugin.fixtures.js +127 -0
- package/src/__test-utils__/fixtures/plugin.fixtures.js.map +1 -0
- package/src/__test-utils__/fixtures/provider.fixtures.d.ts +69 -0
- package/src/__test-utils__/fixtures/provider.fixtures.js +131 -0
- package/src/__test-utils__/fixtures/provider.fixtures.js.map +1 -0
- package/src/__test-utils__/fixtures/scope.fixtures.d.ts +14 -0
- package/src/__test-utils__/fixtures/scope.fixtures.js +59 -0
- package/src/__test-utils__/fixtures/scope.fixtures.js.map +1 -0
- package/src/__test-utils__/fixtures/tool.fixtures.d.ts +36 -0
- package/src/__test-utils__/fixtures/tool.fixtures.js +91 -0
- package/src/__test-utils__/fixtures/tool.fixtures.js.map +1 -0
- package/src/__test-utils__/helpers/assertion.helpers.d.ts +45 -0
- package/src/__test-utils__/helpers/assertion.helpers.js +153 -0
- package/src/__test-utils__/helpers/assertion.helpers.js.map +1 -0
- package/src/__test-utils__/helpers/async.helpers.d.ts +48 -0
- package/src/__test-utils__/helpers/async.helpers.js +112 -0
- package/src/__test-utils__/helpers/async.helpers.js.map +1 -0
- package/src/__test-utils__/helpers/index.d.ts +6 -0
- package/src/__test-utils__/helpers/index.js +10 -0
- package/src/__test-utils__/helpers/index.js.map +1 -0
- package/src/__test-utils__/helpers/setup.helpers.d.ts +54 -0
- package/src/__test-utils__/helpers/setup.helpers.js +106 -0
- package/src/__test-utils__/helpers/setup.helpers.js.map +1 -0
- package/src/__test-utils__/index.d.ts +9 -0
- package/src/__test-utils__/index.js +14 -0
- package/src/__test-utils__/index.js.map +1 -0
- package/src/__test-utils__/mocks/flow-instance.mock.d.ts +50 -0
- package/src/__test-utils__/mocks/flow-instance.mock.js +72 -0
- package/src/__test-utils__/mocks/flow-instance.mock.js.map +1 -0
- package/src/__test-utils__/mocks/hook-registry.mock.d.ts +25 -0
- package/src/__test-utils__/mocks/hook-registry.mock.js +65 -0
- package/src/__test-utils__/mocks/hook-registry.mock.js.map +1 -0
- package/src/__test-utils__/mocks/index.d.ts +8 -0
- package/src/__test-utils__/mocks/index.js +12 -0
- package/src/__test-utils__/mocks/index.js.map +1 -0
- package/src/__test-utils__/mocks/plugin-registry.mock.d.ts +43 -0
- package/src/__test-utils__/mocks/plugin-registry.mock.js +70 -0
- package/src/__test-utils__/mocks/plugin-registry.mock.js.map +1 -0
- package/src/__test-utils__/mocks/provider-registry.mock.d.ts +39 -0
- package/src/__test-utils__/mocks/provider-registry.mock.js +72 -0
- package/src/__test-utils__/mocks/provider-registry.mock.js.map +1 -0
- package/src/__test-utils__/mocks/tool-registry.mock.d.ts +43 -0
- package/src/__test-utils__/mocks/tool-registry.mock.js +79 -0
- package/src/__test-utils__/mocks/tool-registry.mock.js.map +1 -0
- package/src/app/app.utils.js.map +1 -1
- package/src/app/instances/app.local.instance.js +8 -11
- package/src/app/instances/app.local.instance.js.map +1 -1
- package/src/auth/flows/oauth.authorize.flow.d.ts +8 -8
- package/src/auth/flows/oauth.register.flow.d.ts +4 -4
- package/src/auth/flows/oauth.token.flow.d.ts +4 -4
- package/src/auth/flows/well-known.jwks.flow.d.ts +12 -12
- package/src/auth/flows/well-known.oauth-authorization-server.flow.d.ts +8 -8
- package/src/auth/flows/well-known.prm.flow.d.ts +4 -4
- package/src/common/decorators/tool.decorator.d.ts +97 -36
- package/src/common/decorators/tool.decorator.js +0 -1
- package/src/common/decorators/tool.decorator.js.map +1 -1
- package/src/common/entries/tool.entry.d.ts +54 -11
- package/src/common/entries/tool.entry.js +19 -0
- package/src/common/entries/tool.entry.js.map +1 -1
- package/src/common/interfaces/internal/registry.interface.d.ts +10 -2
- package/src/common/interfaces/internal/registry.interface.js.map +1 -1
- package/src/common/interfaces/plugin.interface.d.ts +1 -1
- package/src/common/interfaces/plugin.interface.js.map +1 -1
- package/src/common/interfaces/tool.interface.d.ts +12 -7
- package/src/common/interfaces/tool.interface.js +1 -1
- package/src/common/interfaces/tool.interface.js.map +1 -1
- package/src/common/metadata/front-mcp.metadata.d.ts +145 -145
- package/src/common/metadata/hook.metadata.d.ts +4 -2
- package/src/common/metadata/hook.metadata.js.map +1 -1
- package/src/common/metadata/prompt.metadata.d.ts +28 -28
- package/src/common/metadata/prompt.metadata.js.map +1 -1
- package/src/common/metadata/resource.metadata.d.ts +54 -54
- package/src/common/metadata/tool.metadata.d.ts +190 -7
- package/src/common/metadata/tool.metadata.js +41 -6
- package/src/common/metadata/tool.metadata.js.map +1 -1
- package/src/common/schemas/http-output.schema.d.ts +106 -106
- package/src/common/tokens/tool.tokens.js.map +1 -1
- package/src/common/types/options/logging.options.d.ts +1 -2
- package/src/common/types/options/logging.options.js +1 -9
- package/src/common/types/options/logging.options.js.map +1 -1
- package/src/common/types/options/server-info.options.d.ts +19 -19
- package/src/errors/error-handler.d.ts +65 -0
- package/src/errors/error-handler.js +107 -0
- package/src/errors/error-handler.js.map +1 -0
- package/src/errors/index.d.ts +2 -0
- package/src/errors/index.js +26 -0
- package/src/errors/index.js.map +1 -0
- package/src/errors/mcp.error.d.ts +156 -0
- package/src/errors/mcp.error.js +243 -0
- package/src/errors/mcp.error.js.map +1 -0
- package/src/flows/flow.instance.js +7 -6
- package/src/flows/flow.instance.js.map +1 -1
- package/src/flows/flow.registry.js +1 -1
- package/src/flows/flow.registry.js.map +1 -1
- package/src/front-mcp/front-mcp.providers.d.ts +20 -20
- package/src/hooks/hook.registry.d.ts +5 -3
- package/src/hooks/hook.registry.js +13 -1
- package/src/hooks/hook.registry.js.map +1 -1
- package/src/plugin/plugin.registry.d.ts +7 -2
- package/src/plugin/plugin.registry.js +23 -11
- package/src/plugin/plugin.registry.js.map +1 -1
- package/src/prompt/prompt.registry.js +1 -0
- package/src/prompt/prompt.registry.js.map +1 -1
- package/src/resource/resource.registry.js +1 -0
- package/src/resource/resource.registry.js.map +1 -1
- package/src/scope/scope.registry.js +1 -1
- package/src/scope/scope.registry.js.map +1 -1
- package/src/store/adapters/store.memory.adapter.js +3 -1
- package/src/store/adapters/store.memory.adapter.js.map +1 -1
- package/src/tool/flows/call-tool.flow.d.ts +1012 -676
- package/src/tool/flows/call-tool.flow.js +94 -61
- package/src/tool/flows/call-tool.flow.js.map +1 -1
- package/src/tool/flows/tools-list.flow.d.ts +347 -590
- package/src/tool/flows/tools-list.flow.js +76 -49
- package/src/tool/flows/tools-list.flow.js.map +1 -1
- package/src/tool/tool.instance.d.ts +27 -8
- package/src/tool/tool.instance.js +40 -5
- package/src/tool/tool.instance.js.map +1 -1
- package/src/tool/tool.registry.js +19 -21
- package/src/tool/tool.registry.js.map +1 -1
- package/src/tool/tool.utils.d.ts +3 -2
- package/src/tool/tool.utils.js +377 -14
- package/src/tool/tool.utils.js.map +1 -1
- package/src/transport/adapters/transport.sse.adapter.js.map +1 -1
- package/src/transport/adapters/transport.streamable-http.adapter.js.map +1 -1
- package/src/transport/flows/handle.sse.flow.js +6 -13
- package/src/transport/flows/handle.sse.flow.js.map +1 -1
- package/src/transport/flows/handle.streamable-http.flow.js +1 -0
- package/src/transport/flows/handle.streamable-http.flow.js.map +1 -1
- package/src/transport/mcp-handlers/call-tool-request.handler.d.ts +1 -1
- package/src/transport/mcp-handlers/call-tool-request.handler.js +10 -5
- package/src/transport/mcp-handlers/call-tool-request.handler.js.map +1 -1
- package/src/transport/mcp-handlers/index.d.ts +151 -268
- package/src/transport/mcp-handlers/list-tools-request.handler.d.ts +124 -216
- package/src/transport/transport.local.js +1 -0
- package/src/transport/transport.local.js.map +1 -1
- package/src/utils/string.utils.js +1 -1
- package/src/utils/string.utils.js.map +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="jest" />
|
|
3
|
+
/**
|
|
4
|
+
* Test fixtures for tools
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MockToolClass = void 0;
|
|
8
|
+
exports.createToolMetadata = createToolMetadata;
|
|
9
|
+
exports.createEchoToolMetadata = createEchoToolMetadata;
|
|
10
|
+
exports.createCalculatorToolMetadata = createCalculatorToolMetadata;
|
|
11
|
+
exports.createStructuredOutputToolMetadata = createStructuredOutputToolMetadata;
|
|
12
|
+
exports.createMockToolInstance = createMockToolInstance;
|
|
13
|
+
const zod_1 = require("zod");
|
|
14
|
+
/**
|
|
15
|
+
* Simple test tool metadata
|
|
16
|
+
*/
|
|
17
|
+
function createToolMetadata(overrides = {}) {
|
|
18
|
+
return {
|
|
19
|
+
name: 'test_tool',
|
|
20
|
+
description: 'A test tool',
|
|
21
|
+
inputSchema: {
|
|
22
|
+
text: zod_1.z.string(),
|
|
23
|
+
},
|
|
24
|
+
outputSchema: 'string',
|
|
25
|
+
...overrides,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates a simple echo tool metadata
|
|
30
|
+
*/
|
|
31
|
+
function createEchoToolMetadata() {
|
|
32
|
+
return {
|
|
33
|
+
name: 'echo',
|
|
34
|
+
description: 'Echoes back the input',
|
|
35
|
+
inputSchema: {
|
|
36
|
+
message: zod_1.z.string().describe('Message to echo'),
|
|
37
|
+
},
|
|
38
|
+
outputSchema: 'string',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates a calculator tool metadata
|
|
43
|
+
*/
|
|
44
|
+
function createCalculatorToolMetadata() {
|
|
45
|
+
return {
|
|
46
|
+
name: 'calculator',
|
|
47
|
+
description: 'Performs basic arithmetic',
|
|
48
|
+
inputSchema: {
|
|
49
|
+
operation: zod_1.z.enum(['add', 'subtract', 'multiply', 'divide']),
|
|
50
|
+
a: zod_1.z.number(),
|
|
51
|
+
b: zod_1.z.number(),
|
|
52
|
+
},
|
|
53
|
+
outputSchema: 'number',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Creates a tool with structured output
|
|
58
|
+
*/
|
|
59
|
+
function createStructuredOutputToolMetadata() {
|
|
60
|
+
return {
|
|
61
|
+
name: 'get_user',
|
|
62
|
+
description: 'Gets user information',
|
|
63
|
+
inputSchema: {
|
|
64
|
+
userId: zod_1.z.string(),
|
|
65
|
+
},
|
|
66
|
+
outputSchema: zod_1.z.object({
|
|
67
|
+
id: zod_1.z.string(),
|
|
68
|
+
name: zod_1.z.string(),
|
|
69
|
+
email: zod_1.z.string().email(),
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Mock tool class for testing
|
|
75
|
+
*/
|
|
76
|
+
class MockToolClass {
|
|
77
|
+
async execute(input) {
|
|
78
|
+
return `Echo: ${input.text}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.MockToolClass = MockToolClass;
|
|
82
|
+
/**
|
|
83
|
+
* Mock tool instance for testing
|
|
84
|
+
*/
|
|
85
|
+
function createMockToolInstance(metadata) {
|
|
86
|
+
return {
|
|
87
|
+
metadata: createToolMetadata(metadata),
|
|
88
|
+
execute: jest.fn(async (input) => `Result: ${JSON.stringify(input)}`),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=tool.fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.fixtures.js","sourceRoot":"","sources":["../../../../src/__test-utils__/fixtures/tool.fixtures.ts"],"names":[],"mappings":";AAAA,8BAA8B;AAC9B;;GAEG;;;AAQH,gDAUC;AAKD,wDASC;AAKD,oEAWC;AAKD,gFAaC;AAcD,wDAKC;AAnFD,6BAAwB;AAGxB;;GAEG;AACH,SAAgB,kBAAkB,CAAC,YAAmC,EAAE;IACtE,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,aAAa;QAC1B,WAAW,EAAE;YACX,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE;SACjB;QACD,YAAY,EAAE,QAAQ;QACtB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,sBAAsB;IACpC,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,uBAAuB;QACpC,WAAW,EAAE;YACX,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;SAChD;QACD,YAAY,EAAE,QAAQ;KACvB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,4BAA4B;IAC1C,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,2BAA2B;QACxC,WAAW,EAAE;YACX,SAAS,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC5D,CAAC,EAAE,OAAC,CAAC,MAAM,EAAE;YACb,CAAC,EAAE,OAAC,CAAC,MAAM,EAAE;SACd;QACD,YAAY,EAAE,QAAQ;KACvB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,kCAAkC;IAChD,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,uBAAuB;QACpC,WAAW,EAAE;YACX,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE;SACnB;QACD,YAAY,EAAE,OAAC,CAAC,MAAM,CAAC;YACrB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE;YACd,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE;YAChB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;SAC1B,CAAC;KACH,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAa,aAAa;IACxB,KAAK,CAAC,OAAO,CAAC,KAAuB;QACnC,OAAO,SAAS,KAAK,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;CACF;AAJD,sCAIC;AAED;;GAEG;AACH,SAAgB,sBAAsB,CAAC,QAAgC;IACrE,OAAO;QACL,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,CAAC;QACtC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,KAAU,EAAE,EAAE,CAAC,WAAW,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;KAC3E,CAAC;AACJ,CAAC","sourcesContent":["/// <reference types=\"jest\" />\n/**\n * Test fixtures for tools\n */\n\nimport { z } from 'zod';\nimport { ToolMetadata } from '../../common/metadata';\n\n/**\n * Simple test tool metadata\n */\nexport function createToolMetadata(overrides: Partial<ToolMetadata> = {}): ToolMetadata {\n return {\n name: 'test_tool',\n description: 'A test tool',\n inputSchema: {\n text: z.string(),\n },\n outputSchema: 'string',\n ...overrides,\n };\n}\n\n/**\n * Creates a simple echo tool metadata\n */\nexport function createEchoToolMetadata(): ToolMetadata {\n return {\n name: 'echo',\n description: 'Echoes back the input',\n inputSchema: {\n message: z.string().describe('Message to echo'),\n },\n outputSchema: 'string',\n };\n}\n\n/**\n * Creates a calculator tool metadata\n */\nexport function createCalculatorToolMetadata(): ToolMetadata {\n return {\n name: 'calculator',\n description: 'Performs basic arithmetic',\n inputSchema: {\n operation: z.enum(['add', 'subtract', 'multiply', 'divide']),\n a: z.number(),\n b: z.number(),\n },\n outputSchema: 'number',\n };\n}\n\n/**\n * Creates a tool with structured output\n */\nexport function createStructuredOutputToolMetadata(): ToolMetadata {\n return {\n name: 'get_user',\n description: 'Gets user information',\n inputSchema: {\n userId: z.string(),\n },\n outputSchema: z.object({\n id: z.string(),\n name: z.string(),\n email: z.string().email(),\n }),\n };\n}\n\n/**\n * Mock tool class for testing\n */\nexport class MockToolClass {\n async execute(input: { text: string }): Promise<string> {\n return `Echo: ${input.text}`;\n }\n}\n\n/**\n * Mock tool instance for testing\n */\nexport function createMockToolInstance(metadata?: Partial<ToolMetadata>) {\n return {\n metadata: createToolMetadata(metadata),\n execute: jest.fn(async (input: any) => `Result: ${JSON.stringify(input)}`),\n };\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom assertion helpers for testing SDK components
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Asserts that a value is defined (not null or undefined)
|
|
6
|
+
*/
|
|
7
|
+
export declare function assertDefined<T>(value: T | null | undefined, message?: string): asserts value is T;
|
|
8
|
+
/**
|
|
9
|
+
* Asserts that a value is an instance of a class
|
|
10
|
+
*/
|
|
11
|
+
export declare function assertInstanceOf<T>(value: any, Ctor: new (...args: any[]) => T, message?: string): asserts value is T;
|
|
12
|
+
/**
|
|
13
|
+
* Asserts that an array contains a specific item
|
|
14
|
+
*/
|
|
15
|
+
export declare function assertContains<T>(array: T[], item: T, message?: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Asserts that two arrays have the same elements with the same counts (order doesn't matter)
|
|
18
|
+
*/
|
|
19
|
+
export declare function assertSameElements<T>(actual: T[], expected: T[], message?: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Asserts that a map has a specific key
|
|
22
|
+
*/
|
|
23
|
+
export declare function assertHasKey<K, V>(map: Map<K, V>, key: K, message?: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Asserts that a promise rejects with a specific error
|
|
26
|
+
*/
|
|
27
|
+
export declare function assertRejects(promise: Promise<any>, expectedError?: string | RegExp | (new (...args: any[]) => Error)): Promise<Error>;
|
|
28
|
+
/**
|
|
29
|
+
* Asserts that a promise resolves to a specific value
|
|
30
|
+
*/
|
|
31
|
+
export declare function assertResolves<T>(promise: Promise<T>, expectedValue?: T): Promise<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Creates a spy that captures console output
|
|
34
|
+
*/
|
|
35
|
+
export declare function spyConsole(): {
|
|
36
|
+
logs: string[];
|
|
37
|
+
warns: string[];
|
|
38
|
+
errors: string[];
|
|
39
|
+
infos: string[];
|
|
40
|
+
restore: () => void;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Suppresses console output during test execution
|
|
44
|
+
*/
|
|
45
|
+
export declare function suppressConsole(): () => void;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="jest" />
|
|
3
|
+
/**
|
|
4
|
+
* Custom assertion helpers for testing SDK components
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.assertDefined = assertDefined;
|
|
8
|
+
exports.assertInstanceOf = assertInstanceOf;
|
|
9
|
+
exports.assertContains = assertContains;
|
|
10
|
+
exports.assertSameElements = assertSameElements;
|
|
11
|
+
exports.assertHasKey = assertHasKey;
|
|
12
|
+
exports.assertRejects = assertRejects;
|
|
13
|
+
exports.assertResolves = assertResolves;
|
|
14
|
+
exports.spyConsole = spyConsole;
|
|
15
|
+
exports.suppressConsole = suppressConsole;
|
|
16
|
+
const NO_EXPECTED = Symbol('assertResolves.noExpected');
|
|
17
|
+
/**
|
|
18
|
+
* Asserts that a value is defined (not null or undefined)
|
|
19
|
+
*/
|
|
20
|
+
function assertDefined(value, message) {
|
|
21
|
+
if (value === null || value === undefined) {
|
|
22
|
+
throw new Error(message || 'Expected value to be defined');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Asserts that a value is an instance of a class
|
|
27
|
+
*/
|
|
28
|
+
function assertInstanceOf(value, Ctor, message) {
|
|
29
|
+
if (!(value instanceof Ctor)) {
|
|
30
|
+
throw new Error(message ||
|
|
31
|
+
`Expected value to be an instance of ${Ctor.name}, but got ${value?.constructor?.name || typeof value}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Asserts that an array contains a specific item
|
|
36
|
+
*/
|
|
37
|
+
function assertContains(array, item, message) {
|
|
38
|
+
if (!array.includes(item)) {
|
|
39
|
+
throw new Error(message || `Expected array to contain item`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Asserts that two arrays have the same elements with the same counts (order doesn't matter)
|
|
44
|
+
*/
|
|
45
|
+
function assertSameElements(actual, expected, message) {
|
|
46
|
+
if (actual.length !== expected.length) {
|
|
47
|
+
throw new Error(message || `Expected arrays to have same length: ${actual.length} vs ${expected.length}`);
|
|
48
|
+
}
|
|
49
|
+
// Count occurrences of each element in both arrays
|
|
50
|
+
const actualCounts = new Map();
|
|
51
|
+
const expectedCounts = new Map();
|
|
52
|
+
for (const item of actual) {
|
|
53
|
+
actualCounts.set(item, (actualCounts.get(item) || 0) + 1);
|
|
54
|
+
}
|
|
55
|
+
for (const item of expected) {
|
|
56
|
+
expectedCounts.set(item, (expectedCounts.get(item) || 0) + 1);
|
|
57
|
+
}
|
|
58
|
+
// Check that all expected items exist with the correct counts
|
|
59
|
+
for (const [item, count] of expectedCounts) {
|
|
60
|
+
const actualCount = actualCounts.get(item) || 0;
|
|
61
|
+
if (actualCount !== count) {
|
|
62
|
+
throw new Error(message || `Expected array to contain ${count} occurrence(s) of ${item}, but found ${actualCount}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Check that actual doesn't have extra items
|
|
66
|
+
for (const [item, count] of actualCounts) {
|
|
67
|
+
const expectedCount = expectedCounts.get(item) || 0;
|
|
68
|
+
if (expectedCount !== count) {
|
|
69
|
+
throw new Error(message || `Expected array to contain ${expectedCount} occurrence(s) of ${item}, but found ${count}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Asserts that a map has a specific key
|
|
75
|
+
*/
|
|
76
|
+
function assertHasKey(map, key, message) {
|
|
77
|
+
if (!map.has(key)) {
|
|
78
|
+
throw new Error(message || `Expected map to have key`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Asserts that a promise rejects with a specific error
|
|
83
|
+
*/
|
|
84
|
+
async function assertRejects(promise, expectedError) {
|
|
85
|
+
try {
|
|
86
|
+
await promise;
|
|
87
|
+
throw new Error('Expected promise to reject, but it resolved');
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (expectedError) {
|
|
91
|
+
if (typeof expectedError === 'string') {
|
|
92
|
+
expect(error.message).toContain(expectedError);
|
|
93
|
+
}
|
|
94
|
+
else if (expectedError instanceof RegExp) {
|
|
95
|
+
expect(error.message).toMatch(expectedError);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
expect(error).toBeInstanceOf(expectedError);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Asserts that a promise resolves to a specific value
|
|
106
|
+
*/
|
|
107
|
+
async function assertResolves(promise, expectedValue) {
|
|
108
|
+
const value = await promise;
|
|
109
|
+
const finalExpected = arguments.length > 1 ? expectedValue : NO_EXPECTED;
|
|
110
|
+
if (finalExpected !== NO_EXPECTED) {
|
|
111
|
+
expect(value).toEqual(finalExpected);
|
|
112
|
+
}
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Creates a spy that captures console output
|
|
117
|
+
*/
|
|
118
|
+
function spyConsole() {
|
|
119
|
+
const originalConsole = {
|
|
120
|
+
log: console.log,
|
|
121
|
+
warn: console.warn,
|
|
122
|
+
error: console.error,
|
|
123
|
+
info: console.info,
|
|
124
|
+
};
|
|
125
|
+
const logs = [];
|
|
126
|
+
const warns = [];
|
|
127
|
+
const errors = [];
|
|
128
|
+
const infos = [];
|
|
129
|
+
console.log = jest.fn((...args) => logs.push(args.join(' ')));
|
|
130
|
+
console.warn = jest.fn((...args) => warns.push(args.join(' ')));
|
|
131
|
+
console.error = jest.fn((...args) => errors.push(args.join(' ')));
|
|
132
|
+
console.info = jest.fn((...args) => infos.push(args.join(' ')));
|
|
133
|
+
return {
|
|
134
|
+
logs,
|
|
135
|
+
warns,
|
|
136
|
+
errors,
|
|
137
|
+
infos,
|
|
138
|
+
restore: () => {
|
|
139
|
+
console.log = originalConsole.log;
|
|
140
|
+
console.warn = originalConsole.warn;
|
|
141
|
+
console.error = originalConsole.error;
|
|
142
|
+
console.info = originalConsole.info;
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Suppresses console output during test execution
|
|
148
|
+
*/
|
|
149
|
+
function suppressConsole() {
|
|
150
|
+
const spy = spyConsole();
|
|
151
|
+
return spy.restore;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=assertion.helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertion.helpers.js","sourceRoot":"","sources":["../../../../src/__test-utils__/helpers/assertion.helpers.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"jest\" />\n/**\n * Custom assertion helpers for testing SDK components\n */\n\nconst NO_EXPECTED = Symbol('assertResolves.noExpected');\n\n/**\n * Asserts that a value is defined (not null or undefined)\n */\nexport function assertDefined<T>(value: T | null | undefined, message?: string): asserts value is T {\n if (value === null || value === undefined) {\n throw new Error(message || 'Expected value to be defined');\n }\n}\n\n/**\n * Asserts that a value is an instance of a class\n */\nexport function assertInstanceOf<T>(value: any, Ctor: new (...args: any[]) => T, message?: string): asserts value is T {\n if (!(value instanceof Ctor)) {\n throw new Error(\n message ||\n `Expected value to be an instance of ${Ctor.name}, but got ${value?.constructor?.name || typeof value}`,\n );\n }\n}\n\n/**\n * Asserts that an array contains a specific item\n */\nexport function assertContains<T>(array: T[], item: T, message?: string): void {\n if (!array.includes(item)) {\n throw new Error(message || `Expected array to contain item`);\n }\n}\n\n/**\n * Asserts that two arrays have the same elements with the same counts (order doesn't matter)\n */\nexport function assertSameElements<T>(actual: T[], expected: T[], message?: string): void {\n if (actual.length !== expected.length) {\n throw new Error(message || `Expected arrays to have same length: ${actual.length} vs ${expected.length}`);\n }\n\n // Count occurrences of each element in both arrays\n const actualCounts = new Map<T, number>();\n const expectedCounts = new Map<T, number>();\n\n for (const item of actual) {\n actualCounts.set(item, (actualCounts.get(item) || 0) + 1);\n }\n\n for (const item of expected) {\n expectedCounts.set(item, (expectedCounts.get(item) || 0) + 1);\n }\n\n // Check that all expected items exist with the correct counts\n for (const [item, count] of expectedCounts) {\n const actualCount = actualCounts.get(item) || 0;\n if (actualCount !== count) {\n throw new Error(\n message || `Expected array to contain ${count} occurrence(s) of ${item}, but found ${actualCount}`,\n );\n }\n }\n\n // Check that actual doesn't have extra items\n for (const [item, count] of actualCounts) {\n const expectedCount = expectedCounts.get(item) || 0;\n if (expectedCount !== count) {\n throw new Error(\n message || `Expected array to contain ${expectedCount} occurrence(s) of ${item}, but found ${count}`,\n );\n }\n }\n}\n\n/**\n * Asserts that a map has a specific key\n */\nexport function assertHasKey<K, V>(map: Map<K, V>, key: K, message?: string): void {\n if (!map.has(key)) {\n throw new Error(message || `Expected map to have key`);\n }\n}\n\n/**\n * Asserts that a promise rejects with a specific error\n */\nexport async function assertRejects(\n promise: Promise<any>,\n expectedError?: string | RegExp | (new (...args: any[]) => Error),\n): Promise<Error> {\n try {\n await promise;\n throw new Error('Expected promise to reject, but it resolved');\n } catch (error) {\n if (expectedError) {\n if (typeof expectedError === 'string') {\n expect((error as Error).message).toContain(expectedError);\n } else if (expectedError instanceof RegExp) {\n expect((error as Error).message).toMatch(expectedError);\n } else {\n expect(error).toBeInstanceOf(expectedError);\n }\n }\n return error as Error;\n }\n}\n\n/**\n * Asserts that a promise resolves to a specific value\n */\nexport async function assertResolves<T>(promise: Promise<T>, expectedValue?: T): Promise<T> {\n const value = await promise;\n const finalExpected: T | typeof NO_EXPECTED = arguments.length > 1 ? (expectedValue as T) : NO_EXPECTED;\n\n if (finalExpected !== NO_EXPECTED) {\n expect(value).toEqual(finalExpected as T);\n }\n return value;\n}\n\n/**\n * Creates a spy that captures console output\n */\nexport function spyConsole() {\n const originalConsole = {\n log: console.log,\n warn: console.warn,\n error: console.error,\n info: console.info,\n };\n\n const logs: string[] = [];\n const warns: string[] = [];\n const errors: string[] = [];\n const infos: string[] = [];\n\n console.log = jest.fn((...args) => logs.push(args.join(' ')));\n console.warn = jest.fn((...args) => warns.push(args.join(' ')));\n console.error = jest.fn((...args) => errors.push(args.join(' ')));\n console.info = jest.fn((...args) => infos.push(args.join(' ')));\n\n return {\n logs,\n warns,\n errors,\n infos,\n restore: () => {\n console.log = originalConsole.log;\n console.warn = originalConsole.warn;\n console.error = originalConsole.error;\n console.info = originalConsole.info;\n },\n };\n}\n\n/**\n * Suppresses console output during test execution\n */\nexport function suppressConsole() {\n const spy = spyConsole();\n return spy.restore;\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper utilities for testing asynchronous operations
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Waits for a promise to settle and returns the result or error
|
|
6
|
+
*/
|
|
7
|
+
export declare function settled<T>(promise: Promise<T>): Promise<{
|
|
8
|
+
status: 'fulfilled';
|
|
9
|
+
value: T;
|
|
10
|
+
} | {
|
|
11
|
+
status: 'rejected';
|
|
12
|
+
reason: any;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Waits for a condition to be true with a timeout
|
|
16
|
+
*/
|
|
17
|
+
export declare function waitFor(condition: () => boolean | Promise<boolean>, options?: {
|
|
18
|
+
timeout?: number;
|
|
19
|
+
interval?: number;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Flushes all pending promises (useful for testing async flows)
|
|
23
|
+
*/
|
|
24
|
+
export declare function flushPromises(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Creates a deferred promise that can be resolved/rejected externally
|
|
27
|
+
*/
|
|
28
|
+
export declare function createDeferred<T>(): {
|
|
29
|
+
promise: Promise<T>;
|
|
30
|
+
resolve: (value: T) => void;
|
|
31
|
+
reject: (reason: any) => void;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Runs a function and expects it to throw an error
|
|
35
|
+
*/
|
|
36
|
+
export declare function expectToThrow<T>(fn: () => T | Promise<T>, expectedError?: string | RegExp | (new (...args: any[]) => Error)): Promise<Error>;
|
|
37
|
+
/**
|
|
38
|
+
* Creates a mock function that tracks calls
|
|
39
|
+
*/
|
|
40
|
+
export declare function createMockFn<T extends (...args: any[]) => any>(): {
|
|
41
|
+
fn: jest.Mock<ReturnType<T>, Parameters<T>, any>;
|
|
42
|
+
calls: {
|
|
43
|
+
args: Parameters<T>;
|
|
44
|
+
result?: ReturnType<T>;
|
|
45
|
+
error?: any;
|
|
46
|
+
}[];
|
|
47
|
+
reset: () => void;
|
|
48
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="jest" />
|
|
3
|
+
/**
|
|
4
|
+
* Helper utilities for testing asynchronous operations
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.settled = settled;
|
|
8
|
+
exports.waitFor = waitFor;
|
|
9
|
+
exports.flushPromises = flushPromises;
|
|
10
|
+
exports.createDeferred = createDeferred;
|
|
11
|
+
exports.expectToThrow = expectToThrow;
|
|
12
|
+
exports.createMockFn = createMockFn;
|
|
13
|
+
/**
|
|
14
|
+
* Waits for a promise to settle and returns the result or error
|
|
15
|
+
*/
|
|
16
|
+
async function settled(promise) {
|
|
17
|
+
try {
|
|
18
|
+
const value = await promise;
|
|
19
|
+
return { status: 'fulfilled', value };
|
|
20
|
+
}
|
|
21
|
+
catch (reason) {
|
|
22
|
+
return { status: 'rejected', reason };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Waits for a condition to be true with a timeout
|
|
27
|
+
*/
|
|
28
|
+
async function waitFor(condition, options = {}) {
|
|
29
|
+
const { timeout = 5000, interval = 50 } = options;
|
|
30
|
+
const start = Date.now();
|
|
31
|
+
while (Date.now() - start < timeout) {
|
|
32
|
+
if (await condition()) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`Timeout waiting for condition after ${timeout}ms`);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Flushes all pending promises (useful for testing async flows)
|
|
41
|
+
*/
|
|
42
|
+
async function flushPromises() {
|
|
43
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Creates a deferred promise that can be resolved/rejected externally
|
|
47
|
+
*/
|
|
48
|
+
function createDeferred() {
|
|
49
|
+
let resolve;
|
|
50
|
+
let reject;
|
|
51
|
+
const promise = new Promise((res, rej) => {
|
|
52
|
+
resolve = res;
|
|
53
|
+
reject = rej;
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
promise,
|
|
57
|
+
resolve: resolve,
|
|
58
|
+
reject: reject,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Runs a function and expects it to throw an error
|
|
63
|
+
*/
|
|
64
|
+
async function expectToThrow(fn, expectedError) {
|
|
65
|
+
try {
|
|
66
|
+
await fn();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (expectedError) {
|
|
70
|
+
if (typeof expectedError === 'string') {
|
|
71
|
+
expect(error.message).toContain(expectedError);
|
|
72
|
+
}
|
|
73
|
+
else if (expectedError instanceof RegExp) {
|
|
74
|
+
expect(error.message).toMatch(expectedError);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
expect(error).toBeInstanceOf(expectedError);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return error;
|
|
81
|
+
}
|
|
82
|
+
throw new Error('Expected function to throw an error, but it did not');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates a mock function that tracks calls
|
|
86
|
+
*/
|
|
87
|
+
function createMockFn() {
|
|
88
|
+
const calls = [];
|
|
89
|
+
const fn = jest.fn((...args) => {
|
|
90
|
+
const call = { args };
|
|
91
|
+
try {
|
|
92
|
+
const result = undefined;
|
|
93
|
+
call.result = result;
|
|
94
|
+
calls.push(call);
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
call.error = error;
|
|
99
|
+
calls.push(call);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
fn,
|
|
105
|
+
calls,
|
|
106
|
+
reset: () => {
|
|
107
|
+
calls.length = 0;
|
|
108
|
+
fn.mockReset();
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=async.helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async.helpers.js","sourceRoot":"","sources":["../../../../src/__test-utils__/helpers/async.helpers.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"jest\" />\n/**\n * Helper utilities for testing asynchronous operations\n */\n\n/**\n * Waits for a promise to settle and returns the result or error\n */\nexport async function settled<T>(\n promise: Promise<T>,\n): Promise<{ status: 'fulfilled'; value: T } | { status: 'rejected'; reason: any }> {\n try {\n const value = await promise;\n return { status: 'fulfilled', value };\n } catch (reason) {\n return { status: 'rejected', reason };\n }\n}\n\n/**\n * Waits for a condition to be true with a timeout\n */\nexport async function waitFor(\n condition: () => boolean | Promise<boolean>,\n options: { timeout?: number; interval?: number } = {},\n): Promise<void> {\n const { timeout = 5000, interval = 50 } = options;\n const start = Date.now();\n\n while (Date.now() - start < timeout) {\n if (await condition()) {\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n\n throw new Error(`Timeout waiting for condition after ${timeout}ms`);\n}\n\n/**\n * Flushes all pending promises (useful for testing async flows)\n */\nexport async function flushPromises(): Promise<void> {\n await new Promise((resolve) => setImmediate(resolve));\n}\n\n/**\n * Creates a deferred promise that can be resolved/rejected externally\n */\nexport function createDeferred<T>() {\n let resolve: (value: T) => void;\n let reject: (reason: any) => void;\n\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n return {\n promise,\n resolve: resolve!,\n reject: reject!,\n };\n}\n\n/**\n * Runs a function and expects it to throw an error\n */\nexport async function expectToThrow<T>(\n fn: () => T | Promise<T>,\n expectedError?: string | RegExp | (new (...args: any[]) => Error),\n): Promise<Error> {\n try {\n await fn();\n } catch (error) {\n if (expectedError) {\n if (typeof expectedError === 'string') {\n expect((error as Error).message).toContain(expectedError);\n } else if (expectedError instanceof RegExp) {\n expect((error as Error).message).toMatch(expectedError);\n } else {\n expect(error).toBeInstanceOf(expectedError);\n }\n }\n return error as Error;\n }\n\n throw new Error('Expected function to throw an error, but it did not');\n}\n\n/**\n * Creates a mock function that tracks calls\n */\nexport function createMockFn<T extends (...args: any[]) => any>() {\n const calls: Array<{ args: Parameters<T>; result?: ReturnType<T>; error?: any }> = [];\n\n const fn = jest.fn((...args: Parameters<T>): ReturnType<T> => {\n const call: any = { args };\n try {\n const result = undefined as ReturnType<T>;\n call.result = result;\n calls.push(call);\n return result;\n } catch (error) {\n call.error = error;\n calls.push(call);\n throw error;\n }\n });\n\n return {\n fn,\n calls,\n reset: () => {\n calls.length = 0;\n fn.mockReset();\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Helper utilities for testing
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const tslib_1 = require("tslib");
|
|
7
|
+
tslib_1.__exportStar(require("./async.helpers"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./assertion.helpers"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./setup.helpers"), exports);
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/__test-utils__/helpers/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,0DAAgC;AAChC,8DAAoC;AACpC,0DAAgC","sourcesContent":["/**\n * Helper utilities for testing\n */\n\nexport * from './async.helpers';\nexport * from './assertion.helpers';\nexport * from './setup.helpers';\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper utilities for test setup and teardown
|
|
3
|
+
*/
|
|
4
|
+
import 'reflect-metadata';
|
|
5
|
+
/**
|
|
6
|
+
* Creates a clean test environment with automatic cleanup
|
|
7
|
+
*/
|
|
8
|
+
export declare function createTestEnvironment(): {
|
|
9
|
+
/**
|
|
10
|
+
* Registers a cleanup function to be called after the test
|
|
11
|
+
*/
|
|
12
|
+
onCleanup(fn: () => void | Promise<void>): void;
|
|
13
|
+
/**
|
|
14
|
+
* Cleans up all registered resources
|
|
15
|
+
*/
|
|
16
|
+
cleanup(): Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Wraps a test function with automatic environment setup and cleanup
|
|
20
|
+
*/
|
|
21
|
+
export declare function withTestEnvironment<T extends any[]>(testFn: (env: ReturnType<typeof createTestEnvironment>, ...args: T) => Promise<void> | void): (...args: T) => Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a mock timer for testing time-based operations
|
|
24
|
+
*/
|
|
25
|
+
export declare function useFakeTimers(): {
|
|
26
|
+
advance: (ms: number) => void;
|
|
27
|
+
runAll: () => void;
|
|
28
|
+
runPending: () => void;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Creates a test-specific logger that can be inspected
|
|
32
|
+
*/
|
|
33
|
+
export declare function createTestLogger(): {
|
|
34
|
+
log: (message: string, data?: any) => number;
|
|
35
|
+
warn: (message: string, data?: any) => number;
|
|
36
|
+
error: (message: string, data?: any) => number;
|
|
37
|
+
info: (message: string, data?: any) => number;
|
|
38
|
+
debug: (message: string, data?: any) => number;
|
|
39
|
+
getLogs: () => {
|
|
40
|
+
level: string;
|
|
41
|
+
message: string;
|
|
42
|
+
data?: any;
|
|
43
|
+
}[];
|
|
44
|
+
clear: () => number;
|
|
45
|
+
hasLog: (level: string, message: string) => boolean;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Ensures reflect-metadata is loaded for decorator tests
|
|
49
|
+
*/
|
|
50
|
+
export declare function setupReflectMetadata(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Clears all mocks between tests
|
|
53
|
+
*/
|
|
54
|
+
export declare function setupMockClearing(): void;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference types="jest" />
|
|
3
|
+
/**
|
|
4
|
+
* Helper utilities for test setup and teardown
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createTestEnvironment = createTestEnvironment;
|
|
8
|
+
exports.withTestEnvironment = withTestEnvironment;
|
|
9
|
+
exports.useFakeTimers = useFakeTimers;
|
|
10
|
+
exports.createTestLogger = createTestLogger;
|
|
11
|
+
exports.setupReflectMetadata = setupReflectMetadata;
|
|
12
|
+
exports.setupMockClearing = setupMockClearing;
|
|
13
|
+
require("reflect-metadata");
|
|
14
|
+
/**
|
|
15
|
+
* Creates a clean test environment with automatic cleanup
|
|
16
|
+
*/
|
|
17
|
+
function createTestEnvironment() {
|
|
18
|
+
const cleanupFns = [];
|
|
19
|
+
return {
|
|
20
|
+
/**
|
|
21
|
+
* Registers a cleanup function to be called after the test
|
|
22
|
+
*/
|
|
23
|
+
onCleanup(fn) {
|
|
24
|
+
cleanupFns.push(fn);
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* Cleans up all registered resources
|
|
28
|
+
*/
|
|
29
|
+
async cleanup() {
|
|
30
|
+
for (const fn of cleanupFns.reverse()) {
|
|
31
|
+
await fn();
|
|
32
|
+
}
|
|
33
|
+
cleanupFns.length = 0;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Wraps a test function with automatic environment setup and cleanup
|
|
39
|
+
*/
|
|
40
|
+
function withTestEnvironment(testFn) {
|
|
41
|
+
return async (...args) => {
|
|
42
|
+
const env = createTestEnvironment();
|
|
43
|
+
try {
|
|
44
|
+
await testFn(env, ...args);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await env.cleanup();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Creates a mock timer for testing time-based operations
|
|
53
|
+
*/
|
|
54
|
+
function useFakeTimers() {
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
jest.useFakeTimers();
|
|
57
|
+
});
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
jest.runOnlyPendingTimers();
|
|
60
|
+
jest.useRealTimers();
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
advance: (ms) => jest.advanceTimersByTime(ms),
|
|
64
|
+
runAll: () => jest.runAllTimers(),
|
|
65
|
+
runPending: () => jest.runOnlyPendingTimers(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Creates a test-specific logger that can be inspected
|
|
70
|
+
*/
|
|
71
|
+
function createTestLogger() {
|
|
72
|
+
const logs = [];
|
|
73
|
+
return {
|
|
74
|
+
log: (message, data) => logs.push({ level: 'log', message, data }),
|
|
75
|
+
warn: (message, data) => logs.push({ level: 'warn', message, data }),
|
|
76
|
+
error: (message, data) => logs.push({ level: 'error', message, data }),
|
|
77
|
+
info: (message, data) => logs.push({ level: 'info', message, data }),
|
|
78
|
+
debug: (message, data) => logs.push({ level: 'debug', message, data }),
|
|
79
|
+
getLogs: () => logs,
|
|
80
|
+
clear: () => (logs.length = 0),
|
|
81
|
+
hasLog: (level, message) => logs.some((log) => log.level === level && log.message.includes(message)),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Ensures reflect-metadata is loaded for decorator tests
|
|
86
|
+
*/
|
|
87
|
+
function setupReflectMetadata() {
|
|
88
|
+
beforeAll(() => {
|
|
89
|
+
// Ensure reflect-metadata is loaded
|
|
90
|
+
if (typeof Reflect === 'undefined' || !Reflect.getMetadata) {
|
|
91
|
+
throw new Error('reflect-metadata is not loaded');
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Clears all mocks between tests
|
|
97
|
+
*/
|
|
98
|
+
function setupMockClearing() {
|
|
99
|
+
beforeEach(() => {
|
|
100
|
+
jest.clearAllMocks();
|
|
101
|
+
});
|
|
102
|
+
afterEach(() => {
|
|
103
|
+
jest.restoreAllMocks();
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=setup.helpers.js.map
|