@frontmcp/testing 0.5.1 → 0.6.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/package.json +3 -3
- package/src/auth/mock-api-server.d.ts +99 -0
- package/src/auth/mock-api-server.js +200 -0
- package/src/auth/mock-api-server.js.map +1 -0
- package/src/auth/mock-oauth-server.d.ts +85 -0
- package/src/auth/mock-oauth-server.js +253 -0
- package/src/auth/mock-oauth-server.js.map +1 -0
- package/src/client/mcp-test-client.builder.d.ts +43 -1
- package/src/client/mcp-test-client.builder.js +52 -0
- package/src/client/mcp-test-client.builder.js.map +1 -1
- package/src/client/mcp-test-client.js +22 -14
- package/src/client/mcp-test-client.js.map +1 -1
- package/src/client/mcp-test-client.types.d.ts +67 -6
- package/src/client/mcp-test-client.types.js +9 -0
- package/src/client/mcp-test-client.types.js.map +1 -1
- package/src/example-tools/index.d.ts +19 -0
- package/src/example-tools/index.js +40 -0
- package/src/example-tools/index.js.map +1 -0
- package/src/example-tools/tool-configs.d.ts +170 -0
- package/src/example-tools/tool-configs.js +222 -0
- package/src/example-tools/tool-configs.js.map +1 -0
- package/src/expect.d.ts +6 -5
- package/src/expect.js.map +1 -1
- package/src/fixtures/fixture-types.d.ts +19 -0
- package/src/fixtures/fixture-types.js.map +1 -1
- package/src/fixtures/test-fixture.d.ts +3 -1
- package/src/fixtures/test-fixture.js +35 -4
- package/src/fixtures/test-fixture.js.map +1 -1
- package/src/index.d.ts +7 -0
- package/src/index.js +40 -1
- package/src/index.js.map +1 -1
- package/src/matchers/matcher-types.js.map +1 -1
- package/src/matchers/mcp-matchers.d.ts +7 -0
- package/src/matchers/mcp-matchers.js +8 -4
- package/src/matchers/mcp-matchers.js.map +1 -1
- package/src/platform/index.d.ts +28 -0
- package/src/platform/index.js +47 -0
- package/src/platform/index.js.map +1 -0
- package/src/platform/platform-client-info.d.ts +97 -0
- package/src/platform/platform-client-info.js +155 -0
- package/src/platform/platform-client-info.js.map +1 -0
- package/src/platform/platform-types.d.ts +72 -0
- package/src/platform/platform-types.js +110 -0
- package/src/platform/platform-types.js.map +1 -0
- package/src/server/test-server.d.ts +4 -0
- package/src/server/test-server.js +58 -3
- package/src/server/test-server.js.map +1 -1
- package/src/transport/streamable-http.transport.js +6 -0
- package/src/transport/streamable-http.transport.js.map +1 -1
- package/src/transport/transport.interface.d.ts +3 -0
- package/src/transport/transport.interface.js.map +1 -1
- package/src/ui/ui-assertions.d.ts +59 -0
- package/src/ui/ui-assertions.js +152 -0
- package/src/ui/ui-assertions.js.map +1 -1
- package/src/ui/ui-matchers.d.ts +8 -0
- package/src/ui/ui-matchers.js +218 -0
- package/src/ui/ui-matchers.js.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-configs.js","sourceRoot":"","sources":["../../../src/example-tools/tool-configs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file tool-configs.ts\n * @description Shared tool configurations for platform E2E testing.\n *\n * These configurations provide consistent test fixtures for validating\n * platform-specific meta key behavior across E2E test projects.\n *\n * @example Usage in E2E tool implementation\n * ```typescript\n * import { Tool, ToolContext } from '@frontmcp/sdk';\n * import { BASIC_UI_TOOL_CONFIG, FULL_UI_TOOL_CONFIG } from '@frontmcp/testing';\n *\n * @Tool({\n * name: BASIC_UI_TOOL_CONFIG.name,\n * description: BASIC_UI_TOOL_CONFIG.description,\n * ui: BASIC_UI_TOOL_CONFIG.ui,\n * })\n * export class BasicUITool extends ToolContext<typeof inputSchema, typeof outputSchema> {\n * async execute(input) {\n * return { message: `Hello, ${input.name}!`, timestamp: Date.now() };\n * }\n * }\n * ```\n */\n\nimport { z } from 'zod';\n\n// ═══════════════════════════════════════════════════════════════════\n// BASIC UI TOOL CONFIG\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Input schema for the basic UI tool.\n */\nexport const basicUIToolInputSchema = z.object({\n name: z.string().optional().default('World'),\n});\n\n/**\n * Output schema for the basic UI tool.\n */\nexport const basicUIToolOutputSchema = z.object({\n message: z.string(),\n timestamp: z.number(),\n});\n\n/**\n * Basic UI tool configuration with minimal UI config.\n * Use this for testing that platform-specific meta keys are correctly applied.\n */\nexport const BASIC_UI_TOOL_CONFIG = {\n name: 'platform-test-basic',\n description: 'Basic UI tool for platform testing',\n inputSchema: basicUIToolInputSchema,\n outputSchema: basicUIToolOutputSchema,\n ui: {\n /**\n * Simple template that displays the output.\n * Works with all platforms.\n */\n template: `\n<div class=\"platform-test-basic\">\n <h1>Platform Test - Basic</h1>\n <p>Message: {output.message}</p>\n <p>Timestamp: {output.timestamp}</p>\n</div>\n `.trim(),\n },\n} as const;\n\n// ═══════════════════════════════════════════════════════════════════\n// FULL UI TOOL CONFIG\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Input schema for the full UI tool.\n */\nexport const fullUIToolInputSchema = z.object({\n name: z.string().optional().default('World'),\n count: z.number().optional().default(1),\n});\n\n/**\n * Output schema for the full UI tool.\n */\nexport const fullUIToolOutputSchema = z.object({\n message: z.string(),\n count: z.number(),\n items: z.array(z.string()),\n timestamp: z.number(),\n});\n\n/**\n * Full UI tool configuration with all UI options.\n * Use this for testing comprehensive platform-specific meta key behavior.\n */\nexport const FULL_UI_TOOL_CONFIG = {\n name: 'platform-test-full',\n description: 'Full UI tool with all options for comprehensive platform testing',\n inputSchema: fullUIToolInputSchema,\n outputSchema: fullUIToolOutputSchema,\n ui: {\n /**\n * Template with more complex UI elements.\n */\n template: `\n<div class=\"platform-test-full\">\n <h1>Platform Test - Full</h1>\n <div class=\"message-box\">\n <strong>Message:</strong> {output.message}\n </div>\n <div class=\"count-box\">\n <strong>Count:</strong> {output.count}\n </div>\n <div class=\"items-list\">\n <strong>Items:</strong>\n <ul>\n {output.items.map(item => <li key={item}>{item}</li>)}\n </ul>\n </div>\n <footer>\n <small>Generated at: {new Date(output.timestamp).toISOString()}</small>\n </footer>\n</div>\n `.trim(),\n /**\n * Widget is accessible for callback invocations.\n */\n widgetAccessible: true,\n /**\n * Invocation status messages for OpenAI.\n */\n invocationStatus: {\n invoking: 'Processing request...',\n invoked: 'Request completed',\n },\n /**\n * Content Security Policy configuration.\n */\n csp: {\n connectDomains: ['https://api.example.com'],\n resourceDomains: ['https://cdn.example.com'],\n },\n /**\n * Display mode for the widget.\n */\n displayMode: 'inline' as const,\n /**\n * Prefers border around the widget.\n */\n prefersBorder: true,\n /**\n * Custom sandbox domain.\n */\n sandboxDomain: 'sandbox.example.com',\n },\n} as const;\n\n// ═══════════════════════════════════════════════════════════════════\n// TOOL EXECUTION HELPERS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Generate output for the basic UI tool.\n */\nexport function generateBasicUIToolOutput(input: z.infer<typeof basicUIToolInputSchema>) {\n return {\n message: `Hello, ${input.name}!`,\n timestamp: Date.now(),\n };\n}\n\n/**\n * Generate output for the full UI tool.\n */\nexport function generateFullUIToolOutput(input: z.infer<typeof fullUIToolInputSchema>) {\n const items: string[] = [];\n for (let i = 1; i <= input.count; i++) {\n items.push(`Item ${i}`);\n }\n return {\n message: `Hello, ${input.name}! You requested ${input.count} item(s).`,\n count: input.count,\n items,\n timestamp: Date.now(),\n };\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// EXPECTED META KEYS\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Expected meta keys for OpenAI platform in tools/list response.\n */\nexport const EXPECTED_OPENAI_TOOLS_LIST_META_KEYS = [\n 'openai/outputTemplate',\n 'openai/resultCanProduceWidget',\n 'openai/widgetAccessible',\n] as const;\n\n/**\n * Expected meta keys for OpenAI platform in tools/call response.\n */\nexport const EXPECTED_OPENAI_TOOL_CALL_META_KEYS = ['openai/html', 'openai/mimeType', 'openai/type'] as const;\n\n/**\n * Expected meta keys for ext-apps platform in tools/list response (SEP-1865).\n */\nexport const EXPECTED_EXTAPPS_TOOLS_LIST_META_KEYS = ['ui/resourceUri', 'ui/mimeType', 'ui/cdn', 'ui/type'] as const;\n\n/**\n * Expected meta keys for ext-apps platform in tools/call response (SEP-1865).\n */\nexport const EXPECTED_EXTAPPS_TOOL_CALL_META_KEYS = ['ui/html', 'ui/mimeType', 'ui/type'] as const;\n\n/**\n * Expected meta keys for FrontMCP platforms in tools/list response (Claude, Cursor, etc.).\n */\nexport const EXPECTED_FRONTMCP_TOOLS_LIST_META_KEYS = [\n 'frontmcp/outputTemplate',\n 'frontmcp/resultCanProduceWidget',\n 'ui/cdn',\n 'ui/type',\n] as const;\n\n/**\n * Expected meta keys for FrontMCP platforms in tools/call response (Claude, Cursor, etc.).\n */\nexport const EXPECTED_FRONTMCP_TOOL_CALL_META_KEYS = [\n 'frontmcp/html',\n 'frontmcp/mimeType',\n 'ui/html',\n 'ui/mimeType',\n] as const;\n"]}
|
package/src/expect.d.ts
CHANGED
|
@@ -17,29 +17,30 @@
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
import { expect as jestExpect } from '@jest/globals';
|
|
20
|
+
import type { Matchers } from 'expect';
|
|
20
21
|
import type { McpMatchers } from './matchers/matcher-types';
|
|
21
22
|
/**
|
|
22
23
|
* Extended Jest matchers interface that includes MCP matchers
|
|
23
24
|
*/
|
|
24
|
-
type McpExpectMatchers<R =
|
|
25
|
+
type McpExpectMatchers<R extends void | Promise<void>, T = unknown> = Matchers<R, T> & McpMatchers<R> & {
|
|
25
26
|
/**
|
|
26
27
|
* Inverts the matchers that follow
|
|
27
28
|
*/
|
|
28
|
-
not:
|
|
29
|
+
not: Matchers<R, T> & McpMatchers<R>;
|
|
29
30
|
/**
|
|
30
31
|
* Used to access matchers that are resolved asynchronously
|
|
31
32
|
*/
|
|
32
|
-
resolves:
|
|
33
|
+
resolves: Matchers<Promise<void>, T> & McpMatchers<Promise<void>>;
|
|
33
34
|
/**
|
|
34
35
|
* Used to access matchers that are rejected asynchronously
|
|
35
36
|
*/
|
|
36
|
-
rejects:
|
|
37
|
+
rejects: Matchers<Promise<void>, T> & McpMatchers<Promise<void>>;
|
|
37
38
|
};
|
|
38
39
|
/**
|
|
39
40
|
* Extended expect interface with MCP matchers
|
|
40
41
|
*/
|
|
41
42
|
interface McpExpect {
|
|
42
|
-
<T = unknown>(actual: T): McpExpectMatchers<T>;
|
|
43
|
+
<T = unknown>(actual: T): McpExpectMatchers<void, T>;
|
|
43
44
|
anything(): ReturnType<typeof jestExpect.anything>;
|
|
44
45
|
any(classType: unknown): ReturnType<typeof jestExpect.any>;
|
|
45
46
|
arrayContaining<E = unknown>(arr: readonly E[]): ReturnType<typeof jestExpect.arrayContaining>;
|
package/src/expect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expect.js","sourceRoot":"","sources":["../../src/expect.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAEH,2CAAqD;
|
|
1
|
+
{"version":3,"file":"expect.js","sourceRoot":"","sources":["../../src/expect.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAEH,2CAAqD;AAqDrD;;;;;;GAMG;AACU,QAAA,MAAM,GAAG,gBAAkC,CAAC","sourcesContent":["/**\n * @file expect.ts\n * @description Pre-typed expect export with MCP custom matchers\n *\n * This is the Playwright-style approach - instead of relying on global type\n * augmentation (which can be fragile across monorepos and path mappings),\n * we export a properly typed expect function that includes all MCP matchers.\n *\n * @example\n * ```typescript\n * import { test, expect } from '@frontmcp/testing';\n *\n * test('tools are available', async ({ mcp }) => {\n * const tools = await mcp.tools.list();\n * expect(tools).toContainTool('my-tool'); // Properly typed!\n * });\n * ```\n */\n\nimport { expect as jestExpect } from '@jest/globals';\nimport type { Matchers } from 'expect';\nimport type { McpMatchers } from './matchers/matcher-types';\n\n/**\n * Extended Jest matchers interface that includes MCP matchers\n */\ntype McpExpectMatchers<R extends void | Promise<void>, T = unknown> = Matchers<R, T> &\n McpMatchers<R> & {\n /**\n * Inverts the matchers that follow\n */\n not: Matchers<R, T> & McpMatchers<R>;\n\n /**\n * Used to access matchers that are resolved asynchronously\n */\n resolves: Matchers<Promise<void>, T> & McpMatchers<Promise<void>>;\n\n /**\n * Used to access matchers that are rejected asynchronously\n */\n rejects: Matchers<Promise<void>, T> & McpMatchers<Promise<void>>;\n };\n\n/**\n * Extended expect interface with MCP matchers\n */\ninterface McpExpect {\n <T = unknown>(actual: T): McpExpectMatchers<void, T>;\n\n // Asymmetric matchers\n anything(): ReturnType<typeof jestExpect.anything>;\n any(classType: unknown): ReturnType<typeof jestExpect.any>;\n arrayContaining<E = unknown>(arr: readonly E[]): ReturnType<typeof jestExpect.arrayContaining>;\n objectContaining<E = Record<string, unknown>>(obj: E): ReturnType<typeof jestExpect.objectContaining>;\n stringContaining(str: string): ReturnType<typeof jestExpect.stringContaining>;\n stringMatching(str: string | RegExp): ReturnType<typeof jestExpect.stringMatching>;\n\n // expect.not\n not: {\n arrayContaining<E = unknown>(arr: readonly E[]): ReturnType<typeof jestExpect.not.arrayContaining>;\n objectContaining<E = Record<string, unknown>>(obj: E): ReturnType<typeof jestExpect.not.objectContaining>;\n stringContaining(str: string): ReturnType<typeof jestExpect.not.stringContaining>;\n stringMatching(str: string | RegExp): ReturnType<typeof jestExpect.not.stringMatching>;\n };\n\n // Utilities\n extend(matchers: Record<string, unknown>): void;\n assertions(num: number): void;\n hasAssertions(): void;\n}\n\n/**\n * Pre-typed expect with MCP custom matchers included\n *\n * This approach (similar to Playwright's) provides type safety without\n * relying on global TypeScript namespace augmentation, which can be\n * problematic in monorepo setups with path mappings.\n */\nexport const expect = jestExpect as unknown as McpExpect;\n"]}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @description Type definitions for test fixtures
|
|
4
4
|
*/
|
|
5
5
|
import type { McpTestClient } from '../client/mcp-test-client';
|
|
6
|
+
import type { McpTestClientBuilder } from '../client/mcp-test-client.builder';
|
|
6
7
|
import type { JWK } from 'jose';
|
|
7
8
|
/**
|
|
8
9
|
* Configuration passed to test.use()
|
|
@@ -122,7 +123,25 @@ export interface ServerFixture {
|
|
|
122
123
|
createClient(options?: {
|
|
123
124
|
transport?: 'sse' | 'streamable-http';
|
|
124
125
|
token?: string;
|
|
126
|
+
clientInfo?: {
|
|
127
|
+
name: string;
|
|
128
|
+
version: string;
|
|
129
|
+
};
|
|
125
130
|
}): Promise<McpTestClient>;
|
|
131
|
+
/**
|
|
132
|
+
* Create a client builder for full customization.
|
|
133
|
+
* Use this when you need to set platform-specific capabilities.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const client = await server
|
|
138
|
+
* .createClientBuilder()
|
|
139
|
+
* .withTransport('streamable-http')
|
|
140
|
+
* .withPlatform('ext-apps') // Auto-sets clientInfo AND capabilities
|
|
141
|
+
* .buildAndConnect();
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
createClientBuilder(): McpTestClientBuilder;
|
|
126
145
|
/**
|
|
127
146
|
* Restart the server
|
|
128
147
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture-types.js","sourceRoot":"","sources":["../../../src/fixtures/fixture-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * @file fixture-types.ts\n * @description Type definitions for test fixtures\n */\n\nimport type { McpTestClient } from '../client/mcp-test-client';\nimport type { JWK } from 'jose';\n\n// ═══════════════════════════════════════════════════════════════════\n// TEST CONFIGURATION\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Configuration passed to test.use()\n */\nexport interface TestConfig {\n /** Server entry file path (e.g., './src/main.ts') */\n server?: string;\n /** Port to run server on (default: auto-select available port) */\n port?: number;\n /** Transport type (default: 'streamable-http') */\n transport?: 'sse' | 'streamable-http';\n /** Auth configuration for the server */\n auth?: {\n mode?: 'public' | 'orchestrated';\n type?: 'local' | 'remote';\n };\n /**\n * Enable public mode for the test client.\n * When true, no Authorization header is sent and anonymous token is not requested.\n * Use this for testing servers configured with `auth: { mode: 'public' }`.\n */\n publicMode?: boolean;\n /** Server log level */\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n /** Environment variables to pass to the server */\n env?: Record<string, string>;\n /** Startup timeout in ms (default: 30000) */\n startupTimeout?: number;\n /** Base URL for connecting to an external/already running server */\n baseUrl?: string;\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// FIXTURE TYPES\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Fixtures available in test functions\n */\nexport interface TestFixtures {\n /** Auto-connected MCP client */\n mcp: McpTestClient;\n /** Token factory for auth testing */\n auth: AuthFixture;\n /** Server control */\n server: ServerFixture;\n}\n\n/**\n * Auth fixture for creating and managing test tokens\n */\nexport interface AuthFixture {\n /**\n * Create a JWT token with the specified claims\n */\n createToken(options: {\n sub: string;\n scopes?: string[];\n email?: string;\n name?: string;\n claims?: Record<string, unknown>;\n expiresIn?: number;\n }): Promise<string>;\n\n /**\n * Create an expired token (for testing token expiration)\n */\n createExpiredToken(options: { sub: string }): Promise<string>;\n\n /**\n * Create a token with an invalid signature (for testing signature validation)\n */\n createInvalidToken(options: { sub: string }): string;\n\n /**\n * Pre-built test users with common permission sets\n */\n users: {\n admin: TestUser;\n user: TestUser;\n readOnly: TestUser;\n };\n\n /**\n * Get the public JWKS for verifying tokens\n */\n getJwks(): Promise<{ keys: JWK[] }>;\n\n /**\n * Get the issuer URL\n */\n getIssuer(): string;\n\n /**\n * Get the audience\n */\n getAudience(): string;\n}\n\n/**\n * Pre-defined test user\n */\nexport interface TestUser {\n sub: string;\n scopes: string[];\n email?: string;\n name?: string;\n}\n\n/**\n * Server fixture for controlling the test server\n */\nexport interface ServerFixture {\n /**\n * Server information\n */\n info: {\n baseUrl: string;\n port: number;\n pid?: number;\n };\n\n /**\n * Create an additional MCP client connected to this server\n */\n createClient(options?: {
|
|
1
|
+
{"version":3,"file":"fixture-types.js","sourceRoot":"","sources":["../../../src/fixtures/fixture-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * @file fixture-types.ts\n * @description Type definitions for test fixtures\n */\n\nimport type { McpTestClient } from '../client/mcp-test-client';\nimport type { McpTestClientBuilder } from '../client/mcp-test-client.builder';\nimport type { JWK } from 'jose';\n\n// ═══════════════════════════════════════════════════════════════════\n// TEST CONFIGURATION\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Configuration passed to test.use()\n */\nexport interface TestConfig {\n /** Server entry file path (e.g., './src/main.ts') */\n server?: string;\n /** Port to run server on (default: auto-select available port) */\n port?: number;\n /** Transport type (default: 'streamable-http') */\n transport?: 'sse' | 'streamable-http';\n /** Auth configuration for the server */\n auth?: {\n mode?: 'public' | 'orchestrated';\n type?: 'local' | 'remote';\n };\n /**\n * Enable public mode for the test client.\n * When true, no Authorization header is sent and anonymous token is not requested.\n * Use this for testing servers configured with `auth: { mode: 'public' }`.\n */\n publicMode?: boolean;\n /** Server log level */\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n /** Environment variables to pass to the server */\n env?: Record<string, string>;\n /** Startup timeout in ms (default: 30000) */\n startupTimeout?: number;\n /** Base URL for connecting to an external/already running server */\n baseUrl?: string;\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// FIXTURE TYPES\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Fixtures available in test functions\n */\nexport interface TestFixtures {\n /** Auto-connected MCP client */\n mcp: McpTestClient;\n /** Token factory for auth testing */\n auth: AuthFixture;\n /** Server control */\n server: ServerFixture;\n}\n\n/**\n * Auth fixture for creating and managing test tokens\n */\nexport interface AuthFixture {\n /**\n * Create a JWT token with the specified claims\n */\n createToken(options: {\n sub: string;\n scopes?: string[];\n email?: string;\n name?: string;\n claims?: Record<string, unknown>;\n expiresIn?: number;\n }): Promise<string>;\n\n /**\n * Create an expired token (for testing token expiration)\n */\n createExpiredToken(options: { sub: string }): Promise<string>;\n\n /**\n * Create a token with an invalid signature (for testing signature validation)\n */\n createInvalidToken(options: { sub: string }): string;\n\n /**\n * Pre-built test users with common permission sets\n */\n users: {\n admin: TestUser;\n user: TestUser;\n readOnly: TestUser;\n };\n\n /**\n * Get the public JWKS for verifying tokens\n */\n getJwks(): Promise<{ keys: JWK[] }>;\n\n /**\n * Get the issuer URL\n */\n getIssuer(): string;\n\n /**\n * Get the audience\n */\n getAudience(): string;\n}\n\n/**\n * Pre-defined test user\n */\nexport interface TestUser {\n sub: string;\n scopes: string[];\n email?: string;\n name?: string;\n}\n\n/**\n * Server fixture for controlling the test server\n */\nexport interface ServerFixture {\n /**\n * Server information\n */\n info: {\n baseUrl: string;\n port: number;\n pid?: number;\n };\n\n /**\n * Create an additional MCP client connected to this server\n */\n createClient(options?: {\n transport?: 'sse' | 'streamable-http';\n token?: string;\n clientInfo?: { name: string; version: string };\n }): Promise<McpTestClient>;\n\n /**\n * Create a client builder for full customization.\n * Use this when you need to set platform-specific capabilities.\n *\n * @example\n * ```typescript\n * const client = await server\n * .createClientBuilder()\n * .withTransport('streamable-http')\n * .withPlatform('ext-apps') // Auto-sets clientInfo AND capabilities\n * .buildAndConnect();\n * ```\n */\n createClientBuilder(): McpTestClientBuilder;\n\n /**\n * Restart the server\n */\n restart(): Promise<void>;\n\n /**\n * Get captured server logs\n */\n getLogs(): string[];\n\n /**\n * Clear captured server logs\n */\n clearLogs(): void;\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// TEST FUNCTION TYPE\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Test function that receives fixtures\n */\nexport type TestFn = (fixtures: TestFixtures) => Promise<void> | void;\n\n/**\n * Enhanced test function with fixture support\n */\nexport interface TestWithFixtures {\n (name: string, fn: TestFn): void;\n\n /** Configure fixtures for this test file/suite */\n use(config: TestConfig): void;\n\n /** Create a describe block */\n describe: typeof describe;\n\n /** Run before all tests in the file */\n beforeAll: typeof beforeAll;\n\n /** Run before each test */\n beforeEach: typeof beforeEach;\n\n /** Run after each test */\n afterEach: typeof afterEach;\n\n /** Run after all tests in the file */\n afterAll: typeof afterAll;\n\n /** Skip a test */\n skip(name: string, fn: TestFn): void;\n\n /** Run only this test */\n only(name: string, fn: TestFn): void;\n\n /** Mark test as todo (not implemented) */\n todo(name: string): void;\n}\n"]}
|
|
@@ -30,8 +30,10 @@ declare function initializeSharedResources(): Promise<void>;
|
|
|
30
30
|
declare function createTestFixtures(): Promise<TestFixtures>;
|
|
31
31
|
/**
|
|
32
32
|
* Clean up fixtures after a single test
|
|
33
|
+
* @param fixtures - The test fixtures to clean up
|
|
34
|
+
* @param testFailed - Whether the test failed (to output server logs)
|
|
33
35
|
*/
|
|
34
|
-
declare function cleanupTestFixtures(fixtures: TestFixtures): Promise<void>;
|
|
36
|
+
declare function cleanupTestFixtures(fixtures: TestFixtures, _testFailed?: boolean): Promise<void>;
|
|
35
37
|
/**
|
|
36
38
|
* Clean up shared resources after all tests in a file
|
|
37
39
|
*/
|
|
@@ -27,6 +27,7 @@ exports.cleanupTestFixtures = cleanupTestFixtures;
|
|
|
27
27
|
exports.initializeSharedResources = initializeSharedResources;
|
|
28
28
|
exports.cleanupSharedResources = cleanupSharedResources;
|
|
29
29
|
const mcp_test_client_1 = require("../client/mcp-test-client");
|
|
30
|
+
const mcp_test_client_builder_1 = require("../client/mcp-test-client.builder");
|
|
30
31
|
const token_factory_1 = require("../auth/token-factory");
|
|
31
32
|
const test_server_1 = require("../server/test-server");
|
|
32
33
|
// ═══════════════════════════════════════════════════════════════════
|
|
@@ -98,8 +99,10 @@ async function createTestFixtures() {
|
|
|
98
99
|
}
|
|
99
100
|
/**
|
|
100
101
|
* Clean up fixtures after a single test
|
|
102
|
+
* @param fixtures - The test fixtures to clean up
|
|
103
|
+
* @param testFailed - Whether the test failed (to output server logs)
|
|
101
104
|
*/
|
|
102
|
-
async function cleanupTestFixtures(fixtures) {
|
|
105
|
+
async function cleanupTestFixtures(fixtures, _testFailed = false) {
|
|
103
106
|
// Disconnect client
|
|
104
107
|
if (fixtures.mcp.isConnected()) {
|
|
105
108
|
await fixtures.mcp.disconnect();
|
|
@@ -174,12 +177,25 @@ function createServerFixture(server) {
|
|
|
174
177
|
return {
|
|
175
178
|
info: server.info,
|
|
176
179
|
createClient: async (opts) => {
|
|
180
|
+
// Inherit publicMode from current config when creating additional clients
|
|
181
|
+
// This ensures all clients connect to a public server correctly
|
|
177
182
|
return mcp_test_client_1.McpTestClient.create({
|
|
178
183
|
baseUrl: server.info.baseUrl,
|
|
179
184
|
transport: opts?.transport ?? 'streamable-http',
|
|
180
185
|
auth: opts?.token ? { token: opts.token } : undefined,
|
|
186
|
+
clientInfo: opts?.clientInfo,
|
|
187
|
+
publicMode: currentConfig.publicMode,
|
|
181
188
|
}).buildAndConnect();
|
|
182
189
|
},
|
|
190
|
+
createClientBuilder: () => {
|
|
191
|
+
// Return a pre-configured builder with the server's base URL and publicMode
|
|
192
|
+
// This allows full customization including platform-specific capabilities
|
|
193
|
+
const builder = new mcp_test_client_builder_1.McpTestClientBuilder({
|
|
194
|
+
baseUrl: server.info.baseUrl,
|
|
195
|
+
publicMode: currentConfig.publicMode,
|
|
196
|
+
});
|
|
197
|
+
return builder;
|
|
198
|
+
},
|
|
183
199
|
restart: () => server.restart(),
|
|
184
200
|
getLogs: () => server.getLogs(),
|
|
185
201
|
clearLogs: () => server.clearLogs(),
|
|
@@ -205,11 +221,16 @@ function resolveServerCommand(server) {
|
|
|
205
221
|
function testWithFixtures(name, fn) {
|
|
206
222
|
it(name, async () => {
|
|
207
223
|
const fixtures = await createTestFixtures();
|
|
224
|
+
let testFailed = false;
|
|
208
225
|
try {
|
|
209
226
|
await fn(fixtures);
|
|
210
227
|
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
testFailed = true;
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
211
232
|
finally {
|
|
212
|
-
await cleanupTestFixtures(fixtures);
|
|
233
|
+
await cleanupTestFixtures(fixtures, testFailed);
|
|
213
234
|
}
|
|
214
235
|
});
|
|
215
236
|
}
|
|
@@ -231,11 +252,16 @@ function use(config) {
|
|
|
231
252
|
function skip(name, fn) {
|
|
232
253
|
it.skip(name, async () => {
|
|
233
254
|
const fixtures = await createTestFixtures();
|
|
255
|
+
let testFailed = false;
|
|
234
256
|
try {
|
|
235
257
|
await fn(fixtures);
|
|
236
258
|
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
testFailed = true;
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
237
263
|
finally {
|
|
238
|
-
await cleanupTestFixtures(fixtures);
|
|
264
|
+
await cleanupTestFixtures(fixtures, testFailed);
|
|
239
265
|
}
|
|
240
266
|
});
|
|
241
267
|
}
|
|
@@ -245,11 +271,16 @@ function skip(name, fn) {
|
|
|
245
271
|
function only(name, fn) {
|
|
246
272
|
it.only(name, async () => {
|
|
247
273
|
const fixtures = await createTestFixtures();
|
|
274
|
+
let testFailed = false;
|
|
248
275
|
try {
|
|
249
276
|
await fn(fixtures);
|
|
250
277
|
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
testFailed = true;
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
251
282
|
finally {
|
|
252
|
-
await cleanupTestFixtures(fixtures);
|
|
283
|
+
await cleanupTestFixtures(fixtures, testFailed);
|
|
253
284
|
}
|
|
254
285
|
});
|
|
255
286
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-fixture.js","sourceRoot":"","sources":["../../../src/fixtures/test-fixture.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file test-fixture.ts\n * @description Jest-based fixture system for MCP testing\n *\n * Provides a Playwright-like fixture API for testing FrontMCP servers:\n *\n * @example\n * ```typescript\n * import { test, expect } from '@frontmcp/testing';\n *\n * test.use({\n * server: './src/main.ts',\n * port: 3003,\n * });\n *\n * test('server exposes tools', async ({ mcp }) => {\n * const tools = await mcp.tools.list();\n * expect(tools).toContainTool('my-tool');\n * });\n * ```\n */\n\nimport { McpTestClient } from '../client/mcp-test-client';\nimport { TestTokenFactory } from '../auth/token-factory';\nimport { TestServer } from '../server/test-server';\nimport type {\n TestConfig,\n TestFixtures,\n AuthFixture,\n ServerFixture,\n TestFn,\n TestWithFixtures,\n TestUser,\n} from './fixture-types';\n\n// ═══════════════════════════════════════════════════════════════════\n// GLOBAL STATE\n// ═══════════════════════════════════════════════════════════════════\n\n/** Current test configuration (set via test.use()) */\nlet currentConfig: TestConfig = {};\n\n/** Server instance (shared across tests in a file) */\nlet serverInstance: TestServer | null = null;\n\n/** Token factory instance (shared across tests in a file) */\nlet tokenFactory: TestTokenFactory | null = null;\n\n/** Track if server was started by us (vs external) */\nlet serverStartedByUs = false;\n\n// ═══════════════════════════════════════════════════════════════════\n// FIXTURE SETUP/TEARDOWN\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Initialize shared resources (server, token factory) once per test file\n */\nasync function initializeSharedResources(): Promise<void> {\n // Create token factory if not exists\n if (!tokenFactory) {\n tokenFactory = new TestTokenFactory();\n }\n\n // Start or connect to server if not exists\n if (!serverInstance) {\n if (currentConfig.baseUrl) {\n // Connect to existing external server\n serverInstance = TestServer.connect(currentConfig.baseUrl);\n serverStartedByUs = false;\n } else if (currentConfig.server) {\n // Start new server\n serverInstance = await TestServer.start({\n port: currentConfig.port,\n command: resolveServerCommand(currentConfig.server),\n env: currentConfig.env,\n startupTimeout: currentConfig.startupTimeout ?? 30000,\n debug: currentConfig.logLevel === 'debug',\n });\n serverStartedByUs = true;\n } else {\n throw new Error(\n 'test.use() requires either \"server\" (entry file path) or \"baseUrl\" (for external server) option',\n );\n }\n }\n}\n\n/**\n * Create fixtures for a single test\n */\nasync function createTestFixtures(): Promise<TestFixtures> {\n // Ensure shared resources are initialized\n await initializeSharedResources();\n\n // Create MCP client for this test\n // Pass publicMode if configured to skip authentication\n const clientInstance = await McpTestClient.create({\n baseUrl: serverInstance!.info.baseUrl,\n transport: currentConfig.transport ?? 'streamable-http',\n publicMode: currentConfig.publicMode,\n }).buildAndConnect();\n\n // Build fixtures\n const auth = createAuthFixture(tokenFactory!);\n const server = createServerFixture(serverInstance!);\n\n return {\n mcp: clientInstance,\n auth,\n server,\n };\n}\n\n/**\n * Clean up fixtures after a single test\n */\nasync function cleanupTestFixtures(fixtures: TestFixtures): Promise<void> {\n // Disconnect client\n if (fixtures.mcp.isConnected()) {\n await fixtures.mcp.disconnect();\n }\n}\n\n/**\n * Clean up shared resources after all tests in a file\n */\nasync function cleanupSharedResources(): Promise<void> {\n // Only stop server if we started it\n if (serverInstance && serverStartedByUs) {\n await serverInstance.stop();\n }\n serverInstance = null;\n tokenFactory = null;\n serverStartedByUs = false;\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// FIXTURE FACTORIES\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Create the auth fixture from token factory\n */\nfunction createAuthFixture(factory: TestTokenFactory): AuthFixture {\n const users: Record<string, TestUser> = {\n admin: {\n sub: 'admin-001',\n scopes: ['admin:*', 'read', 'write', 'delete'],\n email: 'admin@test.local',\n name: 'Test Admin',\n },\n user: {\n sub: 'user-001',\n scopes: ['read', 'write'],\n email: 'user@test.local',\n name: 'Test User',\n },\n readOnly: {\n sub: 'readonly-001',\n scopes: ['read'],\n email: 'readonly@test.local',\n name: 'Read Only User',\n },\n };\n\n return {\n createToken: (opts) =>\n factory.createTestToken({\n sub: opts.sub,\n scopes: opts.scopes,\n claims: {\n email: opts.email,\n name: opts.name,\n ...opts.claims,\n },\n exp: opts.expiresIn,\n }),\n\n createExpiredToken: (opts) => factory.createExpiredToken(opts),\n\n createInvalidToken: (opts) => factory.createTokenWithInvalidSignature(opts),\n\n users: {\n admin: users['admin'],\n user: users['user'],\n readOnly: users['readOnly'],\n },\n\n getJwks: () => factory.getPublicJwks(),\n\n getIssuer: () => factory.getIssuer(),\n\n getAudience: () => factory.getAudience(),\n };\n}\n\n/**\n * Create the server fixture from test server\n */\nfunction createServerFixture(server: TestServer): ServerFixture {\n return {\n info: server.info,\n\n createClient: async (opts) => {\n return McpTestClient.create({\n baseUrl: server.info.baseUrl,\n transport: opts?.transport ?? 'streamable-http',\n auth: opts?.token ? { token: opts.token } : undefined,\n }).buildAndConnect();\n },\n\n restart: () => server.restart(),\n\n getLogs: () => server.getLogs(),\n\n clearLogs: () => server.clearLogs(),\n };\n}\n\n/**\n * Resolve server entry to a command\n */\nfunction resolveServerCommand(server: string): string {\n // If it's already a command (contains spaces), use as-is\n if (server.includes(' ')) {\n return server;\n }\n // Otherwise, run with tsx\n return `npx tsx ${server}`;\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// TEST FUNCTION WITH FIXTURES\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Enhanced test function that provides fixtures\n */\nfunction testWithFixtures(name: string, fn: TestFn): void {\n it(name, async () => {\n const fixtures = await createTestFixtures();\n try {\n await fn(fixtures);\n } finally {\n await cleanupTestFixtures(fixtures);\n }\n });\n}\n\n/**\n * Configure test fixtures for the current test file/suite\n */\nfunction use(config: TestConfig): void {\n // Merge with existing config\n currentConfig = { ...currentConfig, ...config };\n\n // Register cleanup hook if not already done\n // This ensures server is stopped after all tests in the file\n afterAll(async () => {\n await cleanupSharedResources();\n });\n}\n\n/**\n * Skip a test\n */\nfunction skip(name: string, fn: TestFn): void {\n it.skip(name, async () => {\n const fixtures = await createTestFixtures();\n try {\n await fn(fixtures);\n } finally {\n await cleanupTestFixtures(fixtures);\n }\n });\n}\n\n/**\n * Run only this test\n */\nfunction only(name: string, fn: TestFn): void {\n it.only(name, async () => {\n const fixtures = await createTestFixtures();\n try {\n await fn(fixtures);\n } finally {\n await cleanupTestFixtures(fixtures);\n }\n });\n}\n\n/**\n * Mark test as todo\n */\nfunction todo(name: string): void {\n it.todo(name);\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// ATTACH STATIC METHODS\n// ═══════════════════════════════════════════════════════════════════\n\n// Cast to the full interface type\nconst test = testWithFixtures as TestWithFixtures;\n\n// Attach configuration method\ntest.use = use;\n\n// Attach Jest lifecycle methods\ntest.describe = describe;\ntest.beforeAll = beforeAll;\ntest.beforeEach = beforeEach;\ntest.afterEach = afterEach;\ntest.afterAll = afterAll;\n\n// Attach test modifiers\ntest.skip = skip;\ntest.only = only;\ntest.todo = todo;\n\n// ═══════════════════════════════════════════════════════════════════\n// EXPORTS\n// ═══════════════════════════════════════════════════════════════════\n\nexport { test };\n\n// Also export for advanced use cases\nexport { createTestFixtures, cleanupTestFixtures, initializeSharedResources, cleanupSharedResources };\n"]}
|
|
1
|
+
{"version":3,"file":"test-fixture.js","sourceRoot":"","sources":["../../../src/fixtures/test-fixture.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file test-fixture.ts\n * @description Jest-based fixture system for MCP testing\n *\n * Provides a Playwright-like fixture API for testing FrontMCP servers:\n *\n * @example\n * ```typescript\n * import { test, expect } from '@frontmcp/testing';\n *\n * test.use({\n * server: './src/main.ts',\n * port: 3003,\n * });\n *\n * test('server exposes tools', async ({ mcp }) => {\n * const tools = await mcp.tools.list();\n * expect(tools).toContainTool('my-tool');\n * });\n * ```\n */\n\nimport { McpTestClient } from '../client/mcp-test-client';\nimport { McpTestClientBuilder } from '../client/mcp-test-client.builder';\nimport { TestTokenFactory } from '../auth/token-factory';\nimport { TestServer } from '../server/test-server';\nimport type {\n TestConfig,\n TestFixtures,\n AuthFixture,\n ServerFixture,\n TestFn,\n TestWithFixtures,\n TestUser,\n} from './fixture-types';\n\n// ═══════════════════════════════════════════════════════════════════\n// GLOBAL STATE\n// ═══════════════════════════════════════════════════════════════════\n\n/** Current test configuration (set via test.use()) */\nlet currentConfig: TestConfig = {};\n\n/** Server instance (shared across tests in a file) */\nlet serverInstance: TestServer | null = null;\n\n/** Token factory instance (shared across tests in a file) */\nlet tokenFactory: TestTokenFactory | null = null;\n\n/** Track if server was started by us (vs external) */\nlet serverStartedByUs = false;\n\n// ═══════════════════════════════════════════════════════════════════\n// FIXTURE SETUP/TEARDOWN\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Initialize shared resources (server, token factory) once per test file\n */\nasync function initializeSharedResources(): Promise<void> {\n // Create token factory if not exists\n if (!tokenFactory) {\n tokenFactory = new TestTokenFactory();\n }\n\n // Start or connect to server if not exists\n if (!serverInstance) {\n if (currentConfig.baseUrl) {\n // Connect to existing external server\n serverInstance = TestServer.connect(currentConfig.baseUrl);\n serverStartedByUs = false;\n } else if (currentConfig.server) {\n // Start new server\n serverInstance = await TestServer.start({\n port: currentConfig.port,\n command: resolveServerCommand(currentConfig.server),\n env: currentConfig.env,\n startupTimeout: currentConfig.startupTimeout ?? 30000,\n debug: currentConfig.logLevel === 'debug',\n });\n serverStartedByUs = true;\n } else {\n throw new Error(\n 'test.use() requires either \"server\" (entry file path) or \"baseUrl\" (for external server) option',\n );\n }\n }\n}\n\n/**\n * Create fixtures for a single test\n */\nasync function createTestFixtures(): Promise<TestFixtures> {\n // Ensure shared resources are initialized\n await initializeSharedResources();\n\n // Create MCP client for this test\n // Pass publicMode if configured to skip authentication\n const clientInstance = await McpTestClient.create({\n baseUrl: serverInstance!.info.baseUrl,\n transport: currentConfig.transport ?? 'streamable-http',\n publicMode: currentConfig.publicMode,\n }).buildAndConnect();\n\n // Build fixtures\n const auth = createAuthFixture(tokenFactory!);\n const server = createServerFixture(serverInstance!);\n\n return {\n mcp: clientInstance,\n auth,\n server,\n };\n}\n\n/**\n * Clean up fixtures after a single test\n * @param fixtures - The test fixtures to clean up\n * @param testFailed - Whether the test failed (to output server logs)\n */\nasync function cleanupTestFixtures(fixtures: TestFixtures, _testFailed = false): Promise<void> {\n // Disconnect client\n if (fixtures.mcp.isConnected()) {\n await fixtures.mcp.disconnect();\n }\n}\n\n/**\n * Clean up shared resources after all tests in a file\n */\nasync function cleanupSharedResources(): Promise<void> {\n // Only stop server if we started it\n if (serverInstance && serverStartedByUs) {\n await serverInstance.stop();\n }\n serverInstance = null;\n tokenFactory = null;\n serverStartedByUs = false;\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// FIXTURE FACTORIES\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Create the auth fixture from token factory\n */\nfunction createAuthFixture(factory: TestTokenFactory): AuthFixture {\n const users: Record<string, TestUser> = {\n admin: {\n sub: 'admin-001',\n scopes: ['admin:*', 'read', 'write', 'delete'],\n email: 'admin@test.local',\n name: 'Test Admin',\n },\n user: {\n sub: 'user-001',\n scopes: ['read', 'write'],\n email: 'user@test.local',\n name: 'Test User',\n },\n readOnly: {\n sub: 'readonly-001',\n scopes: ['read'],\n email: 'readonly@test.local',\n name: 'Read Only User',\n },\n };\n\n return {\n createToken: (opts) =>\n factory.createTestToken({\n sub: opts.sub,\n scopes: opts.scopes,\n claims: {\n email: opts.email,\n name: opts.name,\n ...opts.claims,\n },\n exp: opts.expiresIn,\n }),\n\n createExpiredToken: (opts) => factory.createExpiredToken(opts),\n\n createInvalidToken: (opts) => factory.createTokenWithInvalidSignature(opts),\n\n users: {\n admin: users['admin'],\n user: users['user'],\n readOnly: users['readOnly'],\n },\n\n getJwks: () => factory.getPublicJwks(),\n\n getIssuer: () => factory.getIssuer(),\n\n getAudience: () => factory.getAudience(),\n };\n}\n\n/**\n * Create the server fixture from test server\n */\nfunction createServerFixture(server: TestServer): ServerFixture {\n return {\n info: server.info,\n\n createClient: async (opts) => {\n // Inherit publicMode from current config when creating additional clients\n // This ensures all clients connect to a public server correctly\n return McpTestClient.create({\n baseUrl: server.info.baseUrl,\n transport: opts?.transport ?? 'streamable-http',\n auth: opts?.token ? { token: opts.token } : undefined,\n clientInfo: opts?.clientInfo,\n publicMode: currentConfig.publicMode,\n }).buildAndConnect();\n },\n\n createClientBuilder: () => {\n // Return a pre-configured builder with the server's base URL and publicMode\n // This allows full customization including platform-specific capabilities\n const builder = new McpTestClientBuilder({\n baseUrl: server.info.baseUrl,\n publicMode: currentConfig.publicMode,\n });\n return builder;\n },\n\n restart: () => server.restart(),\n\n getLogs: () => server.getLogs(),\n\n clearLogs: () => server.clearLogs(),\n };\n}\n\n/**\n * Resolve server entry to a command\n */\nfunction resolveServerCommand(server: string): string {\n // If it's already a command (contains spaces), use as-is\n if (server.includes(' ')) {\n return server;\n }\n // Otherwise, run with tsx\n return `npx tsx ${server}`;\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// TEST FUNCTION WITH FIXTURES\n// ═══════════════════════════════════════════════════════════════════\n\n/**\n * Enhanced test function that provides fixtures\n */\nfunction testWithFixtures(name: string, fn: TestFn): void {\n it(name, async () => {\n const fixtures = await createTestFixtures();\n let testFailed = false;\n try {\n await fn(fixtures);\n } catch (error) {\n testFailed = true;\n throw error;\n } finally {\n await cleanupTestFixtures(fixtures, testFailed);\n }\n });\n}\n\n/**\n * Configure test fixtures for the current test file/suite\n */\nfunction use(config: TestConfig): void {\n // Merge with existing config\n currentConfig = { ...currentConfig, ...config };\n\n // Register cleanup hook if not already done\n // This ensures server is stopped after all tests in the file\n afterAll(async () => {\n await cleanupSharedResources();\n });\n}\n\n/**\n * Skip a test\n */\nfunction skip(name: string, fn: TestFn): void {\n it.skip(name, async () => {\n const fixtures = await createTestFixtures();\n let testFailed = false;\n try {\n await fn(fixtures);\n } catch (error) {\n testFailed = true;\n throw error;\n } finally {\n await cleanupTestFixtures(fixtures, testFailed);\n }\n });\n}\n\n/**\n * Run only this test\n */\nfunction only(name: string, fn: TestFn): void {\n it.only(name, async () => {\n const fixtures = await createTestFixtures();\n let testFailed = false;\n try {\n await fn(fixtures);\n } catch (error) {\n testFailed = true;\n throw error;\n } finally {\n await cleanupTestFixtures(fixtures, testFailed);\n }\n });\n}\n\n/**\n * Mark test as todo\n */\nfunction todo(name: string): void {\n it.todo(name);\n}\n\n// ═══════════════════════════════════════════════════════════════════\n// ATTACH STATIC METHODS\n// ═══════════════════════════════════════════════════════════════════\n\n// Cast to the full interface type\nconst test = testWithFixtures as TestWithFixtures;\n\n// Attach configuration method\ntest.use = use;\n\n// Attach Jest lifecycle methods\ntest.describe = describe;\ntest.beforeAll = beforeAll;\ntest.beforeEach = beforeEach;\ntest.afterEach = afterEach;\ntest.afterAll = afterAll;\n\n// Attach test modifiers\ntest.skip = skip;\ntest.only = only;\ntest.todo = todo;\n\n// ═══════════════════════════════════════════════════════════════════\n// EXPORTS\n// ═══════════════════════════════════════════════════════════════════\n\nexport { test };\n\n// Also export for advanced use cases\nexport { createTestFixtures, cleanupTestFixtures, initializeSharedResources, cleanupSharedResources };\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -48,6 +48,10 @@ export type { CreateTokenOptions, TokenFactoryOptions } from './auth/token-facto
|
|
|
48
48
|
export { AuthHeaders } from './auth/auth-headers';
|
|
49
49
|
export { TestUsers, createTestUser } from './auth/user-fixtures';
|
|
50
50
|
export type { TestUserFixture } from './auth/user-fixtures';
|
|
51
|
+
export { MockOAuthServer } from './auth/mock-oauth-server';
|
|
52
|
+
export type { MockOAuthServerOptions, MockOAuthServerInfo } from './auth/mock-oauth-server';
|
|
53
|
+
export { MockAPIServer } from './auth/mock-api-server';
|
|
54
|
+
export type { MockAPIServerOptions, MockAPIServerInfo, MockRoute, MockResponse } from './auth/mock-api-server';
|
|
51
55
|
export { TestServer } from './server/test-server';
|
|
52
56
|
export type { TestServerOptions, TestServerInfo } from './server/test-server';
|
|
53
57
|
export { McpAssertions, containsTool, containsResource, containsResourceTemplate, containsPrompt, isSuccessful, isError, hasTextContent, hasMimeType, } from './assertions/mcp-assertions';
|
|
@@ -63,3 +67,6 @@ export type { InterceptorContext, InterceptorResult, RequestInterceptor, Respons
|
|
|
63
67
|
export { httpMock, httpResponse } from './http-mock';
|
|
64
68
|
export type { HttpMethod, HttpRequestMatcher, HttpMockResponse, HttpMockDefinition, HttpRequestInfo, HttpMockHandle, HttpInterceptor, HttpMockManager, } from './http-mock';
|
|
65
69
|
export { uiMatchers, UIAssertions } from './ui';
|
|
70
|
+
export type { TestPlatformType, PlatformMetaNamespace, TestClientInfo } from './platform';
|
|
71
|
+
export { getPlatformMetaNamespace, getPlatformMimeType, isOpenAIPlatform, isExtAppsPlatform, isFrontmcpPlatform, getToolsListMetaPrefixes, getToolCallMetaPrefixes, getForbiddenMetaPrefixes, getPlatformClientInfo, buildUserAgent, getPlatformUserAgent, PLATFORM_DETECTION_PATTERNS, } from './platform';
|
|
72
|
+
export { BASIC_UI_TOOL_CONFIG, FULL_UI_TOOL_CONFIG, basicUIToolInputSchema, basicUIToolOutputSchema, fullUIToolInputSchema, fullUIToolOutputSchema, generateBasicUIToolOutput, generateFullUIToolOutput, EXPECTED_OPENAI_TOOLS_LIST_META_KEYS, EXPECTED_OPENAI_TOOL_CALL_META_KEYS, EXPECTED_EXTAPPS_TOOLS_LIST_META_KEYS, EXPECTED_EXTAPPS_TOOL_CALL_META_KEYS, EXPECTED_FRONTMCP_TOOLS_LIST_META_KEYS, EXPECTED_FRONTMCP_TOOL_CALL_META_KEYS, } from './example-tools';
|
package/src/index.js
CHANGED
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
-
exports.UIAssertions = exports.uiMatchers = exports.httpResponse = exports.httpMock = exports.interceptors = exports.mockResponse = exports.DefaultInterceptorChain = exports.DefaultMockRegistry = exports.mcpMatchers = exports.expect = exports.test = exports.AssertionError = exports.ServerStartError = exports.McpProtocolError = exports.TimeoutError = exports.ConnectionError = exports.TestClientError = exports.hasMimeType = exports.hasTextContent = exports.isError = exports.isSuccessful = exports.containsPrompt = exports.containsResourceTemplate = exports.containsResource = exports.containsTool = exports.McpAssertions = exports.TestServer = exports.createTestUser = exports.TestUsers = exports.AuthHeaders = exports.TestTokenFactory = exports.StreamableHttpTransport = exports.McpTestClientBuilder = exports.McpTestClient = void 0;
|
|
43
|
+
exports.FULL_UI_TOOL_CONFIG = exports.BASIC_UI_TOOL_CONFIG = exports.PLATFORM_DETECTION_PATTERNS = exports.getPlatformUserAgent = exports.buildUserAgent = exports.getPlatformClientInfo = exports.getForbiddenMetaPrefixes = exports.getToolCallMetaPrefixes = exports.getToolsListMetaPrefixes = exports.isFrontmcpPlatform = exports.isExtAppsPlatform = exports.isOpenAIPlatform = exports.getPlatformMimeType = exports.getPlatformMetaNamespace = exports.UIAssertions = exports.uiMatchers = exports.httpResponse = exports.httpMock = exports.interceptors = exports.mockResponse = exports.DefaultInterceptorChain = exports.DefaultMockRegistry = exports.mcpMatchers = exports.expect = exports.test = exports.AssertionError = exports.ServerStartError = exports.McpProtocolError = exports.TimeoutError = exports.ConnectionError = exports.TestClientError = exports.hasMimeType = exports.hasTextContent = exports.isError = exports.isSuccessful = exports.containsPrompt = exports.containsResourceTemplate = exports.containsResource = exports.containsTool = exports.McpAssertions = exports.TestServer = exports.MockAPIServer = exports.MockOAuthServer = exports.createTestUser = exports.TestUsers = exports.AuthHeaders = exports.TestTokenFactory = exports.StreamableHttpTransport = exports.McpTestClientBuilder = exports.McpTestClient = void 0;
|
|
44
|
+
exports.EXPECTED_FRONTMCP_TOOL_CALL_META_KEYS = exports.EXPECTED_FRONTMCP_TOOLS_LIST_META_KEYS = exports.EXPECTED_EXTAPPS_TOOL_CALL_META_KEYS = exports.EXPECTED_EXTAPPS_TOOLS_LIST_META_KEYS = exports.EXPECTED_OPENAI_TOOL_CALL_META_KEYS = exports.EXPECTED_OPENAI_TOOLS_LIST_META_KEYS = exports.generateFullUIToolOutput = exports.generateBasicUIToolOutput = exports.fullUIToolOutputSchema = exports.fullUIToolInputSchema = exports.basicUIToolOutputSchema = exports.basicUIToolInputSchema = void 0;
|
|
44
45
|
// ═══════════════════════════════════════════════════════════════════
|
|
45
46
|
// CLIENT
|
|
46
47
|
// ═══════════════════════════════════════════════════════════════════
|
|
@@ -60,6 +61,10 @@ Object.defineProperty(exports, "AuthHeaders", { enumerable: true, get: function
|
|
|
60
61
|
var user_fixtures_1 = require("./auth/user-fixtures");
|
|
61
62
|
Object.defineProperty(exports, "TestUsers", { enumerable: true, get: function () { return user_fixtures_1.TestUsers; } });
|
|
62
63
|
Object.defineProperty(exports, "createTestUser", { enumerable: true, get: function () { return user_fixtures_1.createTestUser; } });
|
|
64
|
+
var mock_oauth_server_1 = require("./auth/mock-oauth-server");
|
|
65
|
+
Object.defineProperty(exports, "MockOAuthServer", { enumerable: true, get: function () { return mock_oauth_server_1.MockOAuthServer; } });
|
|
66
|
+
var mock_api_server_1 = require("./auth/mock-api-server");
|
|
67
|
+
Object.defineProperty(exports, "MockAPIServer", { enumerable: true, get: function () { return mock_api_server_1.MockAPIServer; } });
|
|
63
68
|
// ═══════════════════════════════════════════════════════════════════
|
|
64
69
|
// SERVER
|
|
65
70
|
// ═══════════════════════════════════════════════════════════════════
|
|
@@ -125,4 +130,38 @@ Object.defineProperty(exports, "httpResponse", { enumerable: true, get: function
|
|
|
125
130
|
var ui_1 = require("./ui");
|
|
126
131
|
Object.defineProperty(exports, "uiMatchers", { enumerable: true, get: function () { return ui_1.uiMatchers; } });
|
|
127
132
|
Object.defineProperty(exports, "UIAssertions", { enumerable: true, get: function () { return ui_1.UIAssertions; } });
|
|
133
|
+
var platform_1 = require("./platform");
|
|
134
|
+
Object.defineProperty(exports, "getPlatformMetaNamespace", { enumerable: true, get: function () { return platform_1.getPlatformMetaNamespace; } });
|
|
135
|
+
Object.defineProperty(exports, "getPlatformMimeType", { enumerable: true, get: function () { return platform_1.getPlatformMimeType; } });
|
|
136
|
+
Object.defineProperty(exports, "isOpenAIPlatform", { enumerable: true, get: function () { return platform_1.isOpenAIPlatform; } });
|
|
137
|
+
Object.defineProperty(exports, "isExtAppsPlatform", { enumerable: true, get: function () { return platform_1.isExtAppsPlatform; } });
|
|
138
|
+
Object.defineProperty(exports, "isFrontmcpPlatform", { enumerable: true, get: function () { return platform_1.isFrontmcpPlatform; } });
|
|
139
|
+
Object.defineProperty(exports, "getToolsListMetaPrefixes", { enumerable: true, get: function () { return platform_1.getToolsListMetaPrefixes; } });
|
|
140
|
+
Object.defineProperty(exports, "getToolCallMetaPrefixes", { enumerable: true, get: function () { return platform_1.getToolCallMetaPrefixes; } });
|
|
141
|
+
Object.defineProperty(exports, "getForbiddenMetaPrefixes", { enumerable: true, get: function () { return platform_1.getForbiddenMetaPrefixes; } });
|
|
142
|
+
Object.defineProperty(exports, "getPlatformClientInfo", { enumerable: true, get: function () { return platform_1.getPlatformClientInfo; } });
|
|
143
|
+
Object.defineProperty(exports, "buildUserAgent", { enumerable: true, get: function () { return platform_1.buildUserAgent; } });
|
|
144
|
+
Object.defineProperty(exports, "getPlatformUserAgent", { enumerable: true, get: function () { return platform_1.getPlatformUserAgent; } });
|
|
145
|
+
Object.defineProperty(exports, "PLATFORM_DETECTION_PATTERNS", { enumerable: true, get: function () { return platform_1.PLATFORM_DETECTION_PATTERNS; } });
|
|
146
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
147
|
+
// EXAMPLE TOOLS (for E2E platform testing)
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
149
|
+
var example_tools_1 = require("./example-tools");
|
|
150
|
+
// Tool configurations
|
|
151
|
+
Object.defineProperty(exports, "BASIC_UI_TOOL_CONFIG", { enumerable: true, get: function () { return example_tools_1.BASIC_UI_TOOL_CONFIG; } });
|
|
152
|
+
Object.defineProperty(exports, "FULL_UI_TOOL_CONFIG", { enumerable: true, get: function () { return example_tools_1.FULL_UI_TOOL_CONFIG; } });
|
|
153
|
+
Object.defineProperty(exports, "basicUIToolInputSchema", { enumerable: true, get: function () { return example_tools_1.basicUIToolInputSchema; } });
|
|
154
|
+
Object.defineProperty(exports, "basicUIToolOutputSchema", { enumerable: true, get: function () { return example_tools_1.basicUIToolOutputSchema; } });
|
|
155
|
+
Object.defineProperty(exports, "fullUIToolInputSchema", { enumerable: true, get: function () { return example_tools_1.fullUIToolInputSchema; } });
|
|
156
|
+
Object.defineProperty(exports, "fullUIToolOutputSchema", { enumerable: true, get: function () { return example_tools_1.fullUIToolOutputSchema; } });
|
|
157
|
+
// Execution helpers
|
|
158
|
+
Object.defineProperty(exports, "generateBasicUIToolOutput", { enumerable: true, get: function () { return example_tools_1.generateBasicUIToolOutput; } });
|
|
159
|
+
Object.defineProperty(exports, "generateFullUIToolOutput", { enumerable: true, get: function () { return example_tools_1.generateFullUIToolOutput; } });
|
|
160
|
+
// Expected meta keys
|
|
161
|
+
Object.defineProperty(exports, "EXPECTED_OPENAI_TOOLS_LIST_META_KEYS", { enumerable: true, get: function () { return example_tools_1.EXPECTED_OPENAI_TOOLS_LIST_META_KEYS; } });
|
|
162
|
+
Object.defineProperty(exports, "EXPECTED_OPENAI_TOOL_CALL_META_KEYS", { enumerable: true, get: function () { return example_tools_1.EXPECTED_OPENAI_TOOL_CALL_META_KEYS; } });
|
|
163
|
+
Object.defineProperty(exports, "EXPECTED_EXTAPPS_TOOLS_LIST_META_KEYS", { enumerable: true, get: function () { return example_tools_1.EXPECTED_EXTAPPS_TOOLS_LIST_META_KEYS; } });
|
|
164
|
+
Object.defineProperty(exports, "EXPECTED_EXTAPPS_TOOL_CALL_META_KEYS", { enumerable: true, get: function () { return example_tools_1.EXPECTED_EXTAPPS_TOOL_CALL_META_KEYS; } });
|
|
165
|
+
Object.defineProperty(exports, "EXPECTED_FRONTMCP_TOOLS_LIST_META_KEYS", { enumerable: true, get: function () { return example_tools_1.EXPECTED_FRONTMCP_TOOLS_LIST_META_KEYS; } });
|
|
166
|
+
Object.defineProperty(exports, "EXPECTED_FRONTMCP_TOOL_CALL_META_KEYS", { enumerable: true, get: function () { return example_tools_1.EXPECTED_FRONTMCP_TOOL_CALL_META_KEYS; } });
|
|
128
167
|
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;;;AAEH,sEAAsE;AACtE,SAAS;AACT,sEAAsE;AAEtE,4DAAyD;AAAhD,gHAAA,aAAa,OAAA;AACtB,4EAAwE;AAA/D,+HAAA,oBAAoB,OAAA;AAwB7B,mFAAgF;AAAvE,oIAAA,uBAAuB,OAAA;AAEhC,sEAAsE;AACtE,OAAO;AACP,sEAAsE;AAEtE,sDAAwD;AAA/C,iHAAA,gBAAgB,OAAA;AAEzB,oDAAkD;AAAzC,2GAAA,WAAW,OAAA;AACpB,sDAAiE;AAAxD,0GAAA,SAAS,OAAA;AAAE,+GAAA,cAAc,OAAA;AAGlC,sEAAsE;AACtE,SAAS;AACT,sEAAsE;AAEtE,oDAAkD;AAAzC,yGAAA,UAAU,OAAA;AAGnB,sEAAsE;AACtE,aAAa;AACb,sEAAsE;AAEtE,8DAUqC;AATnC,+GAAA,aAAa,OAAA;AACb,8GAAA,YAAY,OAAA;AACZ,kHAAA,gBAAgB,OAAA;AAChB,0HAAA,wBAAwB,OAAA;AACxB,gHAAA,cAAc,OAAA;AACd,8GAAA,YAAY,OAAA;AACZ,yGAAA,OAAO,OAAA;AACP,gHAAA,cAAc,OAAA;AACd,6GAAA,WAAW,OAAA;AAGb,sEAAsE;AACtE,SAAS;AACT,sEAAsE;AAEtE,wCAOwB;AANtB,wGAAA,eAAe,OAAA;AACf,wGAAA,eAAe,OAAA;AACf,qGAAA,YAAY,OAAA;AACZ,yGAAA,gBAAgB,OAAA;AAChB,yGAAA,gBAAgB,OAAA;AAChB,uGAAA,cAAc,OAAA;AAyBhB,sEAAsE;AACtE,yBAAyB;AACzB,sEAAsE;AAEtE,uCAAkC;AAAzB,gGAAA,IAAI,OAAA;AAWb,sEAAsE;AACtE,uBAAuB;AACvB,sEAAsE;AAEtE,4EAA4E;AAC5E,+EAA+E;AAC/E,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AAEf,sEAAsE;AACtE,kBAAkB;AAClB,sEAAsE;AAEtE,uCAAyC;AAAhC,uGAAA,WAAW,OAAA;AAGpB,sEAAsE;AACtE,yBAAyB;AACzB,sEAAsE;AAEtE,6CAAyG;AAAhG,kHAAA,mBAAmB,OAAA;AAAE,sHAAA,uBAAuB,OAAA;AAAE,2GAAA,YAAY,OAAA;AAAE,2GAAA,YAAY,OAAA;AAejF,sEAAsE;AACtE,qCAAqC;AACrC,sEAAsE;AAEtE,yCAAqD;AAA5C,qGAAA,QAAQ,OAAA;AAAE,yGAAA,YAAY,OAAA;AAa/B,sEAAsE;AACtE,aAAa;AACb,sEAAsE;AAEtE,2BAAgD;AAAvC,gGAAA,UAAU,OAAA;AAAE,kGAAA,YAAY,OAAA","sourcesContent":["/**\n * @file index.ts\n * @description Main barrel exports for @frontmcp/testing\n *\n * @example Quick Start with Fixtures\n * ```typescript\n * import { test, expect } from '@frontmcp/testing';\n *\n * test.use({\n * server: './src/main.ts',\n * port: 3003,\n * });\n *\n * test('server exposes tools', async ({ mcp }) => {\n * const tools = await mcp.tools.list();\n * expect(tools).toContainTool('my-tool');\n * });\n *\n * test('tool execution works', async ({ mcp }) => {\n * const result = await mcp.tools.call('my-tool', { input: 'test' });\n * expect(result).toBeSuccessful();\n * });\n * ```\n *\n * @example Manual Client Usage\n * ```typescript\n * import { McpTestClient, TestServer } from '@frontmcp/testing';\n *\n * const server = await TestServer.start({ command: 'npx tsx src/main.ts' });\n * const client = await McpTestClient.create({ baseUrl: server.info.baseUrl })\n * .withTransport('streamable-http')\n * .buildAndConnect();\n *\n * const tools = await client.tools.list();\n * console.log(tools);\n *\n * await client.disconnect();\n * await server.stop();\n * ```\n */\n\n// ═══════════════════════════════════════════════════════════════════\n// CLIENT\n// ═══════════════════════════════════════════════════════════════════\n\nexport { McpTestClient } from './client/mcp-test-client';\nexport { McpTestClientBuilder } from './client/mcp-test-client.builder';\nexport type {\n McpTestClientConfig,\n McpResponse,\n McpErrorInfo,\n TestTransportType,\n TestAuthConfig,\n ToolResultWrapper,\n ResourceContentWrapper,\n PromptResultWrapper,\n LogEntry,\n LogLevel,\n RequestTrace,\n NotificationEntry,\n ProgressUpdate,\n SessionInfo,\n AuthState,\n} from './client/mcp-test-client.types';\n\n// ═══════════════════════════════════════════════════════════════════\n// TRANSPORT\n// ═══════════════════════════════════════════════════════════════════\n\nexport type { McpTransport, TransportConfig, TransportState } from './transport/transport.interface';\nexport { StreamableHttpTransport } from './transport/streamable-http.transport';\n\n// ═══════════════════════════════════════════════════════════════════\n// AUTH\n// ═══════════════════════════════════════════════════════════════════\n\nexport { TestTokenFactory } from './auth/token-factory';\nexport type { CreateTokenOptions, TokenFactoryOptions } from './auth/token-factory';\nexport { AuthHeaders } from './auth/auth-headers';\nexport { TestUsers, createTestUser } from './auth/user-fixtures';\nexport type { TestUserFixture } from './auth/user-fixtures';\n\n// ═══════════════════════════════════════════════════════════════════\n// SERVER\n// ═══════════════════════════════════════════════════════════════════\n\nexport { TestServer } from './server/test-server';\nexport type { TestServerOptions, TestServerInfo } from './server/test-server';\n\n// ═══════════════════════════════════════════════════════════════════\n// ASSERTIONS\n// ═══════════════════════════════════════════════════════════════════\n\nexport {\n McpAssertions,\n containsTool,\n containsResource,\n containsResourceTemplate,\n containsPrompt,\n isSuccessful,\n isError,\n hasTextContent,\n hasMimeType,\n} from './assertions/mcp-assertions';\n\n// ═══════════════════════════════════════════════════════════════════\n// ERRORS\n// ═══════════════════════════════════════════════════════════════════\n\nexport {\n TestClientError,\n ConnectionError,\n TimeoutError,\n McpProtocolError,\n ServerStartError,\n AssertionError,\n} from './errors/index';\n\n// ═══════════════════════════════════════════════════════════════════\n// RE-EXPORTS FROM MCP SDK\n// ═══════════════════════════════════════════════════════════════════\n\nexport type {\n InitializeResult,\n ListToolsResult,\n CallToolResult,\n ListResourcesResult,\n ReadResourceResult,\n ListResourceTemplatesResult,\n ListPromptsResult,\n GetPromptResult,\n Tool,\n Resource,\n ResourceTemplate,\n Prompt,\n // JSON-RPC types\n JSONRPCRequest,\n JSONRPCResponse,\n} from './client/mcp-test-client.types';\n\n// ═══════════════════════════════════════════════════════════════════\n// FIXTURES (Primary API)\n// ═══════════════════════════════════════════════════════════════════\n\nexport { test } from './fixtures';\nexport type {\n TestConfig,\n TestFixtures,\n AuthFixture,\n ServerFixture,\n TestFn,\n TestWithFixtures,\n TestUser,\n} from './fixtures';\n\n// ═══════════════════════════════════════════════════════════════════\n// EXPECT (Primary API)\n// ═══════════════════════════════════════════════════════════════════\n\n// Export the pre-typed expect with MCP matchers (Playwright-style approach)\n// This provides proper typing without relying on global namespace augmentation\nexport { expect } from './expect';\n\n// ═══════════════════════════════════════════════════════════════════\n// CUSTOM MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\nexport { mcpMatchers } from './matchers';\nexport type { McpMatchers } from './matchers';\n\n// ═══════════════════════════════════════════════════════════════════\n// INTERCEPTORS & MOCKING\n// ═══════════════════════════════════════════════════════════════════\n\nexport { DefaultMockRegistry, DefaultInterceptorChain, mockResponse, interceptors } from './interceptor';\n\nexport type {\n InterceptorContext,\n InterceptorResult,\n RequestInterceptor,\n ResponseInterceptorContext,\n ResponseInterceptorResult,\n ResponseInterceptor,\n MockDefinition,\n MockRegistry,\n MockHandle,\n InterceptorChain,\n} from './interceptor';\n\n// ═══════════════════════════════════════════════════════════════════\n// HTTP MOCKING (for offline testing)\n// ═══════════════════════════════════════════════════════════════════\n\nexport { httpMock, httpResponse } from './http-mock';\n\nexport type {\n HttpMethod,\n HttpRequestMatcher,\n HttpMockResponse,\n HttpMockDefinition,\n HttpRequestInfo,\n HttpMockHandle,\n HttpInterceptor,\n HttpMockManager,\n} from './http-mock';\n\n// ═══════════════════════════════════════════════════════════════════\n// UI TESTING\n// ═══════════════════════════════════════════════════════════════════\n\nexport { uiMatchers, UIAssertions } from './ui';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file index.ts\n * @description Main barrel exports for @frontmcp/testing\n *\n * @example Quick Start with Fixtures\n * ```typescript\n * import { test, expect } from '@frontmcp/testing';\n *\n * test.use({\n * server: './src/main.ts',\n * port: 3003,\n * });\n *\n * test('server exposes tools', async ({ mcp }) => {\n * const tools = await mcp.tools.list();\n * expect(tools).toContainTool('my-tool');\n * });\n *\n * test('tool execution works', async ({ mcp }) => {\n * const result = await mcp.tools.call('my-tool', { input: 'test' });\n * expect(result).toBeSuccessful();\n * });\n * ```\n *\n * @example Manual Client Usage\n * ```typescript\n * import { McpTestClient, TestServer } from '@frontmcp/testing';\n *\n * const server = await TestServer.start({ command: 'npx tsx src/main.ts' });\n * const client = await McpTestClient.create({ baseUrl: server.info.baseUrl })\n * .withTransport('streamable-http')\n * .buildAndConnect();\n *\n * const tools = await client.tools.list();\n * console.log(tools);\n *\n * await client.disconnect();\n * await server.stop();\n * ```\n */\n\n// ═══════════════════════════════════════════════════════════════════\n// CLIENT\n// ═══════════════════════════════════════════════════════════════════\n\nexport { McpTestClient } from './client/mcp-test-client';\nexport { McpTestClientBuilder } from './client/mcp-test-client.builder';\nexport type {\n McpTestClientConfig,\n McpResponse,\n McpErrorInfo,\n TestTransportType,\n TestAuthConfig,\n ToolResultWrapper,\n ResourceContentWrapper,\n PromptResultWrapper,\n LogEntry,\n LogLevel,\n RequestTrace,\n NotificationEntry,\n ProgressUpdate,\n SessionInfo,\n AuthState,\n} from './client/mcp-test-client.types';\n\n// ═══════════════════════════════════════════════════════════════════\n// TRANSPORT\n// ═══════════════════════════════════════════════════════════════════\n\nexport type { McpTransport, TransportConfig, TransportState } from './transport/transport.interface';\nexport { StreamableHttpTransport } from './transport/streamable-http.transport';\n\n// ═══════════════════════════════════════════════════════════════════\n// AUTH\n// ═══════════════════════════════════════════════════════════════════\n\nexport { TestTokenFactory } from './auth/token-factory';\nexport type { CreateTokenOptions, TokenFactoryOptions } from './auth/token-factory';\nexport { AuthHeaders } from './auth/auth-headers';\nexport { TestUsers, createTestUser } from './auth/user-fixtures';\nexport type { TestUserFixture } from './auth/user-fixtures';\nexport { MockOAuthServer } from './auth/mock-oauth-server';\nexport type { MockOAuthServerOptions, MockOAuthServerInfo } from './auth/mock-oauth-server';\nexport { MockAPIServer } from './auth/mock-api-server';\nexport type { MockAPIServerOptions, MockAPIServerInfo, MockRoute, MockResponse } from './auth/mock-api-server';\n\n// ═══════════════════════════════════════════════════════════════════\n// SERVER\n// ═══════════════════════════════════════════════════════════════════\n\nexport { TestServer } from './server/test-server';\nexport type { TestServerOptions, TestServerInfo } from './server/test-server';\n\n// ═══════════════════════════════════════════════════════════════════\n// ASSERTIONS\n// ═══════════════════════════════════════════════════════════════════\n\nexport {\n McpAssertions,\n containsTool,\n containsResource,\n containsResourceTemplate,\n containsPrompt,\n isSuccessful,\n isError,\n hasTextContent,\n hasMimeType,\n} from './assertions/mcp-assertions';\n\n// ═══════════════════════════════════════════════════════════════════\n// ERRORS\n// ═══════════════════════════════════════════════════════════════════\n\nexport {\n TestClientError,\n ConnectionError,\n TimeoutError,\n McpProtocolError,\n ServerStartError,\n AssertionError,\n} from './errors/index';\n\n// ═══════════════════════════════════════════════════════════════════\n// RE-EXPORTS FROM MCP SDK\n// ═══════════════════════════════════════════════════════════════════\n\nexport type {\n InitializeResult,\n ListToolsResult,\n CallToolResult,\n ListResourcesResult,\n ReadResourceResult,\n ListResourceTemplatesResult,\n ListPromptsResult,\n GetPromptResult,\n Tool,\n Resource,\n ResourceTemplate,\n Prompt,\n // JSON-RPC types\n JSONRPCRequest,\n JSONRPCResponse,\n} from './client/mcp-test-client.types';\n\n// ═══════════════════════════════════════════════════════════════════\n// FIXTURES (Primary API)\n// ═══════════════════════════════════════════════════════════════════\n\nexport { test } from './fixtures';\nexport type {\n TestConfig,\n TestFixtures,\n AuthFixture,\n ServerFixture,\n TestFn,\n TestWithFixtures,\n TestUser,\n} from './fixtures';\n\n// ═══════════════════════════════════════════════════════════════════\n// EXPECT (Primary API)\n// ═══════════════════════════════════════════════════════════════════\n\n// Export the pre-typed expect with MCP matchers (Playwright-style approach)\n// This provides proper typing without relying on global namespace augmentation\nexport { expect } from './expect';\n\n// ═══════════════════════════════════════════════════════════════════\n// CUSTOM MATCHERS\n// ═══════════════════════════════════════════════════════════════════\n\nexport { mcpMatchers } from './matchers';\nexport type { McpMatchers } from './matchers';\n\n// ═══════════════════════════════════════════════════════════════════\n// INTERCEPTORS & MOCKING\n// ═══════════════════════════════════════════════════════════════════\n\nexport { DefaultMockRegistry, DefaultInterceptorChain, mockResponse, interceptors } from './interceptor';\n\nexport type {\n InterceptorContext,\n InterceptorResult,\n RequestInterceptor,\n ResponseInterceptorContext,\n ResponseInterceptorResult,\n ResponseInterceptor,\n MockDefinition,\n MockRegistry,\n MockHandle,\n InterceptorChain,\n} from './interceptor';\n\n// ═══════════════════════════════════════════════════════════════════\n// HTTP MOCKING (for offline testing)\n// ═══════════════════════════════════════════════════════════════════\n\nexport { httpMock, httpResponse } from './http-mock';\n\nexport type {\n HttpMethod,\n HttpRequestMatcher,\n HttpMockResponse,\n HttpMockDefinition,\n HttpRequestInfo,\n HttpMockHandle,\n HttpInterceptor,\n HttpMockManager,\n} from './http-mock';\n\n// ═══════════════════════════════════════════════════════════════════\n// UI TESTING\n// ═══════════════════════════════════════════════════════════════════\n\nexport { uiMatchers, UIAssertions } from './ui';\n\n// ═══════════════════════════════════════════════════════════════════\n// PLATFORM TESTING\n// ═══════════════════════════════════════════════════════════════════\n\nexport type { TestPlatformType, PlatformMetaNamespace, TestClientInfo } from './platform';\n\nexport {\n getPlatformMetaNamespace,\n getPlatformMimeType,\n isOpenAIPlatform,\n isExtAppsPlatform,\n isFrontmcpPlatform,\n getToolsListMetaPrefixes,\n getToolCallMetaPrefixes,\n getForbiddenMetaPrefixes,\n getPlatformClientInfo,\n buildUserAgent,\n getPlatformUserAgent,\n PLATFORM_DETECTION_PATTERNS,\n} from './platform';\n\n// ═══════════════════════════════════════════════════════════════════\n// EXAMPLE TOOLS (for E2E platform testing)\n// ═══════════════════════════════════════════════════════════════════\n\nexport {\n // Tool configurations\n BASIC_UI_TOOL_CONFIG,\n FULL_UI_TOOL_CONFIG,\n basicUIToolInputSchema,\n basicUIToolOutputSchema,\n fullUIToolInputSchema,\n fullUIToolOutputSchema,\n // Execution helpers\n generateBasicUIToolOutput,\n generateFullUIToolOutput,\n // Expected meta keys\n EXPECTED_OPENAI_TOOLS_LIST_META_KEYS,\n EXPECTED_OPENAI_TOOL_CALL_META_KEYS,\n EXPECTED_EXTAPPS_TOOLS_LIST_META_KEYS,\n EXPECTED_EXTAPPS_TOOL_CALL_META_KEYS,\n EXPECTED_FRONTMCP_TOOLS_LIST_META_KEYS,\n EXPECTED_FRONTMCP_TOOL_CALL_META_KEYS,\n} from './example-tools';\n"]}
|
|
@@ -1 +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
|
|
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, @typescript-eslint/no-empty-interface */\n\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, @typescript-eslint/no-empty-interface */\n\n// This export is needed to make this a module\nexport {};\n"]}
|
|
@@ -29,6 +29,13 @@ export declare const mcpMatchers: {
|
|
|
29
29
|
toHaveCssClass: MatcherFunction<[className: string]>;
|
|
30
30
|
toNotContainRawContent: MatcherFunction<[content: string]>;
|
|
31
31
|
toHaveProperHtmlStructure: MatcherFunction<[]>;
|
|
32
|
+
toHavePlatformMeta: MatcherFunction<[platform: import("..").TestPlatformType]>;
|
|
33
|
+
toHaveMetaKey: MatcherFunction<[key: string]>;
|
|
34
|
+
toHaveMetaValue: MatcherFunction<[key: string, value: unknown]>;
|
|
35
|
+
toNotHaveMetaKey: MatcherFunction<[key: string]>;
|
|
36
|
+
toHaveOnlyNamespacedMeta: MatcherFunction<[namespace: string]>;
|
|
37
|
+
toHavePlatformMimeType: MatcherFunction<[platform: import("..").TestPlatformType]>;
|
|
38
|
+
toHavePlatformHtml: MatcherFunction<[platform: import("..").TestPlatformType]>;
|
|
32
39
|
toContainTool: MatcherFunction<[toolName: string]>;
|
|
33
40
|
toBeSuccessful: MatcherFunction<[]>;
|
|
34
41
|
toBeError: MatcherFunction<[expectedCode?: number | undefined]>;
|
|
@@ -91,18 +91,22 @@ const toBeError = function (received, expectedCode) {
|
|
|
91
91
|
};
|
|
92
92
|
};
|
|
93
93
|
/**
|
|
94
|
-
* Check if tool result has text content, optionally containing specific text
|
|
94
|
+
* Check if tool result or resource content has text content, optionally containing specific text
|
|
95
|
+
* Works with both ToolResultWrapper and ResourceContentWrapper
|
|
95
96
|
*/
|
|
96
97
|
const toHaveTextContent = function (received, expectedText) {
|
|
97
98
|
const result = received;
|
|
98
|
-
|
|
99
|
+
// Check if it's a valid wrapper object with text() method
|
|
100
|
+
if (typeof result !== 'object' || result === null || !('text' in result)) {
|
|
99
101
|
return {
|
|
100
102
|
pass: false,
|
|
101
|
-
message: () => `Expected a ToolResultWrapper object with
|
|
103
|
+
message: () => `Expected a ToolResultWrapper or ResourceContentWrapper object with text method`,
|
|
102
104
|
};
|
|
103
105
|
}
|
|
104
|
-
|
|
106
|
+
// Get text content - works for both wrapper types
|
|
105
107
|
const text = result.text();
|
|
108
|
+
// Check if has text content - ToolResultWrapper has hasTextContent, ResourceContentWrapper uses text() !== undefined
|
|
109
|
+
const hasText = 'hasTextContent' in result ? result.hasTextContent() : text !== undefined;
|
|
106
110
|
let pass = hasText;
|
|
107
111
|
if (pass && expectedText !== undefined) {
|
|
108
112
|
pass = text?.includes(expectedText) ?? false;
|