diff --git a/apps/site/docs/en/integrate-with-playwright.mdx b/apps/site/docs/en/integrate-with-playwright.mdx index 2ac0c083da..dfe30f4300 100644 --- a/apps/site/docs/en/integrate-with-playwright.mdx +++ b/apps/site/docs/en/integrate-with-playwright.mdx @@ -170,10 +170,14 @@ import { PlaywrightAiFixture } from '@midscene/web/playwright'; export const test = base.extend( PlaywrightAiFixture({ waitForNetworkIdleTimeout: 2000, // optional, the timeout for waiting for network idle between each action, default is 2000ms + replanningCycleLimit: 30, // optional, override the default aiAct replanning cycle limit }), ); ``` +`PlaywrightAiFixture()` also accepts Agent initialization options (for example `replanningCycleLimit`, `waitAfterAction`, and `modelConfig`) in addition to fixture-specific options. See [Agent API](./api) for the full option list. + + ### Step 3: Write test cases Review the full catalog of action, query, and utility methods in the [Agent API reference](./api#interaction-methods). When you need lower-level control, you can use `agentForPage` to obtain the underlying `PageAgent` instance and call any API directly: diff --git a/apps/site/docs/zh/integrate-with-playwright.mdx b/apps/site/docs/zh/integrate-with-playwright.mdx index ab9fc4bffb..6c907e41b3 100644 --- a/apps/site/docs/zh/integrate-with-playwright.mdx +++ b/apps/site/docs/zh/integrate-with-playwright.mdx @@ -170,10 +170,14 @@ import { PlaywrightAiFixture } from '@midscene/web/playwright'; export const test = base.extend( PlaywrightAiFixture({ waitForNetworkIdleTimeout: 2000, // 可选, 交互过程中等待网络空闲的超时时间, 默认值为 2000ms, 设置为 0 则禁用超时 + replanningCycleLimit: 30, // 可选,覆盖 aiAct 默认的重规划次数上限 }), ); ``` +`PlaywrightAiFixture()` 除了 fixture 自身配置外,也支持传入 Agent 初始化参数(例如 `replanningCycleLimit`、`waitAfterAction`、`modelConfig`)。完整参数列表请参考 [Agent API 文档](./api)。 + + ### 第三步:编写测试用例 完整的交互、查询和辅助 API 请参考 [Agent API 参考](./api#interaction-methods)。如果需要调用更底层的能力,可以使用 `agentForPage` 获取 `PageAgent` 实例,再直接调用对应的方法: diff --git a/packages/web-integration/src/playwright/ai-fixture.ts b/packages/web-integration/src/playwright/ai-fixture.ts index 2ec14ed4fa..ea362e35d9 100644 --- a/packages/web-integration/src/playwright/ai-fixture.ts +++ b/packages/web-integration/src/playwright/ai-fixture.ts @@ -56,17 +56,17 @@ type PlaywrightCacheConfig = { }; type PlaywrightCache = false | true | PlaywrightCacheConfig; -export const PlaywrightAiFixture = (options?: { - forceSameTabNavigation?: boolean; - waitForNetworkIdleTimeout?: number; - waitForNavigationTimeout?: number; +type PlaywrightAiFixtureOptions = Omit & { cache?: PlaywrightCache; -}) => { +}; + +export const PlaywrightAiFixture = (options?: PlaywrightAiFixtureOptions) => { const { forceSameTabNavigation = true, waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT, waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, cache, + ...fixtureAgentOptions } = options ?? {}; // Helper function to process cache configuration and auto-generate ID from test info @@ -192,6 +192,7 @@ export const PlaywrightAiFixture = (options?: { }) { const { page, testInfo, use, aiActionType } = options; const agent = createOrReuseAgentForPage(page, testInfo, { + ...fixtureAgentOptions, waitForNavigationTimeout, waitForNetworkIdleTimeout, }) as PlaywrightAgent; @@ -289,6 +290,7 @@ export const PlaywrightAiFixture = (options?: { } const agent = createOrReuseAgentForPage(propsPage || page, testInfo, { + ...fixtureAgentOptions, waitForNavigationTimeout, waitForNetworkIdleTimeout, cache: finalCacheConfig, diff --git a/packages/web-integration/tests/unit-test/playwright-ai-fixture-options.test.ts b/packages/web-integration/tests/unit-test/playwright-ai-fixture-options.test.ts new file mode 100644 index 0000000000..e193c43b4c --- /dev/null +++ b/packages/web-integration/tests/unit-test/playwright-ai-fixture-options.test.ts @@ -0,0 +1,89 @@ +import { describe, expect, it, vi } from 'vitest'; + +const agentInstances: Array<{ opts: any }> = []; + +vi.mock('@/playwright/index', () => { + class MockPlaywrightAgent { + opts: any; + + reportFile?: string; + + constructor(_page: any, opts: any) { + this.opts = opts; + agentInstances.push(this); + } + + waitForNetworkIdle = vi.fn(); + + destroy = vi.fn(async () => undefined); + } + + return { + PlaywrightAgent: MockPlaywrightAgent, + }; +}); + +import { PlaywrightAiFixture } from '@/playwright/ai-fixture'; + +const createMockPage = () => ({ + on: vi.fn(), +}); + +const createTestInfo = () => + ({ + testId: 'playwright-fixture-test-id', + titlePath: ['fixture.spec.ts', 'forwards options'], + retry: 0, + annotations: [], + }) as any; + +describe('PlaywrightAiFixture option forwarding', () => { + it('forwards agent options configured at fixture level', async () => { + agentInstances.length = 0; + const fixture = PlaywrightAiFixture({ + replanningCycleLimit: 9, + waitAfterAction: 120, + aiActContext: 'fixture-level-context', + useDeviceTimestamp: true, + }); + + const page = createMockPage(); + await fixture.agentForPage( + { page } as any, + async (getAgent: any) => { + await getAgent(); + }, + createTestInfo(), + ); + + expect(agentInstances).toHaveLength(1); + expect(agentInstances[0].opts.replanningCycleLimit).toBe(9); + expect(agentInstances[0].opts.waitAfterAction).toBe(120); + expect(agentInstances[0].opts.aiActContext).toBe('fixture-level-context'); + expect(agentInstances[0].opts.useDeviceTimestamp).toBe(true); + }); + + it('allows per-call options to override fixture-level options', async () => { + agentInstances.length = 0; + const fixture = PlaywrightAiFixture({ + replanningCycleLimit: 9, + waitAfterAction: 120, + }); + + const page = createMockPage(); + await fixture.agentForPage( + { page } as any, + async (getAgent: any) => { + await getAgent(undefined, { + replanningCycleLimit: 3, + waitAfterAction: 60, + }); + }, + createTestInfo(), + ); + + expect(agentInstances).toHaveLength(1); + expect(agentInstances[0].opts.replanningCycleLimit).toBe(3); + expect(agentInstances[0].opts.waitAfterAction).toBe(60); + }); +});