-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Expand file tree
/
Copy patheval_result.ts
More file actions
111 lines (96 loc) · 2.91 KB
/
Copy patheval_result.ts
File metadata and controls
111 lines (96 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
export interface CapturedFunctionCall {
name: string;
args: Record<string, unknown>;
}
export class Result {
private nextCallIndex = 0;
public readonly calls: CapturedFunctionCall[];
public readonly serverArgs: string[];
constructor(calls: CapturedFunctionCall[], serverArgs: string[]) {
this.calls = calls;
this.serverArgs = serverArgs;
}
get hasPageIdRouting(): boolean {
return this.serverArgs.includes('--experimental-page-id-routing');
}
get remainingCalls(): CapturedFunctionCall[] {
return this.calls.slice(this.nextCallIndex);
}
/**
* Consumes initial page navigation/setup boilerplate.
* - Ignores/skips leading or trailing list_pages calls.
* - Asserts that new_page or navigate_page was called.
* - Determines the expected pageId.
* - Returns the active pageId.
*/
consumePageNavigation(): number | undefined {
if (this.calls[this.nextCallIndex]?.name === 'list_pages') {
this.nextCallIndex++;
}
const navCall = this.calls[this.nextCallIndex];
assert.ok(
navCall &&
(navCall.name === 'new_page' || navCall.name === 'navigate_page'),
`Expected navigation call (new_page or navigate_page), but got: ${navCall?.name || 'none'}`,
);
this.nextCallIndex++;
if (this.calls[this.nextCallIndex]?.name === 'list_pages') {
this.nextCallIndex++;
}
const isNewPage = navCall.name === 'new_page';
let pageId: number | undefined;
if (this.hasPageIdRouting) {
pageId = isNewPage ? 2 : 1;
}
return pageId;
}
/**
* Asserts that the next call in sequence has the correct name and matches expected arguments.
* Increments the internal call index.
*/
assertNextCall(
name: string,
expectedArgs?: Record<string, unknown>,
): CapturedFunctionCall {
const call = this.calls[this.nextCallIndex];
assert.ok(
call,
`Expected call at index ${this.nextCallIndex} (name: '${name}') to exist`,
);
assert.strictEqual(
call.name,
name,
`Expected call at index ${this.nextCallIndex} to be '${name}', but got '${call.name}'`,
);
if (expectedArgs) {
for (const entry of Object.entries(expectedArgs)) {
const key = entry[0];
const value = entry[1];
assert.deepStrictEqual(
call.args[key],
value,
`Expected argument '${key}' on call '${name}' to be ${JSON.stringify(value)}, got ${JSON.stringify(call.args[key])}`,
);
}
}
this.nextCallIndex++;
return call;
}
}
export interface TestScenario {
prompt: string;
maxTurns: number;
expectations: (result: Result) => void;
htmlRoute?: {
path: string;
htmlContent: string;
};
/** Extra CLI flags passed to the MCP server (e.g. '--experimental-page-id-routing'). */
serverArgs?: string[];
}