@frontmcp/testing 0.6.0 → 0.6.2
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/esm/fixtures/index.mjs +2377 -0
- package/esm/index.mjs +4768 -0
- package/esm/matchers/index.mjs +646 -0
- package/esm/package.json +127 -0
- package/esm/playwright/index.mjs +19 -0
- package/esm/setup.mjs +680 -0
- package/fixtures/index.js +2418 -0
- package/index.js +4866 -0
- package/jest-preset.js +3 -3
- package/matchers/index.js +673 -0
- package/package.json +52 -24
- package/playwright/index.js +46 -0
- package/setup.js +651 -0
- package/src/assertions/index.js +0 -18
- package/src/assertions/index.js.map +0 -1
- package/src/assertions/mcp-assertions.js +0 -220
- package/src/assertions/mcp-assertions.js.map +0 -1
- package/src/auth/auth-headers.js +0 -62
- package/src/auth/auth-headers.js.map +0 -1
- package/src/auth/index.js +0 -15
- package/src/auth/index.js.map +0 -1
- package/src/auth/mock-api-server.js +0 -200
- package/src/auth/mock-api-server.js.map +0 -1
- package/src/auth/mock-oauth-server.js +0 -253
- package/src/auth/mock-oauth-server.js.map +0 -1
- package/src/auth/token-factory.js +0 -181
- package/src/auth/token-factory.js.map +0 -1
- package/src/auth/user-fixtures.js +0 -92
- package/src/auth/user-fixtures.js.map +0 -1
- package/src/client/index.js +0 -12
- package/src/client/index.js.map +0 -1
- package/src/client/mcp-test-client.builder.js +0 -163
- package/src/client/mcp-test-client.builder.js.map +0 -1
- package/src/client/mcp-test-client.js +0 -937
- package/src/client/mcp-test-client.js.map +0 -1
- package/src/client/mcp-test-client.types.js +0 -16
- package/src/client/mcp-test-client.types.js.map +0 -1
- package/src/errors/index.js +0 -85
- package/src/errors/index.js.map +0 -1
- package/src/example-tools/index.js +0 -40
- package/src/example-tools/index.js.map +0 -1
- package/src/example-tools/tool-configs.js +0 -222
- package/src/example-tools/tool-configs.js.map +0 -1
- package/src/expect.js +0 -31
- package/src/expect.js.map +0 -1
- package/src/fixtures/fixture-types.js +0 -7
- package/src/fixtures/fixture-types.js.map +0 -1
- package/src/fixtures/index.js +0 -16
- package/src/fixtures/index.js.map +0 -1
- package/src/fixtures/test-fixture.js +0 -311
- package/src/fixtures/test-fixture.js.map +0 -1
- package/src/http-mock/http-mock.js +0 -544
- package/src/http-mock/http-mock.js.map +0 -1
- package/src/http-mock/http-mock.types.js +0 -10
- package/src/http-mock/http-mock.types.js.map +0 -1
- package/src/http-mock/index.js +0 -11
- package/src/http-mock/index.js.map +0 -1
- package/src/index.js +0 -167
- package/src/index.js.map +0 -1
- package/src/interceptor/index.js +0 -15
- package/src/interceptor/index.js.map +0 -1
- package/src/interceptor/interceptor-chain.js +0 -207
- package/src/interceptor/interceptor-chain.js.map +0 -1
- package/src/interceptor/interceptor.types.js +0 -7
- package/src/interceptor/interceptor.types.js.map +0 -1
- package/src/interceptor/mock-registry.js +0 -189
- package/src/interceptor/mock-registry.js.map +0 -1
- package/src/matchers/index.js +0 -12
- package/src/matchers/index.js.map +0 -1
- package/src/matchers/matcher-types.js +0 -10
- package/src/matchers/matcher-types.js.map +0 -1
- package/src/matchers/mcp-matchers.js +0 -395
- package/src/matchers/mcp-matchers.js.map +0 -1
- package/src/platform/index.js +0 -47
- package/src/platform/index.js.map +0 -1
- package/src/platform/platform-client-info.js +0 -155
- package/src/platform/platform-client-info.js.map +0 -1
- package/src/platform/platform-types.js +0 -110
- package/src/platform/platform-types.js.map +0 -1
- package/src/playwright/index.js +0 -49
- package/src/playwright/index.js.map +0 -1
- package/src/server/index.js +0 -10
- package/src/server/index.js.map +0 -1
- package/src/server/test-server.js +0 -341
- package/src/server/test-server.js.map +0 -1
- package/src/setup.js +0 -30
- package/src/setup.js.map +0 -1
- package/src/transport/index.js +0 -10
- package/src/transport/index.js.map +0 -1
- package/src/transport/streamable-http.transport.js +0 -438
- package/src/transport/streamable-http.transport.js.map +0 -1
- package/src/transport/transport.interface.js +0 -7
- package/src/transport/transport.interface.js.map +0 -1
- package/src/ui/index.js +0 -23
- package/src/ui/index.js.map +0 -1
- package/src/ui/ui-assertions.js +0 -367
- package/src/ui/ui-assertions.js.map +0 -1
- package/src/ui/ui-matchers.js +0 -493
- package/src/ui/ui-matchers.js.map +0 -1
- /package/{src/assertions → assertions}/index.d.ts +0 -0
- /package/{src/assertions → assertions}/mcp-assertions.d.ts +0 -0
- /package/{src/auth → auth}/auth-headers.d.ts +0 -0
- /package/{src/auth → auth}/index.d.ts +0 -0
- /package/{src/auth → auth}/mock-api-server.d.ts +0 -0
- /package/{src/auth → auth}/mock-oauth-server.d.ts +0 -0
- /package/{src/auth → auth}/token-factory.d.ts +0 -0
- /package/{src/auth → auth}/user-fixtures.d.ts +0 -0
- /package/{src/client → client}/index.d.ts +0 -0
- /package/{src/client → client}/mcp-test-client.builder.d.ts +0 -0
- /package/{src/client → client}/mcp-test-client.d.ts +0 -0
- /package/{src/client → client}/mcp-test-client.types.d.ts +0 -0
- /package/{src/errors → errors}/index.d.ts +0 -0
- /package/{src/example-tools → example-tools}/index.d.ts +0 -0
- /package/{src/example-tools → example-tools}/tool-configs.d.ts +0 -0
- /package/{src/expect.d.ts → expect.d.ts} +0 -0
- /package/{src/fixtures → fixtures}/fixture-types.d.ts +0 -0
- /package/{src/fixtures → fixtures}/index.d.ts +0 -0
- /package/{src/fixtures → fixtures}/test-fixture.d.ts +0 -0
- /package/{src/http-mock → http-mock}/http-mock.d.ts +0 -0
- /package/{src/http-mock → http-mock}/http-mock.types.d.ts +0 -0
- /package/{src/http-mock → http-mock}/index.d.ts +0 -0
- /package/{src/index.d.ts → index.d.ts} +0 -0
- /package/{src/interceptor → interceptor}/index.d.ts +0 -0
- /package/{src/interceptor → interceptor}/interceptor-chain.d.ts +0 -0
- /package/{src/interceptor → interceptor}/interceptor.types.d.ts +0 -0
- /package/{src/interceptor → interceptor}/mock-registry.d.ts +0 -0
- /package/{src/matchers → matchers}/index.d.ts +0 -0
- /package/{src/matchers → matchers}/matcher-types.d.ts +0 -0
- /package/{src/matchers → matchers}/mcp-matchers.d.ts +0 -0
- /package/{src/platform → platform}/index.d.ts +0 -0
- /package/{src/platform → platform}/platform-client-info.d.ts +0 -0
- /package/{src/platform → platform}/platform-types.d.ts +0 -0
- /package/{src/playwright → playwright}/index.d.ts +0 -0
- /package/{src/server → server}/index.d.ts +0 -0
- /package/{src/server → server}/test-server.d.ts +0 -0
- /package/{src/setup.d.ts → setup.d.ts} +0 -0
- /package/{src/transport → transport}/index.d.ts +0 -0
- /package/{src/transport → transport}/streamable-http.transport.d.ts +0 -0
- /package/{src/transport → transport}/transport.interface.d.ts +0 -0
- /package/{src/ui → ui}/index.d.ts +0 -0
- /package/{src/ui → ui}/ui-assertions.d.ts +0 -0
- /package/{src/ui → ui}/ui-matchers.d.ts +0 -0
package/src/expect.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @file expect.ts
|
|
4
|
-
* @description Pre-typed expect export with MCP custom matchers
|
|
5
|
-
*
|
|
6
|
-
* This is the Playwright-style approach - instead of relying on global type
|
|
7
|
-
* augmentation (which can be fragile across monorepos and path mappings),
|
|
8
|
-
* we export a properly typed expect function that includes all MCP matchers.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```typescript
|
|
12
|
-
* import { test, expect } from '@frontmcp/testing';
|
|
13
|
-
*
|
|
14
|
-
* test('tools are available', async ({ mcp }) => {
|
|
15
|
-
* const tools = await mcp.tools.list();
|
|
16
|
-
* expect(tools).toContainTool('my-tool'); // Properly typed!
|
|
17
|
-
* });
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.expect = void 0;
|
|
22
|
-
const globals_1 = require("@jest/globals");
|
|
23
|
-
/**
|
|
24
|
-
* Pre-typed expect with MCP custom matchers included
|
|
25
|
-
*
|
|
26
|
-
* This approach (similar to Playwright's) provides type safety without
|
|
27
|
-
* relying on global TypeScript namespace augmentation, which can be
|
|
28
|
-
* problematic in monorepo setups with path mappings.
|
|
29
|
-
*/
|
|
30
|
-
exports.expect = globals_1.expect;
|
|
31
|
-
//# sourceMappingURL=expect.js.map
|
package/src/expect.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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"]}
|
|
@@ -1 +0,0 @@
|
|
|
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"]}
|
package/src/fixtures/index.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @file index.ts
|
|
4
|
-
* @description Barrel exports for test fixtures
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.cleanupSharedResources = exports.initializeSharedResources = exports.cleanupTestFixtures = exports.createTestFixtures = exports.test = void 0;
|
|
8
|
-
var test_fixture_1 = require("./test-fixture");
|
|
9
|
-
Object.defineProperty(exports, "test", { enumerable: true, get: function () { return test_fixture_1.test; } });
|
|
10
|
-
// Advanced exports for custom setups
|
|
11
|
-
var test_fixture_2 = require("./test-fixture");
|
|
12
|
-
Object.defineProperty(exports, "createTestFixtures", { enumerable: true, get: function () { return test_fixture_2.createTestFixtures; } });
|
|
13
|
-
Object.defineProperty(exports, "cleanupTestFixtures", { enumerable: true, get: function () { return test_fixture_2.cleanupTestFixtures; } });
|
|
14
|
-
Object.defineProperty(exports, "initializeSharedResources", { enumerable: true, get: function () { return test_fixture_2.initializeSharedResources; } });
|
|
15
|
-
Object.defineProperty(exports, "cleanupSharedResources", { enumerable: true, get: function () { return test_fixture_2.cleanupSharedResources; } });
|
|
16
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/fixtures/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+CAAsC;AAA7B,oGAAA,IAAI,OAAA;AAWb,qCAAqC;AACrC,+CAKwB;AAJtB,kHAAA,kBAAkB,OAAA;AAClB,mHAAA,mBAAmB,OAAA;AACnB,yHAAA,yBAAyB,OAAA;AACzB,sHAAA,sBAAsB,OAAA","sourcesContent":["/**\n * @file index.ts\n * @description Barrel exports for test fixtures\n */\n\nexport { test } from './test-fixture';\nexport type {\n TestConfig,\n TestFixtures,\n AuthFixture,\n ServerFixture,\n TestFn,\n TestWithFixtures,\n TestUser,\n} from './fixture-types';\n\n// Advanced exports for custom setups\nexport {\n createTestFixtures,\n cleanupTestFixtures,\n initializeSharedResources,\n cleanupSharedResources,\n} from './test-fixture';\n"]}
|
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @file test-fixture.ts
|
|
4
|
-
* @description Jest-based fixture system for MCP testing
|
|
5
|
-
*
|
|
6
|
-
* Provides a Playwright-like fixture API for testing FrontMCP servers:
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```typescript
|
|
10
|
-
* import { test, expect } from '@frontmcp/testing';
|
|
11
|
-
*
|
|
12
|
-
* test.use({
|
|
13
|
-
* server: './src/main.ts',
|
|
14
|
-
* port: 3003,
|
|
15
|
-
* });
|
|
16
|
-
*
|
|
17
|
-
* test('server exposes tools', async ({ mcp }) => {
|
|
18
|
-
* const tools = await mcp.tools.list();
|
|
19
|
-
* expect(tools).toContainTool('my-tool');
|
|
20
|
-
* });
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.test = void 0;
|
|
25
|
-
exports.createTestFixtures = createTestFixtures;
|
|
26
|
-
exports.cleanupTestFixtures = cleanupTestFixtures;
|
|
27
|
-
exports.initializeSharedResources = initializeSharedResources;
|
|
28
|
-
exports.cleanupSharedResources = cleanupSharedResources;
|
|
29
|
-
const mcp_test_client_1 = require("../client/mcp-test-client");
|
|
30
|
-
const mcp_test_client_builder_1 = require("../client/mcp-test-client.builder");
|
|
31
|
-
const token_factory_1 = require("../auth/token-factory");
|
|
32
|
-
const test_server_1 = require("../server/test-server");
|
|
33
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
34
|
-
// GLOBAL STATE
|
|
35
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
36
|
-
/** Current test configuration (set via test.use()) */
|
|
37
|
-
let currentConfig = {};
|
|
38
|
-
/** Server instance (shared across tests in a file) */
|
|
39
|
-
let serverInstance = null;
|
|
40
|
-
/** Token factory instance (shared across tests in a file) */
|
|
41
|
-
let tokenFactory = null;
|
|
42
|
-
/** Track if server was started by us (vs external) */
|
|
43
|
-
let serverStartedByUs = false;
|
|
44
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
45
|
-
// FIXTURE SETUP/TEARDOWN
|
|
46
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
47
|
-
/**
|
|
48
|
-
* Initialize shared resources (server, token factory) once per test file
|
|
49
|
-
*/
|
|
50
|
-
async function initializeSharedResources() {
|
|
51
|
-
// Create token factory if not exists
|
|
52
|
-
if (!tokenFactory) {
|
|
53
|
-
tokenFactory = new token_factory_1.TestTokenFactory();
|
|
54
|
-
}
|
|
55
|
-
// Start or connect to server if not exists
|
|
56
|
-
if (!serverInstance) {
|
|
57
|
-
if (currentConfig.baseUrl) {
|
|
58
|
-
// Connect to existing external server
|
|
59
|
-
serverInstance = test_server_1.TestServer.connect(currentConfig.baseUrl);
|
|
60
|
-
serverStartedByUs = false;
|
|
61
|
-
}
|
|
62
|
-
else if (currentConfig.server) {
|
|
63
|
-
// Start new server
|
|
64
|
-
serverInstance = await test_server_1.TestServer.start({
|
|
65
|
-
port: currentConfig.port,
|
|
66
|
-
command: resolveServerCommand(currentConfig.server),
|
|
67
|
-
env: currentConfig.env,
|
|
68
|
-
startupTimeout: currentConfig.startupTimeout ?? 30000,
|
|
69
|
-
debug: currentConfig.logLevel === 'debug',
|
|
70
|
-
});
|
|
71
|
-
serverStartedByUs = true;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
throw new Error('test.use() requires either "server" (entry file path) or "baseUrl" (for external server) option');
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Create fixtures for a single test
|
|
80
|
-
*/
|
|
81
|
-
async function createTestFixtures() {
|
|
82
|
-
// Ensure shared resources are initialized
|
|
83
|
-
await initializeSharedResources();
|
|
84
|
-
// Create MCP client for this test
|
|
85
|
-
// Pass publicMode if configured to skip authentication
|
|
86
|
-
const clientInstance = await mcp_test_client_1.McpTestClient.create({
|
|
87
|
-
baseUrl: serverInstance.info.baseUrl,
|
|
88
|
-
transport: currentConfig.transport ?? 'streamable-http',
|
|
89
|
-
publicMode: currentConfig.publicMode,
|
|
90
|
-
}).buildAndConnect();
|
|
91
|
-
// Build fixtures
|
|
92
|
-
const auth = createAuthFixture(tokenFactory);
|
|
93
|
-
const server = createServerFixture(serverInstance);
|
|
94
|
-
return {
|
|
95
|
-
mcp: clientInstance,
|
|
96
|
-
auth,
|
|
97
|
-
server,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
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)
|
|
104
|
-
*/
|
|
105
|
-
async function cleanupTestFixtures(fixtures, _testFailed = false) {
|
|
106
|
-
// Disconnect client
|
|
107
|
-
if (fixtures.mcp.isConnected()) {
|
|
108
|
-
await fixtures.mcp.disconnect();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Clean up shared resources after all tests in a file
|
|
113
|
-
*/
|
|
114
|
-
async function cleanupSharedResources() {
|
|
115
|
-
// Only stop server if we started it
|
|
116
|
-
if (serverInstance && serverStartedByUs) {
|
|
117
|
-
await serverInstance.stop();
|
|
118
|
-
}
|
|
119
|
-
serverInstance = null;
|
|
120
|
-
tokenFactory = null;
|
|
121
|
-
serverStartedByUs = false;
|
|
122
|
-
}
|
|
123
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
124
|
-
// FIXTURE FACTORIES
|
|
125
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
126
|
-
/**
|
|
127
|
-
* Create the auth fixture from token factory
|
|
128
|
-
*/
|
|
129
|
-
function createAuthFixture(factory) {
|
|
130
|
-
const users = {
|
|
131
|
-
admin: {
|
|
132
|
-
sub: 'admin-001',
|
|
133
|
-
scopes: ['admin:*', 'read', 'write', 'delete'],
|
|
134
|
-
email: 'admin@test.local',
|
|
135
|
-
name: 'Test Admin',
|
|
136
|
-
},
|
|
137
|
-
user: {
|
|
138
|
-
sub: 'user-001',
|
|
139
|
-
scopes: ['read', 'write'],
|
|
140
|
-
email: 'user@test.local',
|
|
141
|
-
name: 'Test User',
|
|
142
|
-
},
|
|
143
|
-
readOnly: {
|
|
144
|
-
sub: 'readonly-001',
|
|
145
|
-
scopes: ['read'],
|
|
146
|
-
email: 'readonly@test.local',
|
|
147
|
-
name: 'Read Only User',
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
return {
|
|
151
|
-
createToken: (opts) => factory.createTestToken({
|
|
152
|
-
sub: opts.sub,
|
|
153
|
-
scopes: opts.scopes,
|
|
154
|
-
claims: {
|
|
155
|
-
email: opts.email,
|
|
156
|
-
name: opts.name,
|
|
157
|
-
...opts.claims,
|
|
158
|
-
},
|
|
159
|
-
exp: opts.expiresIn,
|
|
160
|
-
}),
|
|
161
|
-
createExpiredToken: (opts) => factory.createExpiredToken(opts),
|
|
162
|
-
createInvalidToken: (opts) => factory.createTokenWithInvalidSignature(opts),
|
|
163
|
-
users: {
|
|
164
|
-
admin: users['admin'],
|
|
165
|
-
user: users['user'],
|
|
166
|
-
readOnly: users['readOnly'],
|
|
167
|
-
},
|
|
168
|
-
getJwks: () => factory.getPublicJwks(),
|
|
169
|
-
getIssuer: () => factory.getIssuer(),
|
|
170
|
-
getAudience: () => factory.getAudience(),
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Create the server fixture from test server
|
|
175
|
-
*/
|
|
176
|
-
function createServerFixture(server) {
|
|
177
|
-
return {
|
|
178
|
-
info: server.info,
|
|
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
|
|
182
|
-
return mcp_test_client_1.McpTestClient.create({
|
|
183
|
-
baseUrl: server.info.baseUrl,
|
|
184
|
-
transport: opts?.transport ?? 'streamable-http',
|
|
185
|
-
auth: opts?.token ? { token: opts.token } : undefined,
|
|
186
|
-
clientInfo: opts?.clientInfo,
|
|
187
|
-
publicMode: currentConfig.publicMode,
|
|
188
|
-
}).buildAndConnect();
|
|
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
|
-
},
|
|
199
|
-
restart: () => server.restart(),
|
|
200
|
-
getLogs: () => server.getLogs(),
|
|
201
|
-
clearLogs: () => server.clearLogs(),
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Resolve server entry to a command
|
|
206
|
-
*/
|
|
207
|
-
function resolveServerCommand(server) {
|
|
208
|
-
// If it's already a command (contains spaces), use as-is
|
|
209
|
-
if (server.includes(' ')) {
|
|
210
|
-
return server;
|
|
211
|
-
}
|
|
212
|
-
// Otherwise, run with tsx
|
|
213
|
-
return `npx tsx ${server}`;
|
|
214
|
-
}
|
|
215
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
216
|
-
// TEST FUNCTION WITH FIXTURES
|
|
217
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
218
|
-
/**
|
|
219
|
-
* Enhanced test function that provides fixtures
|
|
220
|
-
*/
|
|
221
|
-
function testWithFixtures(name, fn) {
|
|
222
|
-
it(name, async () => {
|
|
223
|
-
const fixtures = await createTestFixtures();
|
|
224
|
-
let testFailed = false;
|
|
225
|
-
try {
|
|
226
|
-
await fn(fixtures);
|
|
227
|
-
}
|
|
228
|
-
catch (error) {
|
|
229
|
-
testFailed = true;
|
|
230
|
-
throw error;
|
|
231
|
-
}
|
|
232
|
-
finally {
|
|
233
|
-
await cleanupTestFixtures(fixtures, testFailed);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Configure test fixtures for the current test file/suite
|
|
239
|
-
*/
|
|
240
|
-
function use(config) {
|
|
241
|
-
// Merge with existing config
|
|
242
|
-
currentConfig = { ...currentConfig, ...config };
|
|
243
|
-
// Register cleanup hook if not already done
|
|
244
|
-
// This ensures server is stopped after all tests in the file
|
|
245
|
-
afterAll(async () => {
|
|
246
|
-
await cleanupSharedResources();
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Skip a test
|
|
251
|
-
*/
|
|
252
|
-
function skip(name, fn) {
|
|
253
|
-
it.skip(name, async () => {
|
|
254
|
-
const fixtures = await createTestFixtures();
|
|
255
|
-
let testFailed = false;
|
|
256
|
-
try {
|
|
257
|
-
await fn(fixtures);
|
|
258
|
-
}
|
|
259
|
-
catch (error) {
|
|
260
|
-
testFailed = true;
|
|
261
|
-
throw error;
|
|
262
|
-
}
|
|
263
|
-
finally {
|
|
264
|
-
await cleanupTestFixtures(fixtures, testFailed);
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Run only this test
|
|
270
|
-
*/
|
|
271
|
-
function only(name, fn) {
|
|
272
|
-
it.only(name, async () => {
|
|
273
|
-
const fixtures = await createTestFixtures();
|
|
274
|
-
let testFailed = false;
|
|
275
|
-
try {
|
|
276
|
-
await fn(fixtures);
|
|
277
|
-
}
|
|
278
|
-
catch (error) {
|
|
279
|
-
testFailed = true;
|
|
280
|
-
throw error;
|
|
281
|
-
}
|
|
282
|
-
finally {
|
|
283
|
-
await cleanupTestFixtures(fixtures, testFailed);
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Mark test as todo
|
|
289
|
-
*/
|
|
290
|
-
function todo(name) {
|
|
291
|
-
it.todo(name);
|
|
292
|
-
}
|
|
293
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
294
|
-
// ATTACH STATIC METHODS
|
|
295
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
296
|
-
// Cast to the full interface type
|
|
297
|
-
const test = testWithFixtures;
|
|
298
|
-
exports.test = test;
|
|
299
|
-
// Attach configuration method
|
|
300
|
-
test.use = use;
|
|
301
|
-
// Attach Jest lifecycle methods
|
|
302
|
-
test.describe = describe;
|
|
303
|
-
test.beforeAll = beforeAll;
|
|
304
|
-
test.beforeEach = beforeEach;
|
|
305
|
-
test.afterEach = afterEach;
|
|
306
|
-
test.afterAll = afterAll;
|
|
307
|
-
// Attach test modifiers
|
|
308
|
-
test.skip = skip;
|
|
309
|
-
test.only = only;
|
|
310
|
-
test.todo = todo;
|
|
311
|
-
//# sourceMappingURL=test-fixture.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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"]}
|