@frontmcp/testing 0.5.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 +1358 -0
- package/jest-preset.js +61 -0
- package/package.json +94 -0
- package/src/assertions/index.d.ts +5 -0
- package/src/assertions/index.js +18 -0
- package/src/assertions/index.js.map +1 -0
- package/src/assertions/mcp-assertions.d.ts +81 -0
- package/src/assertions/mcp-assertions.js +220 -0
- package/src/assertions/mcp-assertions.js.map +1 -0
- package/src/auth/auth-headers.d.ts +29 -0
- package/src/auth/auth-headers.js +62 -0
- package/src/auth/auth-headers.js.map +1 -0
- package/src/auth/index.d.ts +9 -0
- package/src/auth/index.js +15 -0
- package/src/auth/index.js.map +1 -0
- package/src/auth/token-factory.d.ts +94 -0
- package/src/auth/token-factory.js +181 -0
- package/src/auth/token-factory.js.map +1 -0
- package/src/auth/user-fixtures.d.ts +26 -0
- package/src/auth/user-fixtures.js +92 -0
- package/src/auth/user-fixtures.js.map +1 -0
- package/src/client/index.d.ts +7 -0
- package/src/client/index.js +12 -0
- package/src/client/index.js.map +1 -0
- package/src/client/mcp-test-client.builder.d.ts +72 -0
- package/src/client/mcp-test-client.builder.js +111 -0
- package/src/client/mcp-test-client.builder.js.map +1 -0
- package/src/client/mcp-test-client.d.ts +360 -0
- package/src/client/mcp-test-client.js +929 -0
- package/src/client/mcp-test-client.js.map +1 -0
- package/src/client/mcp-test-client.types.d.ts +216 -0
- package/src/client/mcp-test-client.types.js +7 -0
- package/src/client/mcp-test-client.types.js.map +1 -0
- package/src/errors/index.d.ts +45 -0
- package/src/errors/index.js +85 -0
- package/src/errors/index.js.map +1 -0
- package/src/expect.d.ts +67 -0
- package/src/expect.js +31 -0
- package/src/expect.js.map +1 -0
- package/src/fixtures/fixture-types.d.ts +166 -0
- package/src/fixtures/fixture-types.js +7 -0
- package/src/fixtures/fixture-types.js.map +1 -0
- package/src/fixtures/index.d.ts +7 -0
- package/src/fixtures/index.js +16 -0
- package/src/fixtures/index.js.map +1 -0
- package/src/fixtures/test-fixture.d.ts +41 -0
- package/src/fixtures/test-fixture.js +280 -0
- package/src/fixtures/test-fixture.js.map +1 -0
- package/src/http-mock/http-mock.d.ts +84 -0
- package/src/http-mock/http-mock.js +544 -0
- package/src/http-mock/http-mock.js.map +1 -0
- package/src/http-mock/http-mock.types.d.ts +124 -0
- package/src/http-mock/http-mock.types.js +10 -0
- package/src/http-mock/http-mock.types.js.map +1 -0
- package/src/http-mock/index.d.ts +6 -0
- package/src/http-mock/index.js +11 -0
- package/src/http-mock/index.js.map +1 -0
- package/src/index.d.ts +65 -0
- package/src/index.js +128 -0
- package/src/index.js.map +1 -0
- package/src/interceptor/index.d.ts +7 -0
- package/src/interceptor/index.js +15 -0
- package/src/interceptor/index.js.map +1 -0
- package/src/interceptor/interceptor-chain.d.ts +77 -0
- package/src/interceptor/interceptor-chain.js +207 -0
- package/src/interceptor/interceptor-chain.js.map +1 -0
- package/src/interceptor/interceptor.types.d.ts +131 -0
- package/src/interceptor/interceptor.types.js +7 -0
- package/src/interceptor/interceptor.types.js.map +1 -0
- package/src/interceptor/mock-registry.d.ts +82 -0
- package/src/interceptor/mock-registry.js +189 -0
- package/src/interceptor/mock-registry.js.map +1 -0
- package/src/matchers/index.d.ts +7 -0
- package/src/matchers/index.js +12 -0
- package/src/matchers/index.js.map +1 -0
- package/src/matchers/matcher-types.d.ts +266 -0
- package/src/matchers/matcher-types.js +10 -0
- package/src/matchers/matcher-types.js.map +1 -0
- package/src/matchers/mcp-matchers.d.ts +47 -0
- package/src/matchers/mcp-matchers.js +391 -0
- package/src/matchers/mcp-matchers.js.map +1 -0
- package/src/playwright/index.d.ts +37 -0
- package/src/playwright/index.js +49 -0
- package/src/playwright/index.js.map +1 -0
- package/src/server/index.d.ts +6 -0
- package/src/server/index.js +10 -0
- package/src/server/index.js.map +1 -0
- package/src/server/test-server.d.ts +99 -0
- package/src/server/test-server.js +286 -0
- package/src/server/test-server.js.map +1 -0
- package/src/setup.d.ts +22 -0
- package/src/setup.js +30 -0
- package/src/setup.js.map +1 -0
- package/src/transport/index.d.ts +6 -0
- package/src/transport/index.js +10 -0
- package/src/transport/index.js.map +1 -0
- package/src/transport/streamable-http.transport.d.ts +65 -0
- package/src/transport/streamable-http.transport.js +432 -0
- package/src/transport/streamable-http.transport.js.map +1 -0
- package/src/transport/transport.interface.d.ts +124 -0
- package/src/transport/transport.interface.js +7 -0
- package/src/transport/transport.interface.js.map +1 -0
- package/src/ui/index.d.ts +17 -0
- package/src/ui/index.js +23 -0
- package/src/ui/index.js.map +1 -0
- package/src/ui/ui-assertions.d.ts +94 -0
- package/src/ui/ui-assertions.js +215 -0
- package/src/ui/ui-assertions.js.map +1 -0
- package/src/ui/ui-matchers.d.ts +39 -0
- package/src/ui/ui-matchers.js +275 -0
- package/src/ui/ui-matchers.js.map +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file index.ts
|
|
4
|
+
* @description Barrel exports for MCP Jest matchers
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.mcpMatchers = void 0;
|
|
8
|
+
var mcp_matchers_1 = require("./mcp-matchers");
|
|
9
|
+
Object.defineProperty(exports, "mcpMatchers", { enumerable: true, get: function () { return mcp_matchers_1.mcpMatchers; } });
|
|
10
|
+
// Import the type augmentation to make TypeScript aware of the matchers
|
|
11
|
+
require("./matcher-types");
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/matchers/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+CAA6C;AAApC,2GAAA,WAAW,OAAA;AAKpB,wEAAwE;AACxE,2BAAyB","sourcesContent":["/**\n * @file index.ts\n * @description Barrel exports for MCP Jest matchers\n */\n\nexport { mcpMatchers } from './mcp-matchers';\n\n// Re-export types for convenience\nexport type { McpMatchers } from './matcher-types';\n\n// Import the type augmentation to make TypeScript aware of the matchers\nimport './matcher-types';\n"]}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file matcher-types.ts
|
|
3
|
+
* @description TypeScript declarations for custom MCP Jest matchers
|
|
4
|
+
*
|
|
5
|
+
* This file extends Jest's expect interface with MCP-specific matchers.
|
|
6
|
+
* Import this file or ensure it's included in your tsconfig to get type checking.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Custom MCP matchers for Jest
|
|
10
|
+
*/
|
|
11
|
+
export interface McpMatchers<R = unknown> {
|
|
12
|
+
/**
|
|
13
|
+
* Check if tools array contains a tool with the given name
|
|
14
|
+
* @param toolName - The name of the tool to find
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const tools = await mcp.tools.list();
|
|
19
|
+
* expect(tools).toContainTool('my-tool');
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
toContainTool(toolName: string): R;
|
|
23
|
+
/**
|
|
24
|
+
* Check if result is successful (not an error)
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const result = await mcp.tools.call('my-tool', {});
|
|
29
|
+
* expect(result).toBeSuccessful();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
toBeSuccessful(): R;
|
|
33
|
+
/**
|
|
34
|
+
* Check if result is an error, optionally with a specific error code
|
|
35
|
+
* @param expectedCode - Optional specific MCP error code to match
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const result = await mcp.tools.call('unknown-tool', {});
|
|
40
|
+
* expect(result).toBeError();
|
|
41
|
+
* expect(result).toBeError(-32601); // Method not found
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
toBeError(expectedCode?: number): R;
|
|
45
|
+
/**
|
|
46
|
+
* Check if tool result has text content, optionally containing specific text
|
|
47
|
+
* @param expectedText - Optional text that should be contained in the result
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const result = await mcp.tools.call('my-tool', {});
|
|
52
|
+
* expect(result).toHaveTextContent();
|
|
53
|
+
* expect(result).toHaveTextContent('success');
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
toHaveTextContent(expectedText?: string): R;
|
|
57
|
+
/**
|
|
58
|
+
* Check if tool result has image content
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const result = await mcp.tools.call('generate-image', {});
|
|
63
|
+
* expect(result).toHaveImageContent();
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
toHaveImageContent(): R;
|
|
67
|
+
/**
|
|
68
|
+
* Check if tool result has resource content
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const result = await mcp.tools.call('create-file', {});
|
|
73
|
+
* expect(result).toHaveResourceContent();
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
toHaveResourceContent(): R;
|
|
77
|
+
/**
|
|
78
|
+
* Check if resources array contains a resource with the given URI
|
|
79
|
+
* @param uri - The URI of the resource to find
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const resources = await mcp.resources.list();
|
|
84
|
+
* expect(resources).toContainResource('notes://all');
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
toContainResource(uri: string): R;
|
|
88
|
+
/**
|
|
89
|
+
* Check if resource templates array contains a template with the given URI template
|
|
90
|
+
* @param uriTemplate - The URI template to find
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* const templates = await mcp.resources.listTemplates();
|
|
95
|
+
* expect(templates).toContainResourceTemplate('notes://note/{id}');
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
toContainResourceTemplate(uriTemplate: string): R;
|
|
99
|
+
/**
|
|
100
|
+
* Check if resource content has a specific MIME type
|
|
101
|
+
* @param mimeType - The expected MIME type
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const content = await mcp.resources.read('notes://all');
|
|
106
|
+
* expect(content).toHaveMimeType('application/json');
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
toHaveMimeType(mimeType: string): R;
|
|
110
|
+
/**
|
|
111
|
+
* Check if prompts array contains a prompt with the given name
|
|
112
|
+
* @param name - The name of the prompt to find
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const prompts = await mcp.prompts.list();
|
|
117
|
+
* expect(prompts).toContainPrompt('summarize');
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
toContainPrompt(name: string): R;
|
|
121
|
+
/**
|
|
122
|
+
* Check if prompt result has a specific number of messages
|
|
123
|
+
* @param count - The expected number of messages
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const result = await mcp.prompts.get('summarize', {});
|
|
128
|
+
* expect(result).toHaveMessages(2);
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
toHaveMessages(count: number): R;
|
|
132
|
+
/**
|
|
133
|
+
* Check if response is valid JSON-RPC 2.0
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const response = await mcp.raw.request({ ... });
|
|
138
|
+
* expect(response).toBeValidJsonRpc();
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
toBeValidJsonRpc(): R;
|
|
142
|
+
/**
|
|
143
|
+
* Check if JSON-RPC response has a result
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* const response = await mcp.raw.request({ ... });
|
|
148
|
+
* expect(response).toHaveResult();
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
toHaveResult(): R;
|
|
152
|
+
/**
|
|
153
|
+
* Check if JSON-RPC response has an error
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const response = await mcp.raw.request({ method: 'unknown' });
|
|
158
|
+
* expect(response).toHaveError();
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
toHaveError(): R;
|
|
162
|
+
/**
|
|
163
|
+
* Check if JSON-RPC response has a specific error code
|
|
164
|
+
* @param code - The expected JSON-RPC error code
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* const response = await mcp.raw.request({ method: 'unknown' });
|
|
169
|
+
* expect(response).toHaveErrorCode(-32601);
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
toHaveErrorCode(code: number): R;
|
|
173
|
+
/**
|
|
174
|
+
* Check if tool result has rendered HTML in _meta['ui/html'].
|
|
175
|
+
* Fails if the HTML is the mdx-fallback (escaped raw content).
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* const result = await mcp.tools.call('get_weather', { location: 'London' });
|
|
180
|
+
* expect(result).toHaveRenderedHtml();
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
toHaveRenderedHtml(): R;
|
|
184
|
+
/**
|
|
185
|
+
* Check if HTML contains a specific HTML element tag.
|
|
186
|
+
* @param tag - The HTML tag name to look for (e.g., 'div', 'h1', 'span')
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```typescript
|
|
190
|
+
* expect(result).toContainHtmlElement('div');
|
|
191
|
+
* expect(result).toContainHtmlElement('h1');
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
toContainHtmlElement(tag: string): R;
|
|
195
|
+
/**
|
|
196
|
+
* Check if a bound value from tool output appears in the rendered HTML.
|
|
197
|
+
* @param value - The value to look for (string or number)
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* const output = result.json();
|
|
202
|
+
* expect(result).toContainBoundValue(output.location);
|
|
203
|
+
* expect(result).toContainBoundValue(output.temperature);
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
toContainBoundValue(value: string | number): R;
|
|
207
|
+
/**
|
|
208
|
+
* Check if HTML is XSS-safe (no script tags, event handlers, or javascript: URIs).
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* expect(result).toBeXssSafe();
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
toBeXssSafe(): R;
|
|
216
|
+
/**
|
|
217
|
+
* Check if tool result has widget metadata (ui/resourceUri).
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* expect(result).toHaveWidgetMetadata();
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
toHaveWidgetMetadata(): R;
|
|
225
|
+
/**
|
|
226
|
+
* Check if HTML has a specific CSS class.
|
|
227
|
+
* @param className - The CSS class name to look for
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* expect(result).toHaveCssClass('weather-card');
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
toHaveCssClass(className: string): R;
|
|
235
|
+
/**
|
|
236
|
+
* Check that HTML does NOT contain specific content (useful for fallback checks).
|
|
237
|
+
* @param content - The content that should NOT be in the HTML
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```typescript
|
|
241
|
+
* expect(result).toNotContainRawContent('mdx-fallback');
|
|
242
|
+
* expect(result).toNotContainRawContent('<Alert'); // Custom component should be rendered
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
toNotContainRawContent(content: string): R;
|
|
246
|
+
/**
|
|
247
|
+
* Check if HTML has proper structure (not just escaped text).
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* expect(result).toHaveProperHtmlStructure();
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
toHaveProperHtmlStructure(): R;
|
|
255
|
+
}
|
|
256
|
+
declare global {
|
|
257
|
+
namespace jest {
|
|
258
|
+
interface Matchers<R> extends McpMatchers<R> {
|
|
259
|
+
}
|
|
260
|
+
interface Expect extends McpMatchers<void> {
|
|
261
|
+
}
|
|
262
|
+
interface InverseAsymmetricMatchers extends McpMatchers<void> {
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file matcher-types.ts
|
|
4
|
+
* @description TypeScript declarations for custom MCP Jest matchers
|
|
5
|
+
*
|
|
6
|
+
* This file extends Jest's expect interface with MCP-specific matchers.
|
|
7
|
+
* Import this file or ensure it's included in your tsconfig to get type checking.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
//# sourceMappingURL=matcher-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matcher-types.js","sourceRoot":"","sources":["../../../src/matchers/matcher-types.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG","sourcesContent":["/**\n * @file matcher-types.ts\n * @description TypeScript declarations for custom MCP Jest matchers\n *\n * This file extends Jest's expect interface with MCP-specific matchers.\n * Import this file or ensure it's included in your tsconfig to get type checking.\n */\n\n// Note: These imports are used for documentation/JSDoc purposes in the interface comments\n// The actual runtime types are in mcp-matchers.ts\n\n/**\n * Custom MCP matchers for Jest\n */\nexport interface McpMatchers<R = unknown> {\n // ═══════════════════════════════════════════════════════════════════\n // TOOL MATCHERS\n // ═══════════════════════════════════════════════════════════════════\n\n /**\n * Check if tools array contains a tool with the given name\n * @param toolName - The name of the tool to find\n *\n * @example\n * ```typescript\n * const tools = await mcp.tools.list();\n * expect(tools).toContainTool('my-tool');\n * ```\n */\n toContainTool(toolName: string): R;\n\n /**\n * Check if result is successful (not an error)\n *\n * @example\n * ```typescript\n * const result = await mcp.tools.call('my-tool', {});\n * expect(result).toBeSuccessful();\n * ```\n */\n toBeSuccessful(): R;\n\n /**\n * Check if result is an error, optionally with a specific error code\n * @param expectedCode - Optional specific MCP error code to match\n *\n * @example\n * ```typescript\n * const result = await mcp.tools.call('unknown-tool', {});\n * expect(result).toBeError();\n * expect(result).toBeError(-32601); // Method not found\n * ```\n */\n toBeError(expectedCode?: number): R;\n\n /**\n * Check if tool result has text content, optionally containing specific text\n * @param expectedText - Optional text that should be contained in the result\n *\n * @example\n * ```typescript\n * const result = await mcp.tools.call('my-tool', {});\n * expect(result).toHaveTextContent();\n * expect(result).toHaveTextContent('success');\n * ```\n */\n toHaveTextContent(expectedText?: string): R;\n\n /**\n * Check if tool result has image content\n *\n * @example\n * ```typescript\n * const result = await mcp.tools.call('generate-image', {});\n * expect(result).toHaveImageContent();\n * ```\n */\n toHaveImageContent(): R;\n\n /**\n * Check if tool result has resource content\n *\n * @example\n * ```typescript\n * const result = await mcp.tools.call('create-file', {});\n * expect(result).toHaveResourceContent();\n * ```\n */\n toHaveResourceContent(): R;\n\n // ═══════════════════════════════════════════════════════════════════\n // RESOURCE MATCHERS\n // ═══════════════════════════════════════════════════════════════════\n\n /**\n * Check if resources array contains a resource with the given URI\n * @param uri - The URI of the resource to find\n *\n * @example\n * ```typescript\n * const resources = await mcp.resources.list();\n * expect(resources).toContainResource('notes://all');\n * ```\n */\n toContainResource(uri: string): R;\n\n /**\n * Check if resource templates array contains a template with the given URI template\n * @param uriTemplate - The URI template to find\n *\n * @example\n * ```typescript\n * const templates = await mcp.resources.listTemplates();\n * expect(templates).toContainResourceTemplate('notes://note/{id}');\n * ```\n */\n toContainResourceTemplate(uriTemplate: string): R;\n\n /**\n * Check if resource content has a specific MIME type\n * @param mimeType - The expected MIME type\n *\n * @example\n * ```typescript\n * const content = await mcp.resources.read('notes://all');\n * expect(content).toHaveMimeType('application/json');\n * ```\n */\n toHaveMimeType(mimeType: string): R;\n\n // ═══════════════════════════════════════════════════════════════════\n // PROMPT MATCHERS\n // ═══════════════════════════════════════════════════════════════════\n\n /**\n * Check if prompts array contains a prompt with the given name\n * @param name - The name of the prompt to find\n *\n * @example\n * ```typescript\n * const prompts = await mcp.prompts.list();\n * expect(prompts).toContainPrompt('summarize');\n * ```\n */\n toContainPrompt(name: string): R;\n\n /**\n * Check if prompt result has a specific number of messages\n * @param count - The expected number of messages\n *\n * @example\n * ```typescript\n * const result = await mcp.prompts.get('summarize', {});\n * expect(result).toHaveMessages(2);\n * ```\n */\n toHaveMessages(count: number): R;\n\n // ═══════════════════════════════════════════════════════════════════\n // PROTOCOL MATCHERS\n // ═══════════════════════════════════════════════════════════════════\n\n /**\n * Check if response is valid JSON-RPC 2.0\n *\n * @example\n * ```typescript\n * const response = await mcp.raw.request({ ... });\n * expect(response).toBeValidJsonRpc();\n * ```\n */\n toBeValidJsonRpc(): R;\n\n /**\n * Check if JSON-RPC response has a result\n *\n * @example\n * ```typescript\n * const response = await mcp.raw.request({ ... });\n * expect(response).toHaveResult();\n * ```\n */\n toHaveResult(): R;\n\n /**\n * Check if JSON-RPC response has an error\n *\n * @example\n * ```typescript\n * const response = await mcp.raw.request({ method: 'unknown' });\n * expect(response).toHaveError();\n * ```\n */\n toHaveError(): R;\n\n /**\n * Check if JSON-RPC response has a specific error code\n * @param code - The expected JSON-RPC error code\n *\n * @example\n * ```typescript\n * const response = await mcp.raw.request({ method: 'unknown' });\n * expect(response).toHaveErrorCode(-32601);\n * ```\n */\n toHaveErrorCode(code: number): R;\n\n // ═══════════════════════════════════════════════════════════════════\n // UI MATCHERS\n // ═══════════════════════════════════════════════════════════════════\n\n /**\n * Check if tool result has rendered HTML in _meta['ui/html'].\n * Fails if the HTML is the mdx-fallback (escaped raw content).\n *\n * @example\n * ```typescript\n * const result = await mcp.tools.call('get_weather', { location: 'London' });\n * expect(result).toHaveRenderedHtml();\n * ```\n */\n toHaveRenderedHtml(): R;\n\n /**\n * Check if HTML contains a specific HTML element tag.\n * @param tag - The HTML tag name to look for (e.g., 'div', 'h1', 'span')\n *\n * @example\n * ```typescript\n * expect(result).toContainHtmlElement('div');\n * expect(result).toContainHtmlElement('h1');\n * ```\n */\n toContainHtmlElement(tag: string): R;\n\n /**\n * Check if a bound value from tool output appears in the rendered HTML.\n * @param value - The value to look for (string or number)\n *\n * @example\n * ```typescript\n * const output = result.json();\n * expect(result).toContainBoundValue(output.location);\n * expect(result).toContainBoundValue(output.temperature);\n * ```\n */\n toContainBoundValue(value: string | number): R;\n\n /**\n * Check if HTML is XSS-safe (no script tags, event handlers, or javascript: URIs).\n *\n * @example\n * ```typescript\n * expect(result).toBeXssSafe();\n * ```\n */\n toBeXssSafe(): R;\n\n /**\n * Check if tool result has widget metadata (ui/resourceUri).\n *\n * @example\n * ```typescript\n * expect(result).toHaveWidgetMetadata();\n * ```\n */\n toHaveWidgetMetadata(): R;\n\n /**\n * Check if HTML has a specific CSS class.\n * @param className - The CSS class name to look for\n *\n * @example\n * ```typescript\n * expect(result).toHaveCssClass('weather-card');\n * ```\n */\n toHaveCssClass(className: string): R;\n\n /**\n * Check that HTML does NOT contain specific content (useful for fallback checks).\n * @param content - The content that should NOT be in the HTML\n *\n * @example\n * ```typescript\n * expect(result).toNotContainRawContent('mdx-fallback');\n * expect(result).toNotContainRawContent('<Alert'); // Custom component should be rendered\n * ```\n */\n toNotContainRawContent(content: string): R;\n\n /**\n * Check if HTML has proper structure (not just escaped text).\n *\n * @example\n * ```typescript\n * expect(result).toHaveProperHtmlStructure();\n * ```\n */\n toHaveProperHtmlStructure(): R;\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// JEST TYPE AUGMENTATION\n// ═══════════════════════════════════════════════════════════════════\n\n/* eslint-disable @typescript-eslint/no-namespace */\n/* eslint-disable @typescript-eslint/no-empty-object-type */\ndeclare global {\n namespace jest {\n // Extend expect matchers\n interface Matchers<R> extends McpMatchers<R> {}\n\n // Extend asymmetric matchers (for expect.toContainTool etc.)\n interface Expect extends McpMatchers<void> {}\n\n // Extend inverse matchers (for expect.not.toContainTool etc.)\n interface InverseAsymmetricMatchers extends McpMatchers<void> {}\n }\n}\n/* eslint-enable @typescript-eslint/no-namespace */\n/* eslint-enable @typescript-eslint/no-empty-object-type */\n\n// This export is needed to make this a module\nexport {};\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file mcp-matchers.ts
|
|
3
|
+
* @description Custom Jest matchers for MCP testing
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { test, expect } from '@frontmcp/testing';
|
|
8
|
+
*
|
|
9
|
+
* test('tools work', async ({ mcp }) => {
|
|
10
|
+
* const tools = await mcp.tools.list();
|
|
11
|
+
* expect(tools).toContainTool('my-tool');
|
|
12
|
+
*
|
|
13
|
+
* const result = await mcp.tools.call('my-tool', {});
|
|
14
|
+
* expect(result).toBeSuccessful();
|
|
15
|
+
* expect(result).toHaveTextContent();
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import type { MatcherFunction } from 'expect';
|
|
20
|
+
/**
|
|
21
|
+
* All MCP matchers as an object for expect.extend()
|
|
22
|
+
*/
|
|
23
|
+
export declare const mcpMatchers: {
|
|
24
|
+
toHaveRenderedHtml: MatcherFunction<[]>;
|
|
25
|
+
toContainHtmlElement: MatcherFunction<[tag: string]>;
|
|
26
|
+
toContainBoundValue: MatcherFunction<[value: string | number]>;
|
|
27
|
+
toBeXssSafe: MatcherFunction<[]>;
|
|
28
|
+
toHaveWidgetMetadata: MatcherFunction<[]>;
|
|
29
|
+
toHaveCssClass: MatcherFunction<[className: string]>;
|
|
30
|
+
toNotContainRawContent: MatcherFunction<[content: string]>;
|
|
31
|
+
toHaveProperHtmlStructure: MatcherFunction<[]>;
|
|
32
|
+
toContainTool: MatcherFunction<[toolName: string]>;
|
|
33
|
+
toBeSuccessful: MatcherFunction<[]>;
|
|
34
|
+
toBeError: MatcherFunction<[expectedCode?: number | undefined]>;
|
|
35
|
+
toHaveTextContent: MatcherFunction<[expectedText?: string | undefined]>;
|
|
36
|
+
toHaveImageContent: MatcherFunction<[]>;
|
|
37
|
+
toHaveResourceContent: MatcherFunction<[]>;
|
|
38
|
+
toContainResource: MatcherFunction<[uri: string]>;
|
|
39
|
+
toContainResourceTemplate: MatcherFunction<[uriTemplate: string]>;
|
|
40
|
+
toHaveMimeType: MatcherFunction<[mimeType: string]>;
|
|
41
|
+
toContainPrompt: MatcherFunction<[name: string]>;
|
|
42
|
+
toHaveMessages: MatcherFunction<[count: number]>;
|
|
43
|
+
toBeValidJsonRpc: MatcherFunction<[]>;
|
|
44
|
+
toHaveResult: MatcherFunction<[]>;
|
|
45
|
+
toHaveError: MatcherFunction<[]>;
|
|
46
|
+
toHaveErrorCode: MatcherFunction<[code: number]>;
|
|
47
|
+
};
|