diff --git a/packages/playwright-core/src/client/events.ts b/packages/playwright-core/src/client/events.ts index e3e4df611ccfe..f56c52f3a01fd 100644 --- a/packages/playwright-core/src/client/events.ts +++ b/packages/playwright-core/src/client/events.ts @@ -55,7 +55,6 @@ export const Events = { }, Page: { - AgentTurn: 'agentturn', Close: 'close', Crash: 'crash', Console: 'console', @@ -79,7 +78,6 @@ export const Events = { Worker: 'worker', }, - PageAgent: { Turn: 'turn', }, diff --git a/packages/playwright-core/src/client/pageAgent.ts b/packages/playwright-core/src/client/pageAgent.ts index ec6a16cc9aeab..bcf0cdb839d98 100644 --- a/packages/playwright-core/src/client/pageAgent.ts +++ b/packages/playwright-core/src/client/pageAgent.ts @@ -33,7 +33,7 @@ export class PageAgent extends ChannelOwner implement constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.PageAgentInitializer) { super(parent, type, guid, initializer); this._page = Page.from(initializer.page); - this._channel.on('turn', params => this.emit(Events.Page.AgentTurn, params)); + this._channel.on('turn', params => this.emit(Events.PageAgent.Turn, params)); } async expect(expectation: string, options: channels.PageAgentExpectOptions = {}) { diff --git a/packages/playwright-core/src/server/dispatchers/pageAgentDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageAgentDispatcher.ts index c30f551085b4d..65b57bab06576 100644 --- a/packages/playwright-core/src/server/dispatchers/pageAgentDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageAgentDispatcher.ts @@ -67,6 +67,9 @@ export class PageAgentDispatcher extends Dispatcher { + progress.metadata.potentiallyClosesScope = true; + void this.stopPendingOperations(new Error('The agent is disposed')); + this._dispose(); } private _eventSupport(): loopTypes.LoopEvents { diff --git a/packages/playwright-core/src/server/progress.ts b/packages/playwright-core/src/server/progress.ts index 07b580bff6376..58001fefb7f14 100644 --- a/packages/playwright-core/src/server/progress.ts +++ b/packages/playwright-core/src/server/progress.ts @@ -52,7 +52,7 @@ export class ProgressController { (error as any)[kAbortErrorSymbol] = true; this._state = { error }; this._forceAbortPromise.reject(error); - this._controller.abort(); + this._controller.abort(error); } await this._donePromise; } diff --git a/tests/library/agent-perform.spec.ts b/tests/library/agent-perform.spec.ts index d4f196d3a97bb..915915cd80a05 100644 --- a/tests/library/agent-perform.spec.ts +++ b/tests/library/agent-perform.spec.ts @@ -250,3 +250,48 @@ test('perform reports error', async ({ context }) => { const e = await agent.perform('click the Rabbit button').catch(e => e); expect(e.message).toContain('Agent refused to perform action:'); }); + +test('should dispatch event and respect dispose()', async ({ context, server }) => { + let apiResponse; + server.setRoute('/api', (req, res) => { + apiResponse = res; + // stall + }); + + const apiRequestPromise = server.waitForRequest('/api'); + const { page, agent } = await generateAgent(context, { + provider: { + api: 'anthropic', + apiKey: 'not a real key', + apiEndpoint: server.PREFIX + '/api', + model: 'no such model', + }, + }); + await page.setContent(``); + + const promiseCanceledByDispose = agent.perform('click the Wolf button').catch(e => e); + let promiseAfterDispose; + let eventCounter = 0; + + agent.on('turn', async () => { + ++eventCounter; + if (eventCounter > 1) + return; + + await apiRequestPromise; + void agent.dispose(); + promiseAfterDispose = agent.perform('click the Wolf button again').catch(e => e); + apiResponse.end(); + }); + + const errorCanceledByDispose = await promiseCanceledByDispose; + expect(errorCanceledByDispose.message).toContain('The agent is disposed'); + expect(errorCanceledByDispose.message).not.toContain('after being disposed'); + + const errorAfterDispose = await promiseAfterDispose; + expect(errorAfterDispose.message).toContain('Target page, context or browser has been closed'); + + // no more events after dispose + await page.waitForTimeout(1000); + expect(eventCounter).toBe(1); +});