@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.
Files changed (112) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1358 -0
  3. package/jest-preset.js +61 -0
  4. package/package.json +94 -0
  5. package/src/assertions/index.d.ts +5 -0
  6. package/src/assertions/index.js +18 -0
  7. package/src/assertions/index.js.map +1 -0
  8. package/src/assertions/mcp-assertions.d.ts +81 -0
  9. package/src/assertions/mcp-assertions.js +220 -0
  10. package/src/assertions/mcp-assertions.js.map +1 -0
  11. package/src/auth/auth-headers.d.ts +29 -0
  12. package/src/auth/auth-headers.js +62 -0
  13. package/src/auth/auth-headers.js.map +1 -0
  14. package/src/auth/index.d.ts +9 -0
  15. package/src/auth/index.js +15 -0
  16. package/src/auth/index.js.map +1 -0
  17. package/src/auth/token-factory.d.ts +94 -0
  18. package/src/auth/token-factory.js +181 -0
  19. package/src/auth/token-factory.js.map +1 -0
  20. package/src/auth/user-fixtures.d.ts +26 -0
  21. package/src/auth/user-fixtures.js +92 -0
  22. package/src/auth/user-fixtures.js.map +1 -0
  23. package/src/client/index.d.ts +7 -0
  24. package/src/client/index.js +12 -0
  25. package/src/client/index.js.map +1 -0
  26. package/src/client/mcp-test-client.builder.d.ts +72 -0
  27. package/src/client/mcp-test-client.builder.js +111 -0
  28. package/src/client/mcp-test-client.builder.js.map +1 -0
  29. package/src/client/mcp-test-client.d.ts +360 -0
  30. package/src/client/mcp-test-client.js +929 -0
  31. package/src/client/mcp-test-client.js.map +1 -0
  32. package/src/client/mcp-test-client.types.d.ts +216 -0
  33. package/src/client/mcp-test-client.types.js +7 -0
  34. package/src/client/mcp-test-client.types.js.map +1 -0
  35. package/src/errors/index.d.ts +45 -0
  36. package/src/errors/index.js +85 -0
  37. package/src/errors/index.js.map +1 -0
  38. package/src/expect.d.ts +67 -0
  39. package/src/expect.js +31 -0
  40. package/src/expect.js.map +1 -0
  41. package/src/fixtures/fixture-types.d.ts +166 -0
  42. package/src/fixtures/fixture-types.js +7 -0
  43. package/src/fixtures/fixture-types.js.map +1 -0
  44. package/src/fixtures/index.d.ts +7 -0
  45. package/src/fixtures/index.js +16 -0
  46. package/src/fixtures/index.js.map +1 -0
  47. package/src/fixtures/test-fixture.d.ts +41 -0
  48. package/src/fixtures/test-fixture.js +280 -0
  49. package/src/fixtures/test-fixture.js.map +1 -0
  50. package/src/http-mock/http-mock.d.ts +84 -0
  51. package/src/http-mock/http-mock.js +544 -0
  52. package/src/http-mock/http-mock.js.map +1 -0
  53. package/src/http-mock/http-mock.types.d.ts +124 -0
  54. package/src/http-mock/http-mock.types.js +10 -0
  55. package/src/http-mock/http-mock.types.js.map +1 -0
  56. package/src/http-mock/index.d.ts +6 -0
  57. package/src/http-mock/index.js +11 -0
  58. package/src/http-mock/index.js.map +1 -0
  59. package/src/index.d.ts +65 -0
  60. package/src/index.js +128 -0
  61. package/src/index.js.map +1 -0
  62. package/src/interceptor/index.d.ts +7 -0
  63. package/src/interceptor/index.js +15 -0
  64. package/src/interceptor/index.js.map +1 -0
  65. package/src/interceptor/interceptor-chain.d.ts +77 -0
  66. package/src/interceptor/interceptor-chain.js +207 -0
  67. package/src/interceptor/interceptor-chain.js.map +1 -0
  68. package/src/interceptor/interceptor.types.d.ts +131 -0
  69. package/src/interceptor/interceptor.types.js +7 -0
  70. package/src/interceptor/interceptor.types.js.map +1 -0
  71. package/src/interceptor/mock-registry.d.ts +82 -0
  72. package/src/interceptor/mock-registry.js +189 -0
  73. package/src/interceptor/mock-registry.js.map +1 -0
  74. package/src/matchers/index.d.ts +7 -0
  75. package/src/matchers/index.js +12 -0
  76. package/src/matchers/index.js.map +1 -0
  77. package/src/matchers/matcher-types.d.ts +266 -0
  78. package/src/matchers/matcher-types.js +10 -0
  79. package/src/matchers/matcher-types.js.map +1 -0
  80. package/src/matchers/mcp-matchers.d.ts +47 -0
  81. package/src/matchers/mcp-matchers.js +391 -0
  82. package/src/matchers/mcp-matchers.js.map +1 -0
  83. package/src/playwright/index.d.ts +37 -0
  84. package/src/playwright/index.js +49 -0
  85. package/src/playwright/index.js.map +1 -0
  86. package/src/server/index.d.ts +6 -0
  87. package/src/server/index.js +10 -0
  88. package/src/server/index.js.map +1 -0
  89. package/src/server/test-server.d.ts +99 -0
  90. package/src/server/test-server.js +286 -0
  91. package/src/server/test-server.js.map +1 -0
  92. package/src/setup.d.ts +22 -0
  93. package/src/setup.js +30 -0
  94. package/src/setup.js.map +1 -0
  95. package/src/transport/index.d.ts +6 -0
  96. package/src/transport/index.js +10 -0
  97. package/src/transport/index.js.map +1 -0
  98. package/src/transport/streamable-http.transport.d.ts +65 -0
  99. package/src/transport/streamable-http.transport.js +432 -0
  100. package/src/transport/streamable-http.transport.js.map +1 -0
  101. package/src/transport/transport.interface.d.ts +124 -0
  102. package/src/transport/transport.interface.js +7 -0
  103. package/src/transport/transport.interface.js.map +1 -0
  104. package/src/ui/index.d.ts +17 -0
  105. package/src/ui/index.js +23 -0
  106. package/src/ui/index.js.map +1 -0
  107. package/src/ui/ui-assertions.d.ts +94 -0
  108. package/src/ui/ui-assertions.js +215 -0
  109. package/src/ui/ui-assertions.js.map +1 -0
  110. package/src/ui/ui-matchers.d.ts +39 -0
  111. package/src/ui/ui-matchers.js +275 -0
  112. package/src/ui/ui-matchers.js.map +1 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @file index.ts
3
+ * @description Barrel exports for MCP Jest matchers
4
+ */
5
+ export { mcpMatchers } from './mcp-matchers';
6
+ export type { McpMatchers } from './matcher-types';
7
+ import './matcher-types';
@@ -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
+ };