diff --git a/src/McpContext.ts b/src/McpContext.ts index 83cdb2fca..645ecf69a 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -120,8 +120,6 @@ export class McpContext implements Context { null; #focusedPagePerContext = new Map(); - #requestPage?: ContextPage; - #nextPageId = 1; #extensionServiceWorkerMap = new WeakMap(); @@ -196,17 +194,6 @@ export class McpContext implements Context { return context; } - // TODO: Refactor away mutable request state (e.g. per-request facade, - // per-request context object, or another approach). Once resolved, the - // global toolMutex could become per-BrowserContext for parallel execution. - setRequestPage(page?: ContextPage): void { - this.#requestPage = page; - } - - #resolveTargetPage(): Page { - return this.#requestPage?.pptrPage ?? this.getSelectedPptrPage(); - } - resolveCdpRequestId(page: McpPage, cdpRequestId: string): number | undefined { if (!cdpRequestId) { this.logger('no network request'); @@ -250,20 +237,28 @@ export class McpContext implements Context { return; } - getNetworkRequests(includePreservedRequests?: boolean): HTTPRequest[] { - const page = this.#resolveTargetPage(); - return this.#networkCollector.getData(page, includePreservedRequests); + getNetworkRequests( + page: McpPage, + includePreservedRequests?: boolean, + ): HTTPRequest[] { + return this.#networkCollector.getData( + page.pptrPage, + includePreservedRequests, + ); } getConsoleData( + page: McpPage, includePreservedMessages?: boolean, ): Array { - const page = this.#resolveTargetPage(); - return this.#consoleCollector.getData(page, includePreservedMessages); + return this.#consoleCollector.getData( + page.pptrPage, + includePreservedMessages, + ); } - getDevToolsUniverse(): TargetUniverse | null { - return this.#devtoolsUniverseManager.get(this.#resolveTargetPage()); + getDevToolsUniverse(page: McpPage): TargetUniverse | null { + return this.#devtoolsUniverseManager.get(page.pptrPage); } getConsoleMessageStableId( @@ -273,15 +268,16 @@ export class McpContext implements Context { } getConsoleMessageById( + page: McpPage, id: number, ): ConsoleMessage | Error | DevTools.AggregatedIssue | UncaughtError { - return this.#consoleCollector.getById(this.#resolveTargetPage(), id); + return this.#consoleCollector.getById(page.pptrPage, id); } async newPage( background?: boolean, isolatedContextName?: string, - ): Promise { + ): Promise { let page: Page; if (isolatedContextName !== undefined) { let ctx = this.#isolatedContexts.get(isolatedContextName); @@ -316,15 +312,13 @@ export class McpContext implements Context { await page.close({runBeforeUnload: false}); } - getNetworkRequestById(reqid: number): HTTPRequest { - return this.#networkCollector.getById(this.#resolveTargetPage(), reqid); + getNetworkRequestById(page: McpPage, reqid: number): HTTPRequest { + return this.#networkCollector.getById(page.pptrPage, reqid); } - async restoreEmulation(targetPage?: Page) { - const page = targetPage ?? this.getSelectedPptrPage(); - const mcpPage = this.#getMcpPage(page); - const currentSetting = mcpPage.emulationSettings; - await this.emulate(currentSetting, targetPage); + async restoreEmulation(page: McpPage) { + const currentSetting = page.emulationSettings; + await this.emulate(currentSetting, page.pptrPage); } async emulate( @@ -440,30 +434,6 @@ export class McpContext implements Context { } } - getNetworkConditions(): string | null { - return this.#getMcpPage(this.#resolveTargetPage()).networkConditions; - } - - getCpuThrottlingRate(): number { - return this.#getMcpPage(this.#resolveTargetPage()).cpuThrottlingRate; - } - - getGeolocation(): GeolocationOptions | null { - return this.#getMcpPage(this.#resolveTargetPage()).geolocation; - } - - getViewport(): Viewport | null { - return this.#getMcpPage(this.#resolveTargetPage()).viewport; - } - - getUserAgent(): string | null { - return this.#getMcpPage(this.#resolveTargetPage()).userAgent; - } - - getColorScheme(): 'dark' | 'light' | null { - return this.#getMcpPage(this.#resolveTargetPage()).colorScheme; - } - setIsRunningPerformanceTrace(x: boolean): void { this.#isRunningTrace = x; } @@ -573,23 +543,20 @@ export class McpContext implements Context { } #updateSelectedPageTimeouts() { - const page = this.getSelectedPptrPage(); + const page = this.#getSelectedMcpPage(); // For waiters 5sec timeout should be sufficient. // Increased in case we throttle the CPU - const cpuMultiplier = this.getCpuThrottlingRate(); - page.setDefaultTimeout(DEFAULT_TIMEOUT * cpuMultiplier); + const cpuMultiplier = page.cpuThrottlingRate; + page.pptrPage.setDefaultTimeout(DEFAULT_TIMEOUT * cpuMultiplier); // 10sec should be enough for the load event to be emitted during // navigations. // Increased in case we throttle the network requests const networkMultiplier = getNetworkMultiplierFromString( - this.getNetworkConditions(), + page.networkConditions, + ); + page.pptrPage.setDefaultNavigationTimeout( + NAVIGATION_TIMEOUT * networkMultiplier, ); - page.setDefaultNavigationTimeout(NAVIGATION_TIMEOUT * networkMultiplier); - } - - getNavigationTimeout() { - const page = this.#resolveTargetPage(); - return page.getDefaultNavigationTimeout(); } // Linear scan over per-page snapshots. The page count is small (typically @@ -858,11 +825,10 @@ export class McpContext implements Context { return this.#mcpPages.get(page)?.devToolsPage; } - async getDevToolsData(): Promise { + async getDevToolsData(page: McpPage): Promise { try { this.logger('Getting DevTools UI data'); - const selectedPage = this.#resolveTargetPage(); - const devtoolsPage = this.getDevToolsPage(selectedPage); + const devtoolsPage = this.getDevToolsPage(page.pptrPage); if (!devtoolsPage) { this.logger('No DevTools page detected'); return {}; @@ -896,13 +862,11 @@ export class McpContext implements Context { * Creates a text snapshot of a page. */ async createTextSnapshot( + page: McpPage, verbose = false, devtoolsData: DevToolsData | undefined = undefined, - targetPage?: Page, ): Promise { - const page = targetPage ?? this.getSelectedPptrPage(); - const mcpPage = this.#getMcpPage(page); - const rootNode = await page.accessibility.snapshot({ + const rootNode = await page.pptrPage.accessibility.snapshot({ includeIframes: true, interestingOnly: !verbose, }); @@ -910,7 +874,7 @@ export class McpContext implements Context { return; } - const {uniqueBackendNodeIdToMcpId} = mcpPage; + const {uniqueBackendNodeIdToMcpId} = page; const snapshotId = this.#nextSnapshotId++; // Iterate through the whole accessibility node tree and assign node ids that @@ -961,12 +925,12 @@ export class McpContext implements Context { hasSelectedElement: false, verbose, }; - mcpPage.textSnapshot = snapshot; - const data = devtoolsData ?? (await this.getDevToolsData()); + page.textSnapshot = snapshot; + const data = devtoolsData ?? (await this.getDevToolsData(page)); if (data?.cdpBackendNodeId) { snapshot.hasSelectedElement = true; snapshot.selectedElementUid = this.resolveCdpElementId( - mcpPage, + page, data?.cdpBackendNodeId, ); } @@ -1041,13 +1005,13 @@ export class McpContext implements Context { action: () => Promise, options?: {timeout?: number}, ): Promise { - const page = this.getSelectedPptrPage(); - const cpuMultiplier = this.getCpuThrottlingRate(); + const page = this.#getSelectedMcpPage(); + const cpuMultiplier = page.cpuThrottlingRate; const networkMultiplier = getNetworkMultiplierFromString( - this.getNetworkConditions(), + page.networkConditions, ); const waitForHelper = this.getWaitForHelper( - page, + page.pptrPage, cpuMultiplier, networkMultiplier, ); diff --git a/src/McpResponse.ts b/src/McpResponse.ts index bfc7b98b2..d1ee4b8aa 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -266,10 +266,13 @@ export class McpResponse implements Response { let snapshot: SnapshotFormatter | string | undefined; if (this.#snapshotParams) { + if (!this.#page) { + throw new Error('Response must have a page'); + } await context.createTextSnapshot( + this.#page, this.#snapshotParams.verbose, this.#devToolsData, - this.#snapshotParams.page?.pptrPage, ); const textSnapshot = context.getTextSnapshot( this.#snapshotParams.page?.pptrPage, @@ -290,7 +293,11 @@ export class McpResponse implements Response { let detailedNetworkRequest: NetworkFormatter | undefined; if (this.#attachedNetworkRequestId) { + if (!this.#page) { + throw new Error(`Response must have an McpPage`); + } const request = context.getNetworkRequestById( + this.#page, this.#attachedNetworkRequestId, ); const formatter = await NetworkFormatter.from(request, { @@ -307,22 +314,24 @@ export class McpResponse implements Response { let detailedConsoleMessage: ConsoleFormatter | IssueFormatter | undefined; if (this.#attachedConsoleMessageId) { + if (!this.#page) { + throw new Error(`Response must have an McpPage`); + } + const message = context.getConsoleMessageById( + this.#page, this.#attachedConsoleMessageId, ); const consoleMessageStableId = this.#attachedConsoleMessageId; if ('args' in message || message instanceof UncaughtError) { const consoleMessage = message as ConsoleMessage | UncaughtError; - const devTools = context.getDevToolsUniverse(); + const devTools = context.getDevToolsUniverse(this.#page); detailedConsoleMessage = await ConsoleFormatter.from(consoleMessage, { id: consoleMessageStableId, fetchDetailedData: true, devTools: devTools ?? undefined, }); } else if (message instanceof DevTools.AggregatedIssue) { - if (!this.#page) { - throw new Error(`Response must have an McpPage`); - } const formatter = new IssueFormatter(message, { id: consoleMessageStableId, requestIdResolver: context.resolveCdpRequestId.bind( @@ -349,7 +358,12 @@ export class McpResponse implements Response { } let consoleMessages: Array | undefined; if (this.#consoleDataOptions?.include) { + if (!this.#page) { + throw new Error(`Response must have an McpPage`); + } + const page = this.#page; let messages = context.getConsoleData( + this.#page, this.#consoleDataOptions.includePreservedMessages, ); @@ -374,7 +388,7 @@ export class McpResponse implements Response { context.getConsoleMessageStableId(item); if ('args' in item || item instanceof UncaughtError) { const consoleMessage = item as ConsoleMessage | UncaughtError; - const devTools = context.getDevToolsUniverse(); + const devTools = context.getDevToolsUniverse(page); return await ConsoleFormatter.from(consoleMessage, { id: consoleMessageStableId, fetchDetailedData: false, @@ -399,7 +413,11 @@ export class McpResponse implements Response { let networkRequests: NetworkFormatter[] | undefined; if (this.#networkRequestsOptions?.include) { + if (!this.#page) { + throw new Error(`Response must have an McpPage`); + } let requests = context.getNetworkRequests( + this.#page, this.#networkRequestsOptions?.includePreservedRequests, ); @@ -493,39 +511,38 @@ export class McpResponse implements Response { response.push(...this.#textResponseLines); } - const networkConditions = context.getNetworkConditions(); + const networkConditions = this.#page?.networkConditions; if (networkConditions) { + const timeout = this.#page!.pptrPage.getDefaultNavigationTimeout(); response.push(`## Network emulation`); response.push(`Emulating: ${networkConditions}`); - response.push( - `Default navigation timeout set to ${context.getNavigationTimeout()} ms`, - ); + response.push(`Default navigation timeout set to ${timeout} ms`); structuredContent.networkConditions = networkConditions; - structuredContent.navigationTimeout = context.getNavigationTimeout(); + structuredContent.navigationTimeout = timeout; } - const viewport = context.getViewport(); + const viewport = this.#page?.viewport; if (viewport) { response.push(`## Viewport emulation`); response.push(`Emulating viewport: ${JSON.stringify(viewport)}`); structuredContent.viewport = viewport; } - const userAgent = context.getUserAgent(); + const userAgent = this.#page?.userAgent; if (userAgent) { response.push(`## UserAgent emulation`); response.push(`Emulating userAgent: ${userAgent}`); structuredContent.userAgent = userAgent; } - const cpuThrottlingRate = context.getCpuThrottlingRate(); + const cpuThrottlingRate = this.#page?.cpuThrottlingRate ?? 1; if (cpuThrottlingRate > 1) { response.push(`## CPU emulation`); response.push(`Emulating: ${cpuThrottlingRate}x slowdown`); structuredContent.cpuThrottlingRate = cpuThrottlingRate; } - const colorScheme = context.getColorScheme(); + const colorScheme = this.#page?.colorScheme; if (colorScheme) { response.push(`## Color Scheme emulation`); response.push(`Emulating: ${colorScheme}`); diff --git a/src/server.ts b/src/server.ts index 02c0cac20..b36e3826a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -177,54 +177,49 @@ export async function createMcpServer( const response = serverArgs.slim ? new SlimMcpResponse(serverArgs) : new McpResponse(serverArgs); - try { - if ('pageScoped' in tool && tool.pageScoped) { - const page = - serverArgs.experimentalPageIdRouting && - params.pageId && - !serverArgs.slim - ? context.resolvePageById(params.pageId) - : context.getSelectedMcpPage(); - context.setRequestPage(page); - response.setPage(page); - await tool.handler( - { - params, - page, - }, - response, - context, - ); - } else { - await tool.handler( - // @ts-expect-error types do not match. - { - params, - }, - response, - context, - ); - } - const {content, structuredContent} = await response.handle( - tool.name, + if ('pageScoped' in tool && tool.pageScoped) { + const page = + serverArgs.experimentalPageIdRouting && + params.pageId && + !serverArgs.slim + ? context.resolvePageById(params.pageId) + : context.getSelectedMcpPage(); + response.setPage(page); + await tool.handler( + { + params, + page, + }, + response, + context, + ); + } else { + await tool.handler( + // @ts-expect-error types do not match. + { + params, + }, + response, context, ); - const result: CallToolResult & { - structuredContent?: Record; - } = { - content, - }; - success = true; - if (serverArgs.experimentalStructuredContent) { - result.structuredContent = structuredContent as Record< - string, - unknown - >; - } - return result; - } finally { - context.setRequestPage(undefined); } + const {content, structuredContent} = await response.handle( + tool.name, + context, + ); + const result: CallToolResult & { + structuredContent?: Record; + } = { + content, + }; + success = true; + if (serverArgs.experimentalStructuredContent) { + result.structuredContent = structuredContent as Record< + string, + unknown + >; + } + return result; } catch (err) { logger(`${tool.name} error:`, err, err?.stack); let errorText = err && 'message' in err ? err.message : String(err); diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index fc364e47f..831512958 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -146,7 +146,7 @@ export type Context = Readonly<{ assertPageIsFocused(page: Page): void; getElementByUid(uid: string, page?: Page): Promise>; getAXNodeByUid(uid: string): TextSnapshotNode | undefined; - restoreEmulation(): Promise; + restoreEmulation(page: ContextPage): Promise; emulate( options: { networkConditions?: string | null; @@ -175,7 +175,7 @@ export type Context = Readonly<{ timeout?: number, page?: Page, ): Promise; - getDevToolsData(): Promise; + getDevToolsData(page: ContextPage): Promise; /** * Returns a reqid for a cdpRequestId. */ diff --git a/src/tools/lighthouse.ts b/src/tools/lighthouse.ts index 5ed15fa96..0c46c3ac5 100644 --- a/src/tools/lighthouse.ts +++ b/src/tools/lighthouse.ts @@ -95,7 +95,7 @@ export const lighthouseAudit = definePageTool({ throw new Error('Lighthouse audit failed.'); } } finally { - await context.restoreEmulation(); + await context.restoreEmulation(page); } const lhr = result.lhr; diff --git a/src/tools/network.ts b/src/tools/network.ts index f399f7098..2df445cc3 100644 --- a/src/tools/network.ts +++ b/src/tools/network.ts @@ -71,7 +71,7 @@ export const listNetworkRequests = definePageTool({ ), }, handler: async (request, response, context) => { - const data = await context.getDevToolsData(); + const data = await context.getDevToolsData(request.page); response.attachDevToolsData(data); const reqid = data?.cdpRequestId ? context.resolveCdpRequestId(request.page, data.cdpRequestId) @@ -120,7 +120,7 @@ export const getNetworkRequest = definePageTool({ responseFilePath: request.params.responseFilePath, }); } else { - const data = await context.getDevToolsData(); + const data = await context.getDevToolsData(request.page); response.attachDevToolsData(data); const reqid = data?.cdpRequestId ? context.resolveCdpRequestId(request.page, data.cdpRequestId) diff --git a/tests/McpContext.test.ts b/tests/McpContext.test.ts index 387a5bfc1..f5bad3011 100644 --- a/tests/McpContext.test.ts +++ b/tests/McpContext.test.ts @@ -30,9 +30,9 @@ describe('McpContext', () => { value="Input" />`, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); assert.ok(await context.getElementByUid('1_1')); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await context.getElementByUid('1_1'); }); }); @@ -104,9 +104,9 @@ describe('McpContext', () => { it('resolves uid from a non-selected page snapshot', async () => { await withMcpContext(async (_response, context) => { // Page 1: set content and snapshot - const page1 = context.getSelectedPptrPage(); - await page1.setContent(html``); - await context.createTextSnapshot(false, undefined, page1); + const page1 = context.getSelectedMcpPage(); + await page1.pptrPage.setContent(html``); + await context.createTextSnapshot(page1, false, undefined); // Capture a uid from page1's snapshot (snapshotId=1, button is node 1) const page1Uid = '1_1'; @@ -117,7 +117,7 @@ describe('McpContext', () => { const page2 = await context.newPage(); context.selectPage(page2); await page2.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, page2.pptrPage); + await context.createTextSnapshot(page2, false, undefined); // Page 2 is now selected. Page 1's uid should still resolve. const node = context.getAXNodeByUid(page1Uid); @@ -125,7 +125,7 @@ describe('McpContext', () => { assert.strictEqual(node?.name, 'Page1 Button'); // The element should also be retrievable when the target page is provided. - const element = await context.getElementByUid(page1Uid, page1); + const element = await context.getElementByUid(page1Uid, page1.pptrPage); assert.ok(element, 'should get element handle from page1 snapshot uid'); }); }); @@ -135,7 +135,7 @@ describe('McpContext', () => { await withMcpContext(async (_response, context) => { const page = await context.newPage(false, 'agent-a'); await page.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, page.pptrPage); + await context.createTextSnapshot(page, false, undefined); // page is focused for agent-a context; should resolve. const handle = await context.getElementByUid('1_1'); @@ -147,13 +147,13 @@ describe('McpContext', () => { await withMcpContext(async (_response, context) => { const pageA1 = await context.newPage(false, 'agent-a'); await pageA1.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, pageA1.pptrPage); + await context.createTextSnapshot(pageA1, false, undefined); const a1Uid = '1_1'; // button on pageA1 // Open a second page in the same context (becomes focused). const pageA2 = await context.newPage(false, 'agent-a'); await pageA2.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, pageA2.pptrPage); + await context.createTextSnapshot(pageA2, false, undefined); // pageA2 is now focused for agent-a; clicking pageA1's uid should throw. await assert.rejects( @@ -172,12 +172,12 @@ describe('McpContext', () => { // Set up two pages in separate isolated contexts. const pageA = await context.newPage(false, 'agent-a'); await pageA.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, pageA.pptrPage); + await context.createTextSnapshot(pageA, false, undefined); const uidA = '1_1'; const pageB = await context.newPage(false, 'agent-b'); await pageB.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, pageB.pptrPage); + await context.createTextSnapshot(pageB, false, undefined); const uidB = '2_1'; // Simulate race: agent-a selects its page, then agent-b overwrites global. @@ -198,12 +198,12 @@ describe('McpContext', () => { await withMcpContext(async (_response, context) => { const pageA = await context.newPage(false, 'agent-a'); await pageA.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, pageA.pptrPage); + await context.createTextSnapshot(pageA, false, undefined); const uidA = '1_1'; const pageB = await context.newPage(false, 'agent-b'); await pageB.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, pageB.pptrPage); + await context.createTextSnapshot(pageB, false, undefined); // Global is on pageB after newPage. assert.strictEqual(context.getSelectedMcpPage(), pageB); @@ -219,7 +219,7 @@ describe('McpContext', () => { await withMcpContext(async (_response, context) => { const page = await context.newPage(false, 'agent-a'); await page.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, page.pptrPage); + await context.createTextSnapshot(page, false, undefined); await assert.rejects(() => context.getElementByUid('nonexistent_99'), { message: 'No such element found in any snapshot.', @@ -230,9 +230,11 @@ describe('McpContext', () => { it('resolves for default context page alongside isolated contexts', async () => { await withMcpContext(async (_response, context) => { // Default context page (already exists from withMcpContext setup). - const defaultPage = context.getSelectedPptrPage(); - await defaultPage.setContent(html``); - await context.createTextSnapshot(false, undefined, defaultPage); + const defaultPage = context.getSelectedMcpPage(); + await defaultPage.pptrPage.setContent( + html``, + ); + await context.createTextSnapshot(defaultPage, false, undefined); const defaultUid = '1_1'; // Isolated context page. @@ -240,7 +242,7 @@ describe('McpContext', () => { await isoPage.pptrPage.setContent( html``, ); - await context.createTextSnapshot(false, undefined, isoPage.pptrPage); + await context.createTextSnapshot(isoPage, false, undefined); const isoUid = '2_1'; // Global is now isoPage. Default context focus is still defaultPage. @@ -256,12 +258,12 @@ describe('McpContext', () => { await withMcpContext(async (_response, context) => { const pageA = await context.newPage(false, 'agent-a'); await pageA.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, pageA.pptrPage); + await context.createTextSnapshot(pageA, false, undefined); const uidA = '1_1'; const pageB = await context.newPage(false, 'agent-b'); await pageB.pptrPage.setContent(html``); - await context.createTextSnapshot(false, undefined, pageB.pptrPage); + await context.createTextSnapshot(pageB, false, undefined); // uidA belongs to pageA; searching with pageB should throw. await assert.rejects( diff --git a/tests/tools/console.test.ts b/tests/tools/console.test.ts index 979853587..4014764ce 100644 --- a/tests/tools/console.test.ts +++ b/tests/tools/console.test.ts @@ -36,7 +36,7 @@ describe('console', () => { it('lists error messages', async () => { await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.setContent( '', ); @@ -53,7 +53,7 @@ describe('console', () => { it('lists error objects', async t => { await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.setContent( '', ); @@ -70,7 +70,7 @@ describe('console', () => { it('work with primitive unhandled errors', async () => { await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.setContent(''); await listConsoleMessages.handler( {params: {}, page: context.getSelectedMcpPage()}, @@ -86,7 +86,7 @@ describe('console', () => { describe('issues', () => { it('lists issues', async () => { await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); const issuePromise = new Promise(resolve => { page.pptrPage.once('issue', () => { resolve(); @@ -114,6 +114,7 @@ describe('console', () => { it('lists issues after a page reload', async () => { await withMcpContext(async (response, context) => { const page = await context.newPage(); + response.setPage(page); const issuePromise = new Promise(resolve => { page.pptrPage.once('issue', () => { resolve(); @@ -168,7 +169,7 @@ describe('console', () => { it('gets a specific console message', async () => { await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.setContent( '', ); @@ -195,7 +196,7 @@ describe('console', () => { describe('issues type', () => { it('gets issue details with node id parsing', async t => { await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); const issuePromise = new Promise(resolve => { page.pptrPage.once('issue', () => { resolve(); @@ -204,7 +205,7 @@ describe('console', () => { await page.pptrPage.setContent( '', ); - await context.createTextSnapshot(); + await context.createTextSnapshot(page); await issuePromise; await listConsoleMessages.handler( {params: {}, page: context.getSelectedMcpPage()}, @@ -230,7 +231,7 @@ describe('console', () => { }); await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); const issuePromise = new Promise(resolve => { page.pptrPage.once('issue', () => { resolve(); @@ -249,9 +250,9 @@ describe('console', () => { }); `); - await context.createTextSnapshot(); + await context.createTextSnapshot(page); await issuePromise; - const messages = context.getConsoleData(); + const messages = context.getConsoleData(page); let issueMsg; for (const message of messages) { if (message instanceof DevTools.AggregatedIssue) { @@ -299,7 +300,7 @@ describe('console', () => { ); await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.goto(server.getRoute('/index.html')); await getConsoleMessage.handler( @@ -328,7 +329,7 @@ describe('console', () => { ); await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.goto(server.getRoute('/index.html')); await getConsoleMessage.handler( @@ -357,7 +358,7 @@ describe('console', () => { ); await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.goto(server.getRoute('/index.html')); await getConsoleMessage.handler( @@ -386,7 +387,7 @@ describe('console', () => { ); await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.goto(server.getRoute('/index.html')); await getConsoleMessage.handler( @@ -415,7 +416,7 @@ describe('console', () => { ); await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.goto(server.getRoute('/index.html')); await getConsoleMessage.handler( @@ -458,7 +459,7 @@ describe('console', () => { ); await withMcpContext(async (response, context) => { - const page = await context.newPage(); + const page = context.getSelectedMcpPage(); await page.pptrPage.goto(server.getRoute('/index.html')); await getConsoleMessage.handler( diff --git a/tests/tools/emulation.test.ts b/tests/tools/emulation.test.ts index 6c59dee06..36e45aa2c 100644 --- a/tests/tools/emulation.test.ts +++ b/tests/tools/emulation.test.ts @@ -28,7 +28,10 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getNetworkConditions(), 'Offline'); + assert.strictEqual( + context.getSelectedMcpPage().networkConditions, + 'Offline', + ); }); }); it('emulates network throttling when the throttling option is valid', async () => { @@ -44,7 +47,10 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getNetworkConditions(), 'Slow 3G'); + assert.strictEqual( + context.getSelectedMcpPage().networkConditions, + 'Slow 3G', + ); }); }); @@ -61,7 +67,10 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getNetworkConditions(), null); + assert.strictEqual( + context.getSelectedMcpPage().networkConditions, + null, + ); }); }); @@ -78,7 +87,10 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getNetworkConditions(), null); + assert.strictEqual( + context.getSelectedMcpPage().networkConditions, + null, + ); }); }); @@ -95,12 +107,18 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getNetworkConditions(), 'Slow 3G'); + assert.strictEqual( + context.getSelectedMcpPage().networkConditions, + 'Slow 3G', + ); const page = await context.newPage(); context.selectPage(page); - assert.strictEqual(context.getNetworkConditions(), null); + assert.strictEqual( + context.getSelectedMcpPage().networkConditions, + null, + ); }); }); }); @@ -119,7 +137,7 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getCpuThrottlingRate(), 4); + assert.strictEqual(context.getSelectedMcpPage().cpuThrottlingRate, 4); }); }); @@ -139,7 +157,7 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getCpuThrottlingRate(), 1); + assert.strictEqual(context.getSelectedMcpPage().cpuThrottlingRate, 1); }); }); @@ -156,12 +174,12 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getCpuThrottlingRate(), 4); + assert.strictEqual(context.getSelectedMcpPage().cpuThrottlingRate, 4); const page = await context.newPage(); context.selectPage(page); - assert.strictEqual(context.getCpuThrottlingRate(), 1); + assert.strictEqual(context.getSelectedMcpPage().cpuThrottlingRate, 1); }); }); }); @@ -183,7 +201,7 @@ describe('emulation', () => { context, ); - const geolocation = context.getGeolocation(); + const geolocation = context.getSelectedMcpPage().geolocation; assert.strictEqual(geolocation?.latitude, 48.137154); assert.strictEqual(geolocation?.longitude, 11.576124); }); @@ -206,7 +224,7 @@ describe('emulation', () => { context, ); - assert.notStrictEqual(context.getGeolocation(), null); + assert.notStrictEqual(context.getSelectedMcpPage().geolocation, null); // Then clear it by setting geolocation to null await emulate.handler( @@ -220,7 +238,7 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getGeolocation(), null); + assert.strictEqual(context.getSelectedMcpPage().geolocation, null); }); }); @@ -240,14 +258,14 @@ describe('emulation', () => { context, ); - const geolocation = context.getGeolocation(); + const geolocation = context.getSelectedMcpPage().geolocation; assert.strictEqual(geolocation?.latitude, 48.137154); assert.strictEqual(geolocation?.longitude, 11.576124); const page = await context.newPage(); context.selectPage(page); - assert.strictEqual(context.getGeolocation(), null); + assert.strictEqual(context.getSelectedMcpPage().geolocation, null); }); }); }); @@ -338,7 +356,7 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getViewport(), null); + assert.strictEqual(context.getSelectedMcpPage().viewport, null); // Somehow reset of the viewport seems to be async. await context.getSelectedPptrPage().waitForFunction(() => { @@ -363,12 +381,12 @@ describe('emulation', () => { context, ); - assert.ok(context.getViewport()); + assert.ok(context.getSelectedMcpPage().viewport); const page = await context.newPage(); context.selectPage(page); - assert.strictEqual(context.getViewport(), null); + assert.strictEqual(context.getSelectedMcpPage().viewport, null); assert.ok( await context.getSelectedPptrPage().evaluate(() => { return window.innerWidth !== 400 && window.innerHeight !== 400; @@ -392,7 +410,7 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getUserAgent(), 'MyUA'); + assert.strictEqual(context.getSelectedMcpPage().userAgent, 'MyUA'); const page = context.getSelectedPptrPage(); const ua = await page.evaluate(() => navigator.userAgent); assert.strictEqual(ua, 'MyUA'); @@ -411,7 +429,7 @@ describe('emulation', () => { response, context, ); - assert.strictEqual(context.getUserAgent(), 'UA1'); + assert.strictEqual(context.getSelectedMcpPage().userAgent, 'UA1'); await emulate.handler( { @@ -423,7 +441,7 @@ describe('emulation', () => { response, context, ); - assert.strictEqual(context.getUserAgent(), 'UA2'); + assert.strictEqual(context.getSelectedMcpPage().userAgent, 'UA2'); const page = context.getSelectedPptrPage(); const ua = await page.evaluate(() => navigator.userAgent); assert.strictEqual(ua, 'UA2'); @@ -443,7 +461,7 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getUserAgent(), 'MyUA'); + assert.strictEqual(context.getSelectedMcpPage().userAgent, 'MyUA'); await emulate.handler( { @@ -456,7 +474,7 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getUserAgent(), null); + assert.strictEqual(context.getSelectedMcpPage().userAgent, null); const page = context.getSelectedPptrPage(); const ua = await page.evaluate(() => navigator.userAgent); assert.notStrictEqual(ua, 'MyUA'); @@ -477,12 +495,12 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getUserAgent(), 'MyUA'); + assert.strictEqual(context.getSelectedMcpPage().userAgent, 'MyUA'); const page = await context.newPage(); context.selectPage(page); - assert.strictEqual(context.getUserAgent(), null); + assert.strictEqual(context.getSelectedMcpPage().userAgent, null); assert.ok( await context.getSelectedPptrPage().evaluate(() => { return navigator.userAgent !== 'MyUA'; @@ -506,7 +524,7 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getColorScheme(), 'dark'); + assert.strictEqual(context.getSelectedMcpPage().colorScheme, 'dark'); const page = context.getSelectedPptrPage(); const scheme = await page.evaluate(() => window.matchMedia('(prefers-color-scheme: dark)').matches @@ -529,7 +547,7 @@ describe('emulation', () => { response, context, ); - assert.strictEqual(context.getColorScheme(), 'dark'); + assert.strictEqual(context.getSelectedMcpPage().colorScheme, 'dark'); await emulate.handler( { @@ -541,7 +559,7 @@ describe('emulation', () => { response, context, ); - assert.strictEqual(context.getColorScheme(), 'light'); + assert.strictEqual(context.getSelectedMcpPage().colorScheme, 'light'); const page = context.getSelectedPptrPage(); const scheme = await page.evaluate(() => window.matchMedia('(prefers-color-scheme: light)').matches @@ -570,7 +588,7 @@ describe('emulation', () => { response, context, ); - assert.strictEqual(context.getColorScheme(), 'dark'); + assert.strictEqual(context.getSelectedMcpPage().colorScheme, 'dark'); // Check manually that it is dark assert.strictEqual( @@ -591,7 +609,7 @@ describe('emulation', () => { context, ); - assert.strictEqual(context.getColorScheme(), null); + assert.strictEqual(context.getSelectedMcpPage().colorScheme, null); assert.strictEqual( await page.evaluate( () => window.matchMedia('(prefers-color-scheme: dark)').matches, diff --git a/tests/tools/input.test.ts b/tests/tools/input.test.ts index 19f7efb1d..8182a441a 100644 --- a/tests/tools/input.test.ts +++ b/tests/tools/input.test.ts @@ -36,7 +36,7 @@ describe('input', () => { await page.setContent( html``, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await click.handler( { params: { @@ -63,7 +63,7 @@ describe('input', () => { >test`, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await click.handler( { params: { @@ -98,7 +98,7 @@ describe('input', () => { await withMcpContext(async (response, context) => { const page = context.getSelectedPptrPage(); await page.goto(server.getRoute('/link')); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); const clickPromise = click.handler( { params: { @@ -141,7 +141,7 @@ describe('input', () => { await withMcpContext(async (response, context) => { const page = context.getSelectedPptrPage(); await page.goto(server.getRoute('/unstable')); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); const handlerResolveTime = await click .handler( { @@ -169,7 +169,7 @@ describe('input', () => { await page.setContent( html``, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await click.handler( { params: { @@ -194,7 +194,7 @@ describe('input', () => { await page.setContent( html``, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await click.handler( { params: { @@ -222,7 +222,7 @@ describe('input', () => { await page.setContent( html``, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await hover.handler( { params: { @@ -253,7 +253,7 @@ describe('input', () => { onclick="this.innerText = 'clicked'" >`, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await clickAt.handler( { params: { @@ -283,7 +283,7 @@ describe('input', () => { ondblclick="this.innerText = 'dblclicked'" >`, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await clickAt.handler( { params: { @@ -311,7 +311,7 @@ describe('input', () => { await withMcpContext(async (response, context) => { const page = context.getSelectedPptrPage(); await page.setContent(html``); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await fill.handler( { params: { @@ -341,7 +341,7 @@ describe('input', () => { >`, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await fill.handler( { params: { @@ -369,7 +369,7 @@ describe('input', () => { await withMcpContext(async (response, context) => { const page = context.getSelectedPptrPage(); await page.setContent(html``); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await fill.handler( { params: { @@ -398,7 +398,7 @@ describe('input', () => { await withMcpContext(async (response, context) => { const page = context.getSelectedPptrPage(); await page.setContent(html``); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); page.setDefaultTimeout(1000); await fill.handler( { @@ -431,7 +431,7 @@ describe('input', () => { const page = context.getSelectedPptrPage(); await page.setContent(html``); await page.click('textarea'); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await typeText.handler( { params: { @@ -457,7 +457,7 @@ describe('input', () => { const page = context.getSelectedPptrPage(); await page.setContent(html``); await page.click('textarea'); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await typeText.handler( { params: { @@ -494,7 +494,7 @@ describe('input', () => { const page = context.getSelectedPptrPage(); await page.setContent(html``); await page.click('textarea'); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); try { await typeText.handler( { @@ -528,7 +528,7 @@ describe('input', () => { /> `, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); // Fill email const response1 = new McpResponse({} as ParsedArguments); @@ -622,7 +622,7 @@ describe('input', () => { }); `, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await drag.handler( { params: { @@ -666,7 +666,7 @@ describe('input', () => { /> `, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await fillForm.handler( { params: { @@ -721,7 +721,7 @@ describe('input', () => { /> `, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await uploadFile.handler( { params: { @@ -764,7 +764,7 @@ describe('input', () => { }); `, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await uploadFile.handler( { params: { @@ -798,7 +798,7 @@ describe('input', () => { await withMcpContext(async (response, context) => { const page = context.getSelectedPptrPage(); await page.setContent(html`
Not a file input
`); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await assert.rejects( uploadFile.handler( @@ -864,7 +864,7 @@ describe('input', () => { document.addEventListener('keyup', e => logs.push('u' + e.key)); `, ); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await pressKey.handler( { diff --git a/tests/tools/screenshot.test.ts b/tests/tools/screenshot.test.ts index 49830a965..9d177f9bb 100644 --- a/tests/tools/screenshot.test.ts +++ b/tests/tools/screenshot.test.ts @@ -155,7 +155,7 @@ describe('screenshot', () => { const page = context.getSelectedPptrPage(); await page.setContent(fixture.html); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await screenshot.handler( { params: { diff --git a/tests/tools/script.test.ts b/tests/tools/script.test.ts index c0203d718..1ddaea4c7 100644 --- a/tests/tools/script.test.ts +++ b/tests/tools/script.test.ts @@ -124,7 +124,7 @@ describe('script', () => { await page.setContent(html``); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await evaluateScript.handler( { @@ -150,7 +150,7 @@ describe('script', () => { await page.setContent(html``); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await evaluateScript.handler( { @@ -180,7 +180,7 @@ describe('script', () => { await withMcpContext(async (response, context) => { const page = context.getSelectedPptrPage(); await page.goto(server.getRoute('/main')); - await context.createTextSnapshot(); + await context.createTextSnapshot(context.getSelectedMcpPage()); await evaluateScript.handler( { params: {