From 2ccd232a86ed5b6e13fd30667416ad367e993fa5 Mon Sep 17 00:00:00 2001 From: EAGzzyCSL Date: Thu, 2 Apr 2026 20:06:49 +0800 Subject: [PATCH] fix(web-integration): align yaml waitForNetworkIdle behavior and docs --- .../docs/en/automate-with-scripts-in-yaml.mdx | 9 +++++--- .../docs/zh/automate-with-scripts-in-yaml.mdx | 9 +++++--- .../src/puppeteer/agent-launcher.ts | 4 ++++ .../unit-test/base-page-invoke-action.test.ts | 22 +++++++++++++++++++ .../puppeteer/agent-launcher.test.ts | 16 ++++++++++++++ 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/apps/site/docs/en/automate-with-scripts-in-yaml.mdx b/apps/site/docs/en/automate-with-scripts-in-yaml.mdx index b9c76e092a..00af43c946 100644 --- a/apps/site/docs/en/automate-with-scripts-in-yaml.mdx +++ b/apps/site/docs/en/automate-with-scripts-in-yaml.mdx @@ -166,11 +166,14 @@ web: # Path to a JSON format browser cookie file, optional. cookie: - # The strategy for waiting for network idle, optional. + # The strategy for waiting for network idle in Puppeteer mode, optional. + # `timeout` applies to the initial opening of `web.url` defined in YAML and to later actions such as `aiTap` and `aiInput`. + # `continueOnNetworkIdleError` only applies to the initial opening of `web.url` defined in YAML. waitForNetworkIdle: - # The timeout in milliseconds, optional, defaults to 2000ms. + # The timeout in milliseconds for each network idle wait, optional, defaults to 2000ms. timeout: - # Whether to continue on timeout, optional, defaults to true. + # Whether to continue if the initial opening of `web.url` defined in YAML times out while waiting for network idle, optional, defaults to true. + # Later action-time waits always continue even if they time out. continueOnNetworkIdleError: # The path to the JSON file for outputting aiQuery/aiAssert results, optional. diff --git a/apps/site/docs/zh/automate-with-scripts-in-yaml.mdx b/apps/site/docs/zh/automate-with-scripts-in-yaml.mdx index f428f05574..4b84712500 100644 --- a/apps/site/docs/zh/automate-with-scripts-in-yaml.mdx +++ b/apps/site/docs/zh/automate-with-scripts-in-yaml.mdx @@ -165,11 +165,14 @@ web: # JSON 格式的浏览器 Cookie 文件路径,可选 cookie: - # 等待网络空闲的策略,可选 + # Puppeteer 模式下等待网络空闲的策略,可选 + # `timeout` 会作用于初始打开 YAML 中的 `web.url`,以及后续 `aiTap`、`aiInput` 等操作后的等待 + # `continueOnNetworkIdleError` 只作用于初始打开 YAML 中的 `web.url` waitForNetworkIdle: - # 等待超时时间,可选,默认 2000ms + # 每次网络空闲等待的超时时间,可选,默认 2000ms timeout: - # 是否在等待超时后继续,可选,默认 true + # 初始打开 YAML 中的 `web.url` 时,如果网络空闲等待超时,是否继续执行,可选,默认 true + # 后续脚本执行中的等待即使超时也总是继续执行 continueOnNetworkIdleError: # 输出 aiQuery/aiAssert 结果的 JSON 文件路径,可选 diff --git a/packages/web-integration/src/puppeteer/agent-launcher.ts b/packages/web-integration/src/puppeteer/agent-launcher.ts index 8bc5db31f9..13845366dd 100644 --- a/packages/web-integration/src/puppeteer/agent-launcher.ts +++ b/packages/web-integration/src/puppeteer/agent-launcher.ts @@ -371,6 +371,10 @@ export async function puppeteerAgentForTarget( const agent = new PuppeteerAgent(page, { ...preferenceToUse, aiActContext, + waitForNetworkIdleTimeout: + typeof target.waitForNetworkIdle?.timeout === 'number' + ? target.waitForNetworkIdle.timeout + : undefined, forceSameTabNavigation: typeof target.forceSameTabNavigation !== 'undefined' ? target.forceSameTabNavigation diff --git a/packages/web-integration/tests/unit-test/base-page-invoke-action.test.ts b/packages/web-integration/tests/unit-test/base-page-invoke-action.test.ts index c4e9aaecf1..d9917ba52e 100644 --- a/packages/web-integration/tests/unit-test/base-page-invoke-action.test.ts +++ b/packages/web-integration/tests/unit-test/base-page-invoke-action.test.ts @@ -276,6 +276,28 @@ describe('Page - beforeInvokeAction and afterInvokeAction', () => { expect(mockPage.waitForNetworkIdle).not.toHaveBeenCalled(); }); + it('should use the configured network idle timeout', async () => { + const mockPage = { + url: () => 'http://example.com', + mouse: { move: vi.fn() }, + keyboard: { down: vi.fn(), up: vi.fn(), press: vi.fn(), type: vi.fn() }, + waitForSelector: vi.fn().mockResolvedValue(true), + waitForNetworkIdle: vi.fn().mockResolvedValue(true), + evaluate: vi.fn(), + } as any; + + const page = new Page(mockPage, 'puppeteer', { + waitForNetworkIdleTimeout: 4321, + }); + await page.afterInvokeAction('testAction', {}); + + expect(mockPage.waitForNetworkIdle).toHaveBeenCalledWith({ + idleTime: 200, + concurrency: 2, + timeout: 4321, + }); + }); + it('should handle navigation timeout gracefully', async () => { const consoleWarnSpy = vi .spyOn(console, 'warn') diff --git a/packages/web-integration/tests/unit-test/puppeteer/agent-launcher.test.ts b/packages/web-integration/tests/unit-test/puppeteer/agent-launcher.test.ts index 85de48fc51..10e93b1029 100644 --- a/packages/web-integration/tests/unit-test/puppeteer/agent-launcher.test.ts +++ b/packages/web-integration/tests/unit-test/puppeteer/agent-launcher.test.ts @@ -2,6 +2,7 @@ import { defaultViewportHeight, defaultViewportWidth, launchPuppeteerPage, + puppeteerAgentForTarget, } from '@/puppeteer/agent-launcher'; import { beforeEach, describe, expect, it, vi } from 'vitest'; @@ -21,6 +22,8 @@ const createPageMock = () => ({ setViewport: vi.fn().mockResolvedValue(undefined), goto: vi.fn().mockResolvedValue(undefined), waitForNetworkIdle: vi.fn().mockResolvedValue(undefined), + on: vi.fn(), + isClosed: vi.fn().mockReturnValue(false), }); vi.mock('puppeteer', () => ({ @@ -67,4 +70,17 @@ describe('launchPuppeteerPage', () => { expect.objectContaining({ defaultViewport: null }), ); }); + + it('passes yaml waitForNetworkIdle settings to the agent for later actions', async () => { + const { agent } = await puppeteerAgentForTarget({ + url: 'https://example.com', + forceSameTabNavigation: false, + waitForNetworkIdle: { + timeout: 4321, + continueOnNetworkIdleError: false, + }, + }); + + expect((agent.page as any).waitForNetworkIdleTimeout).toBe(4321); + }); });