diff --git a/docs/src/api/class-inspector.md b/docs/src/api/class-inspector.md index 07a4f39503feb..a642cc9752e52 100644 --- a/docs/src/api/class-inspector.md +++ b/docs/src/api/class-inspector.md @@ -4,16 +4,24 @@ Interface to the Playwright inspector. +## async method: Inspector.cancelPickLocator +* since: v1.59 + +Cancels an ongoing [`method: Inspector.pickLocator`] call by deactivating pick locator mode. +If no pick locator mode is active, this method is a no-op. + +## async method: Inspector.pickLocator +* since: v1.59 +- returns: <[Locator]> + +Enters pick locator mode where hovering over page elements highlights them and shows the corresponding locator. +Once the user clicks an element, the mode is deactivated and the [Locator] for the picked element is returned. + **Usage** ```js -const inspector = page.inspector(); -inspector.on('screencastframe', ({ data, width, height }) => { - console.log(`received frame ${width}x${height}, jpeg size: ${data.length}`); -}); -await inspector.startScreencast(); -// ... perform actions ... -await inspector.stopScreencast(); +const locator = await page.inspector().pickLocator(); +console.log(locator); ``` ## event: Inspector.screencastFrame diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 4618253e18d4d..8e89b1af12a95 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -2864,30 +2864,6 @@ the place it was paused. This method requires Playwright to be started in a headed mode, with a falsy [`option: BrowserType.launch.headless`] option. ::: -## async method: Page.cancelPickLocator -* since: v1.59 - -Cancels an ongoing [`method: Page.pickLocator`] call by deactivating pick locator mode. -If no pick locator mode is active, this method is a no-op. - -## async method: Page.pickLocator -* since: v1.59 -- returns: <[Locator]> - -Enters pick locator mode where hovering over page elements highlights them and shows the corresponding locator. -Once the user clicks an element, the mode is deactivated and the [Locator] for the picked element is returned. - -:::note -This method requires Playwright to be started in a headed mode. -::: - -**Usage** - -```js -const locator = await page.pickLocator(); -console.log(locator); -``` - ## async method: Page.pdf * since: v1.8 - returns: <[Buffer]> diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 5cfbb21320586..e20be3e3d7c98 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -2187,12 +2187,6 @@ export interface Page { */ bringToFront(): Promise; - /** - * Cancels an ongoing [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) call by - * deactivating pick locator mode. If no pick locator mode is active, this method is a no-op. - */ - cancelPickLocator(): Promise; - /** * **NOTE** Use locator-based [locator.check([options])](https://playwright.dev/docs/api/class-locator#locator-check) instead. * Read more about [locators](https://playwright.dev/docs/locators). @@ -3928,23 +3922,6 @@ export interface Page { width?: string|number; }): Promise; - /** - * Enters pick locator mode where hovering over page elements highlights them and shows the corresponding locator. - * Once the user clicks an element, the mode is deactivated and the - * [Locator](https://playwright.dev/docs/api/class-locator) for the picked element is returned. - * - * **NOTE** This method requires Playwright to be started in a headed mode. - * - * **Usage** - * - * ```js - * const locator = await page.pickLocator(); - * console.log(locator); - * ``` - * - */ - pickLocator(): Promise; - /** * **NOTE** Use locator-based [locator.press(key[, options])](https://playwright.dev/docs/api/class-locator#locator-press) * instead. Read more about [locators](https://playwright.dev/docs/locators). @@ -20438,19 +20415,6 @@ export interface FrameLocator { /** * Interface to the Playwright inspector. - * - * **Usage** - * - * ```js - * const inspector = page.inspector(); - * inspector.on('screencastframe', ({ data, width, height }) => { - * console.log(`received frame ${width}x${height}, jpeg size: ${data.length}`); - * }); - * await inspector.startScreencast(); - * // ... perform actions ... - * await inspector.stopScreencast(); - * ``` - * */ export interface Inspector { /** @@ -20615,6 +20579,28 @@ export interface Inspector { height: number; }) => any): this; + /** + * Cancels an ongoing + * [inspector.pickLocator()](https://playwright.dev/docs/api/class-inspector#inspector-pick-locator) call by + * deactivating pick locator mode. If no pick locator mode is active, this method is a no-op. + */ + cancelPickLocator(): Promise; + + /** + * Enters pick locator mode where hovering over page elements highlights them and shows the corresponding locator. + * Once the user clicks an element, the mode is deactivated and the + * [Locator](https://playwright.dev/docs/api/class-locator) for the picked element is returned. + * + * **Usage** + * + * ```js + * const locator = await page.inspector().pickLocator(); + * console.log(locator); + * ``` + * + */ + pickLocator(): Promise; + /** * Starts capturing screencast frames. Frames are emitted as * [inspector.on('screencastframe')](https://playwright.dev/docs/api/class-inspector#inspector-event-screencast-frame) diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index 9fb558e4522da..1c0c6a2dba225 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -17,16 +17,16 @@ }, { "name": "chromium-tip-of-tree", - "revision": "1409", + "revision": "1410", "installByDefault": false, - "browserVersion": "147.0.7705.0", + "browserVersion": "147.0.7714.0", "title": "Chrome Canary for Testing" }, { "name": "chromium-tip-of-tree-headless-shell", - "revision": "1409", + "revision": "1410", "installByDefault": false, - "browserVersion": "147.0.7705.0", + "browserVersion": "147.0.7714.0", "title": "Chrome Canary Headless Shell" }, { diff --git a/packages/playwright-core/src/client/inspector.ts b/packages/playwright-core/src/client/inspector.ts index 98b81c347cb15..c59816888fef6 100644 --- a/packages/playwright-core/src/client/inspector.ts +++ b/packages/playwright-core/src/client/inspector.ts @@ -17,6 +17,7 @@ import { EventEmitter } from './eventEmitter'; import type * as api from '../../types/types'; +import type { Locator } from './locator'; import type { Page } from './page'; export class Inspector extends EventEmitter implements api.Inspector { @@ -28,6 +29,15 @@ export class Inspector extends EventEmitter implements api.Inspector { this._page._channel.on('screencastFrame', ({ data, width, height }) => this.emit('screencastframe', { data, width, height })); } + async pickLocator(): Promise { + const { selector } = await this._page._channel.pickLocator({}); + return this._page.locator(selector); + } + + async cancelPickLocator(): Promise { + await this._page._channel.cancelPickLocator({}); + } + async startScreencast(options: { size?: { width: number, height: number } } = {}): Promise { await this._page._channel.startScreencast(options); } diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index d1eaca443e12e..72a4f9eda5218 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -826,15 +826,6 @@ export class Page extends ChannelOwner implements api.Page this._browserContext.setDefaultTimeout(defaultTimeout); } - async pickLocator(): Promise { - const { selector } = await this._channel.pickLocator({}); - return this.locator(selector); - } - - async cancelPickLocator(): Promise { - await this._channel.cancelPickLocator({}); - } - async pdf(options: PDFOptions = {}): Promise { const transportOptions: channels.PagePdfParams = { ...options } as channels.PagePdfParams; if (transportOptions.margin) diff --git a/packages/playwright-core/src/server/har/harTracer.ts b/packages/playwright-core/src/server/har/harTracer.ts index dc6399abf7fe3..bf23b3dc26d43 100644 --- a/packages/playwright-core/src/server/har/harTracer.ts +++ b/packages/playwright-core/src/server/har/harTracer.ts @@ -362,6 +362,11 @@ export class HarTracer { }); this._addBarrier(page || request.serviceWorker(), promise); + this._addBarrier(page || request.serviceWorker(), response.httpVersion().then(httpVersion => { + harEntry.request.httpVersion = httpVersion; + harEntry.response.httpVersion = httpVersion; + })); + // Response end timing is only available after the response event was received. const timing = response.timing(); harEntry.timings.receive = response.request()._responseEndTiming !== -1 ? helper.millisToRoundishMillis(response.request()._responseEndTiming - timing.responseStart) : -1; @@ -462,11 +467,6 @@ export class HarTracer { _transferSize: this._options.omitSizes ? undefined : -1 }; - this._addBarrier(page || request.serviceWorker(), response.httpVersion().then(httpVersion => { - harEntry.request.httpVersion = httpVersion; - harEntry.response.httpVersion = httpVersion; - })); - if (!this._options.omitTiming) { const startDateTime = pageEntry ? ((pageEntry as any)[startedDateSymbol] as Date).valueOf() : 0; const timing = response.timing(); diff --git a/packages/playwright-core/src/server/webkit/wkPage.ts b/packages/playwright-core/src/server/webkit/wkPage.ts index 4751ae522babe..9eb7695498180 100644 --- a/packages/playwright-core/src/server/webkit/wkPage.ts +++ b/packages/playwright-core/src/server/webkit/wkPage.ts @@ -1094,6 +1094,7 @@ export class WKPage implements PageDelegate { private _handleRequestRedirect(request: WKInterceptableRequest, requestId: string, responsePayload: Protocol.Network.Response, timestamp: number) { const response = request.createResponse(responsePayload); + response._setHttpVersion(null); response._securityDetailsFinished(); response._serverAddrFinished(); response.setResponseHeadersSize(null); diff --git a/packages/playwright-core/src/utils/isomorphic/trace/entries.ts b/packages/playwright-core/src/utils/isomorphic/trace/entries.ts index b203acb9a19dd..5fb51f63f4ca4 100644 --- a/packages/playwright-core/src/utils/isomorphic/trace/entries.ts +++ b/packages/playwright-core/src/utils/isomorphic/trace/entries.ts @@ -41,6 +41,7 @@ export type ContextEntry = { errors: trace.ErrorTraceEvent[]; hasSource: boolean; contextId: string; + testTimeout?: number; }; export type PageEntry = { diff --git a/packages/playwright-core/src/utils/isomorphic/trace/traceModel.ts b/packages/playwright-core/src/utils/isomorphic/trace/traceModel.ts index 91cfeb14bff52..0ca3b802ba4c5 100644 --- a/packages/playwright-core/src/utils/isomorphic/trace/traceModel.ts +++ b/packages/playwright-core/src/utils/isomorphic/trace/traceModel.ts @@ -89,6 +89,7 @@ export class TraceModel { resources: ResourceEntry[]; readonly actionCounters: Map; readonly traceUri: string; + readonly testTimeout?: number; constructor(traceUri: string, contexts: ContextEntry[]) { @@ -104,6 +105,7 @@ export class TraceModel { this.playwrightVersion = contexts.find(c => c.playwrightVersion)?.playwrightVersion; this.title = libraryContext?.title || ''; this.options = libraryContext?.options || {}; + this.testTimeout = contexts.find(c => c.origin === 'testRunner')?.testTimeout; // Next call updates all timestamps for all events in library contexts, so it must be done first. this.actions = mergeActionsAndUpdateTiming(contexts); this.pages = ([] as PageEntry[]).concat(...contexts.map(c => c.pages)); diff --git a/packages/playwright-core/src/utils/isomorphic/trace/traceModernizer.ts b/packages/playwright-core/src/utils/isomorphic/trace/traceModernizer.ts index f2b4f159deef0..20e703145f553 100644 --- a/packages/playwright-core/src/utils/isomorphic/trace/traceModernizer.ts +++ b/packages/playwright-core/src/utils/isomorphic/trace/traceModernizer.ts @@ -98,6 +98,7 @@ export class TraceModernizer { contextEntry.options = event.options; contextEntry.testIdAttributeName = event.testIdAttributeName; contextEntry.contextId = event.contextId ?? ''; + contextEntry.testTimeout = event.testTimeout; break; } case 'screencast-frame': { diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 5cfbb21320586..e20be3e3d7c98 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -2187,12 +2187,6 @@ export interface Page { */ bringToFront(): Promise; - /** - * Cancels an ongoing [page.pickLocator()](https://playwright.dev/docs/api/class-page#page-pick-locator) call by - * deactivating pick locator mode. If no pick locator mode is active, this method is a no-op. - */ - cancelPickLocator(): Promise; - /** * **NOTE** Use locator-based [locator.check([options])](https://playwright.dev/docs/api/class-locator#locator-check) instead. * Read more about [locators](https://playwright.dev/docs/locators). @@ -3928,23 +3922,6 @@ export interface Page { width?: string|number; }): Promise; - /** - * Enters pick locator mode where hovering over page elements highlights them and shows the corresponding locator. - * Once the user clicks an element, the mode is deactivated and the - * [Locator](https://playwright.dev/docs/api/class-locator) for the picked element is returned. - * - * **NOTE** This method requires Playwright to be started in a headed mode. - * - * **Usage** - * - * ```js - * const locator = await page.pickLocator(); - * console.log(locator); - * ``` - * - */ - pickLocator(): Promise; - /** * **NOTE** Use locator-based [locator.press(key[, options])](https://playwright.dev/docs/api/class-locator#locator-press) * instead. Read more about [locators](https://playwright.dev/docs/locators). @@ -20438,19 +20415,6 @@ export interface FrameLocator { /** * Interface to the Playwright inspector. - * - * **Usage** - * - * ```js - * const inspector = page.inspector(); - * inspector.on('screencastframe', ({ data, width, height }) => { - * console.log(`received frame ${width}x${height}, jpeg size: ${data.length}`); - * }); - * await inspector.startScreencast(); - * // ... perform actions ... - * await inspector.stopScreencast(); - * ``` - * */ export interface Inspector { /** @@ -20615,6 +20579,28 @@ export interface Inspector { height: number; }) => any): this; + /** + * Cancels an ongoing + * [inspector.pickLocator()](https://playwright.dev/docs/api/class-inspector#inspector-pick-locator) call by + * deactivating pick locator mode. If no pick locator mode is active, this method is a no-op. + */ + cancelPickLocator(): Promise; + + /** + * Enters pick locator mode where hovering over page elements highlights them and shows the corresponding locator. + * Once the user clicks an element, the mode is deactivated and the + * [Locator](https://playwright.dev/docs/api/class-locator) for the picked element is returned. + * + * **Usage** + * + * ```js + * const locator = await page.inspector().pickLocator(); + * console.log(locator); + * ``` + * + */ + pickLocator(): Promise; + /** * Starts capturing screencast frames. Frames are emitted as * [inspector.on('screencastframe')](https://playwright.dev/docs/api/class-inspector#inspector-event-screencast-frame) diff --git a/packages/playwright/src/worker/testTracing.ts b/packages/playwright/src/worker/testTracing.ts index fa99c112a907a..a82cdb917af57 100644 --- a/packages/playwright/src/worker/testTracing.ts +++ b/packages/playwright/src/worker/testTracing.ts @@ -168,6 +168,8 @@ export class TestTracing { } async stopIfNeeded() { + this._contextCreatedEvent.testTimeout = this._testInfo.timeout; + if (!this._options) return; diff --git a/packages/trace-viewer/src/ui/metadataView.tsx b/packages/trace-viewer/src/ui/metadataView.tsx index 20d3b6c5a9a27..1a863f4c4ce53 100644 --- a/packages/trace-viewer/src/ui/metadataView.tsx +++ b/packages/trace-viewer/src/ui/metadataView.tsx @@ -31,6 +31,7 @@ export const MetadataView: React.FunctionComponent<{
Time
{!!wallTime &&
start time:{wallTime}
}
duration:{msToString(model.endTime - model.startTime)}
+ {model.testTimeout !== undefined &&
test timeout:{msToString(model.testTimeout)}
}
Browser
engine:{model.browserName}
{model.channel &&
channel:{model.channel}
} diff --git a/packages/trace/src/trace.ts b/packages/trace/src/trace.ts index 3ffa6c4586874..d11ac4dc91045 100644 --- a/packages/trace/src/trace.ts +++ b/packages/trace/src/trace.ts @@ -46,6 +46,7 @@ export type ContextCreatedTraceEvent = { sdkLanguage?: Language, testIdAttributeName?: string, contextId?: string, + testTimeout?: number, }; export type ScreencastFrameTraceEvent = { diff --git a/tests/library/inspector/recorder-api.spec.ts b/tests/library/inspector/recorder-api.spec.ts index 75fd778003d96..c0874ff83884f 100644 --- a/tests/library/inspector/recorder-api.spec.ts +++ b/tests/library/inspector/recorder-api.spec.ts @@ -152,11 +152,11 @@ test('should disable recorder', async ({ context }) => { expect(log.action('click')).toHaveLength(2); }); -test('page.pickLocator should return locator for picked element', async ({ page }) => { +test('inspector.pickLocator should return locator for picked element', async ({ page }) => { await page.setContent(``); const scriptReady = page.waitForEvent('console', msg => msg.text() === 'Recorder script ready for test'); - const pickPromise = page.pickLocator(); + const pickPromise = page.inspector().pickLocator(); await scriptReady; const box = await page.getByRole('button', { name: 'Submit' }).boundingBox(); @@ -166,15 +166,15 @@ test('page.pickLocator should return locator for picked element', async ({ page await expect(locator).toHaveText('Submit'); }); -test('page.cancelPickLocator should cancel ongoing pickLocator', async ({ page }) => { +test('inspector.cancelPickLocator should cancel ongoing pickLocator', async ({ page }) => { await page.setContent(``); const scriptReady = page.waitForEvent('console', msg => msg.text() === 'Recorder script ready for test'); - const pickPromise = page.pickLocator(); + const pickPromise = page.inspector().pickLocator(); await scriptReady; await Promise.all([ - page.cancelPickLocator(), + page.inspector().cancelPickLocator(), expect(pickPromise).rejects.toThrow('Locator picking was cancelled'), ]); }); diff --git a/tests/playwright-test/playwright.trace.spec.ts b/tests/playwright-test/playwright.trace.spec.ts index a39a0e8d87fc8..e771195992e05 100644 --- a/tests/playwright-test/playwright.trace.spec.ts +++ b/tests/playwright-test/playwright.trace.spec.ts @@ -1389,3 +1389,34 @@ test('should record trace snapshot for more obscure commands', async ({ runInlin expect(trace.snapshots.snapshotByName(snapshotFrameOrPageId, boundingBoxAction.beforeSnapshot)).toBeTruthy(); expect(trace.snapshots.snapshotByName(snapshotFrameOrPageId, boundingBoxAction.afterSnapshot)).toBeTruthy(); }); + +test('should record default test timeout in trace', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('pass', async ({}) => { + }); + `, + }, { trace: 'on' }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + const trace = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip')); + expect(trace.model.testTimeout).toBe(30_000); +}); + +test('should record custom test timeout in trace', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('pass', async ({}) => { + test.setTimeout(120_000); + }); + `, + }, { trace: 'on' }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + const trace = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip')); + expect(trace.model.testTimeout).toBe(120_000); +});