豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit 496ab1b

Browse files
authored
feat: support any-match text arrays in wait_for (#1011)
## Summary Enhances wait_for to support waiting on multiple possible texts and resolve when any one appears. This addresses long-running flows that can end in different UI outcomes (for example, "Complete" or "Error"), avoiding unnecessary 300s waits when only one expected string is provided. Closes #916. ## Tool Update ### wait_for Waits for text on the selected page, now with any-match support. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | text | string \| string[] | yes | A single text or a non-empty list of texts. Resolves when any value appears. | | timeout | integer | no | Maximum wait in ms (0 keeps default behavior). | --- ## Design / Implementation - Kept backward compatibility: existing single-string text calls continue to work unchanged. - Added schema support for string | string[] with non-empty array validation. - Updated context API to accept string | string[]. - Matching logic now normalizes to an array and races all candidates across all frames using both: - aria/<text> - text/<text> - Added clearer response output for array input: - Element matching one of ["Complete","Error"] found. - Updated generated tool docs for the new wait_for contract. - Improved docs generation to render ZodUnion types (so union params are documented correctly, not as unknown). --- ## Tests Added/updated coverage for: - Schema acceptance of: - single string - non-empty string array - rejection of empty array - Any-match array success case - Any-match array when matching text appears later (async/delayed content) - Existing wait_for behavior remains covered for single-text usage Executed relevant test suites: - tests/tools/snapshot.test.ts - tests/McpContext.test.ts - tests/index.test.ts Confirmation after the change applied: https://opncd.ai/share/8m6I4r4a
1 parent 7ffdc5e commit 496ab1b

File tree

5 files changed

+81
-18
lines changed

5 files changed

+81
-18
lines changed

docs/tool-reference.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!-- AUTO GENERATED DO NOT EDIT - run 'npm run docs' to update-->
22

3-
# Chrome DevTools MCP Tool Reference (~6885 cl100k_base tokens)
3+
# Chrome DevTools MCP Tool Reference (~6916 cl100k_base tokens)
44

55
- **[Input automation](#input-automation)** (8 tools)
66
- [`click`](#click)
@@ -195,7 +195,7 @@
195195

196196
**Parameters:**
197197

198-
- **text** (string) **(required)**: Text to appear on the page
198+
- **text** (array) **(required)**: Non-empty list of texts. Resolves when any value appears on the page.
199199
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
200200

201201
---

src/McpContext.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -881,15 +881,17 @@ export class McpContext implements Context {
881881
return this.#networkCollector.getIdForResource(request);
882882
}
883883

884-
waitForTextOnPage(text: string, timeout?: number): Promise<Element> {
884+
waitForTextOnPage(text: string[], timeout?: number): Promise<Element> {
885885
const page = this.getSelectedPage();
886886
const frames = page.frames();
887887

888888
let locator = this.#locatorClass.race(
889-
frames.flatMap(frame => [
890-
frame.locator(`aria/${text}`),
891-
frame.locator(`text/${text}`),
892-
]),
889+
frames.flatMap(frame =>
890+
text.flatMap(value => [
891+
frame.locator(`aria/${value}`),
892+
frame.locator(`text/${value}`),
893+
]),
894+
),
893895
);
894896

895897
if (timeout) {

src/tools/ToolDefinition.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export type Context = Readonly<{
144144
action: () => Promise<unknown>,
145145
options?: {timeout?: number},
146146
): Promise<void>;
147-
waitForTextOnPage(text: string, timeout?: number): Promise<Element>;
147+
waitForTextOnPage(text: string[], timeout?: number): Promise<Element>;
148148
getDevToolsData(): Promise<DevToolsData>;
149149
/**
150150
* Returns a reqid for a cdpRequestId.

src/tools/snapshot.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ export const waitFor = defineTool({
4949
readOnlyHint: true,
5050
},
5151
schema: {
52-
text: zod.string().describe('Text to appear on the page'),
52+
text: zod
53+
.array(zod.string())
54+
.min(1)
55+
.describe(
56+
'Non-empty list of texts. Resolves when any value appears on the page.',
57+
),
5358
...timeoutSchema,
5459
},
5560
handler: async (request, response, context) => {
@@ -59,7 +64,7 @@ export const waitFor = defineTool({
5964
);
6065

6166
response.appendResponseLine(
62-
`Element with text "${request.params.text}" found.`,
67+
`Element matching one of ${JSON.stringify(request.params.text)} found.`,
6368
);
6469

6570
response.includeSnapshot();

tests/tools/snapshot.test.ts

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('snapshot', () => {
3030
await waitFor.handler(
3131
{
3232
params: {
33-
text: 'Hello',
33+
text: ['Hello'],
3434
},
3535
},
3636
response,
@@ -39,19 +39,75 @@ describe('snapshot', () => {
3939

4040
assert.equal(
4141
response.responseLines[0],
42-
'Element with text "Hello" found.',
42+
'Element matching one of ["Hello"] found.',
4343
);
4444
assert.ok(response.includeSnapshot);
4545
});
4646
});
47+
48+
it('should work with any-match array', async () => {
49+
await withMcpContext(async (response, context) => {
50+
const page = context.getSelectedPage();
51+
52+
await page.setContent(
53+
html`<main><span>Status</span><div>Error</div></main>`,
54+
);
55+
await waitFor.handler(
56+
{
57+
params: {
58+
text: ['Complete', 'Error'],
59+
},
60+
},
61+
response,
62+
context,
63+
);
64+
65+
assert.equal(
66+
response.responseLines[0],
67+
'Element matching one of ["Complete","Error"] found.',
68+
);
69+
assert.ok(response.includeSnapshot);
70+
});
71+
});
72+
73+
it('should work with any-match array when element shows up later', async () => {
74+
await withMcpContext(async (response, context) => {
75+
const page = context.getSelectedPage();
76+
77+
const handlePromise = waitFor.handler(
78+
{
79+
params: {
80+
text: ['Complete', 'Error'],
81+
},
82+
},
83+
response,
84+
context,
85+
);
86+
87+
await page.setContent(
88+
html`<main
89+
><span>Hello</span><span> </span><div>Complete</div></main
90+
>`,
91+
);
92+
93+
await handlePromise;
94+
95+
assert.equal(
96+
response.responseLines[0],
97+
'Element matching one of ["Complete","Error"] found.',
98+
);
99+
assert.ok(response.includeSnapshot);
100+
});
101+
});
102+
47103
it('should work with element that show up later', async () => {
48104
await withMcpContext(async (response, context) => {
49105
const page = context.getSelectedPage();
50106

51107
const handlePromise = waitFor.handler(
52108
{
53109
params: {
54-
text: 'Hello World',
110+
text: ['Hello World'],
55111
},
56112
},
57113
response,
@@ -66,7 +122,7 @@ describe('snapshot', () => {
66122

67123
assert.equal(
68124
response.responseLines[0],
69-
'Element with text "Hello World" found.',
125+
'Element matching one of ["Hello World"] found.',
70126
);
71127
assert.ok(response.includeSnapshot);
72128
});
@@ -82,7 +138,7 @@ describe('snapshot', () => {
82138
await waitFor.handler(
83139
{
84140
params: {
85-
text: 'Header',
141+
text: ['Header'],
86142
},
87143
},
88144
response,
@@ -91,7 +147,7 @@ describe('snapshot', () => {
91147

92148
assert.equal(
93149
response.responseLines[0],
94-
'Element with text "Header" found.',
150+
'Element matching one of ["Header"] found.',
95151
);
96152
assert.ok(response.includeSnapshot);
97153
});
@@ -109,7 +165,7 @@ describe('snapshot', () => {
109165
await waitFor.handler(
110166
{
111167
params: {
112-
text: 'Hello iframe',
168+
text: ['Hello iframe'],
113169
},
114170
},
115171
response,
@@ -118,7 +174,7 @@ describe('snapshot', () => {
118174

119175
assert.equal(
120176
response.responseLines[0],
121-
'Element with text "Hello iframe" found.',
177+
'Element matching one of ["Hello iframe"] found.',
122178
);
123179
assert.ok(response.includeSnapshot);
124180
});

0 commit comments

Comments
 (0)