diff --git a/packages/playwright-core/src/client/clientInstrumentation.ts b/packages/playwright-core/src/client/clientInstrumentation.ts index 6d90911acd..55c787df05 100644 --- a/packages/playwright-core/src/client/clientInstrumentation.ts +++ b/packages/playwright-core/src/client/clientInstrumentation.ts @@ -24,7 +24,7 @@ export interface ClientInstrumentation { removeAllListeners(): void; onApiCallBegin(apiCall: string, params: Record, frames: StackFrame[], userData: any, out: { stepId?: string }): void; onApiCallEnd(userData: any, error?: Error): void; - onWillPause(): void; + onWillPause(options: { keepTestTimeout: boolean }): void; runAfterCreateBrowserContext(context: BrowserContext): Promise; runAfterCreateRequestContext(context: APIRequestContext): Promise; @@ -35,7 +35,7 @@ export interface ClientInstrumentation { export interface ClientInstrumentationListener { onApiCallBegin?(apiName: string, params: Record, frames: StackFrame[], userData: any, out: { stepId?: string }): void; onApiCallEnd?(userData: any, error?: Error): void; - onWillPause?(): void; + onWillPause?(options: { keepTestTimeout: boolean }): void; runAfterCreateBrowserContext?(context: BrowserContext): Promise; runAfterCreateRequestContext?(context: APIRequestContext): Promise; diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index c8d816f62a..f1d90fece2 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -786,14 +786,14 @@ export class Page extends ChannelOwner implements api.Page return [...this._workers]; } - async pause() { + async pause(_options?: { __testHookKeepTestTimeout: boolean }) { if (require('inspector').url()) return; const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout(); const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout(); this._browserContext.setDefaultNavigationTimeout(0); this._browserContext.setDefaultTimeout(0); - this._instrumentation?.onWillPause(); + this._instrumentation?.onWillPause({ keepTestTimeout: !!_options?.__testHookKeepTestTimeout }); await this._closedOrCrashedScope.safeRace(this.context()._channel.pause()); this._browserContext.setDefaultNavigationTimeout(defaultNavigationTimeout); this._browserContext.setDefaultTimeout(defaultTimeout); diff --git a/packages/playwright/src/index.ts b/packages/playwright/src/index.ts index 9364e6b879..a08f3790d9 100644 --- a/packages/playwright/src/index.ts +++ b/packages/playwright/src/index.ts @@ -287,8 +287,9 @@ const playwrightFixtures: Fixtures = ({ const step = userData.step; step?.complete({ error }); }, - onWillPause: () => { - currentTestInfo()?._setDebugMode(); + onWillPause: ({ keepTestTimeout }) => { + if (!keepTestTimeout) + currentTestInfo()?._setDebugMode(); }, runAfterCreateBrowserContext: async (context: BrowserContext) => { await artifactsRecorder?.didCreateBrowserContext(context); diff --git a/tests/library/browsertype-connect.spec.ts b/tests/library/browsertype-connect.spec.ts index 085c75c521..77bcd5647c 100644 --- a/tests/library/browsertype-connect.spec.ts +++ b/tests/library/browsertype-connect.spec.ts @@ -176,7 +176,8 @@ for (const kind of ['launchServer', 'run-server'] as const) { const browser = await connect(remoteServer.wsEndpoint()); const browserContext = await browser.newContext(); const page = await browserContext.newPage(); - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await browser.close(); (browserType as any)._defaultLaunchOptions.headless = headless; }); diff --git a/tests/library/inspector/console-api.spec.ts b/tests/library/inspector/console-api.spec.ts index 0305f5c5e3..50f4c5f063 100644 --- a/tests/library/inspector/console-api.spec.ts +++ b/tests/library/inspector/console-api.spec.ts @@ -22,7 +22,8 @@ let scriptPromise: Promise; it.beforeEach(async ({ page, recorderPageGetter }) => { scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); await recorderPageGetter(); }); diff --git a/tests/library/inspector/pause.spec.ts b/tests/library/inspector/pause.spec.ts index 858ededfcb..3dfc3ba036 100644 --- a/tests/library/inspector/pause.spec.ts +++ b/tests/library/inspector/pause.spec.ts @@ -24,7 +24,8 @@ it('should resume when closing inspector', async ({ page, recorderPageGetter, cl it.skip(mode !== 'default'); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); await recorderPageGetter(); await closeRecorder(); @@ -35,7 +36,8 @@ it('should not reset timeouts', async ({ page, recorderPageGetter, closeRecorder page.context().setDefaultNavigationTimeout(1000); page.context().setDefaultTimeout(1000); - const pausePromise = page.pause(); + // @ts-ignore + const pausePromise = page.pause({ __testHookKeepTestTimeout: true }); await recorderPageGetter(); await closeRecorder(); await pausePromise; @@ -61,7 +63,8 @@ it.describe('pause', () => { it('should pause and resume the script', async ({ page, recorderPageGetter }) => { const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); const recorderPage = await recorderPageGetter(); await recorderPage.click('[title="Resume (F8)"]'); @@ -70,7 +73,8 @@ it.describe('pause', () => { it('should pause and resume the script with keyboard shortcut', async ({ page, recorderPageGetter }) => { const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); const recorderPage = await recorderPageGetter(); await recorderPage.keyboard.press('F8'); @@ -79,7 +83,8 @@ it.describe('pause', () => { it('should resume from console', async ({ page }) => { const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); await Promise.all([ page.waitForFunction(() => (window as any).playwright && (window as any).playwright.resume).then(() => { @@ -92,7 +97,8 @@ it.describe('pause', () => { it('should pause after a navigation', async ({ page, server, recorderPageGetter }) => { const scriptPromise = (async () => { await page.goto(server.EMPTY_PAGE); - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); const recorderPage = await recorderPageGetter(); await recorderPage.click('[title="Resume (F8)"]'); @@ -101,26 +107,29 @@ it.describe('pause', () => { it('should show source', async ({ page, recorderPageGetter }) => { const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); const recorderPage = await recorderPageGetter(); await expect(recorderPage.getByRole('combobox', { name: 'Source chooser' })).toHaveValue(/pause\.spec\.ts/); const source = await recorderPage.textContent('.source-line-paused'); - expect(source).toContain('page.pause()'); + expect(source).toContain('page.pause({ __testHookKeepTestTimeout: true })'); await recorderPage.click('[title="Resume (F8)"]'); await scriptPromise; }); it('should pause on next pause', async ({ page, recorderPageGetter }) => { const scriptPromise = (async () => { - await page.pause(); // 1 - await page.pause(); // 2 + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); // 1 + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); // 2 })(); const recorderPage = await recorderPageGetter(); const source = await recorderPage.textContent('.source-line-paused'); - expect(source).toContain('page.pause(); // 1'); + expect(source).toContain('page.pause({ __testHookKeepTestTimeout: true }); // 1'); await recorderPage.click('[title="Resume (F8)"]'); - await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")'); + await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause({ __testHookKeepTestTimeout: true }); // 2")'); await recorderPage.click('[title="Resume (F8)"]'); await scriptPromise; }); @@ -128,12 +137,13 @@ it.describe('pause', () => { it('should step', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.click('button'); })(); const recorderPage = await recorderPageGetter(); const source = await recorderPage.textContent('.source-line-paused'); - expect(source).toContain('page.pause();'); + expect(source).toContain('page.pause({ __testHookKeepTestTimeout: true });'); await recorderPage.click('[title="Step over (F10)"]'); await recorderPage.waitForSelector('.source-line-paused :has-text("page.click")'); @@ -145,12 +155,13 @@ it.describe('pause', () => { it('should step with keyboard shortcut', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.click('button'); })(); const recorderPage = await recorderPageGetter(); const source = await recorderPage.textContent('.source-line-paused'); - expect(source).toContain('page.pause();'); + expect(source).toContain('page.pause({ __testHookKeepTestTimeout: true });'); await recorderPage.keyboard.press('F10'); await recorderPage.waitForSelector('.source-line-paused :has-text("page.click")'); @@ -168,7 +179,8 @@ it.describe('pause', () => { `); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.frameLocator('iframe').locator('button').click(); })(); const recorderPage = await recorderPageGetter(); @@ -198,13 +210,15 @@ it.describe('pause', () => { it('should skip input when resuming', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.click('button'); - await page.pause(); // 2 + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); // 2 })(); const recorderPage = await recorderPageGetter(); await recorderPage.click('[title="Resume (F8)"]'); - await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")'); + await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause({ __testHookKeepTestTimeout: true }); // 2")'); await recorderPage.click('[title="Resume (F8)"]'); await scriptPromise; }); @@ -212,13 +226,15 @@ it.describe('pause', () => { it('should populate log', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.click('button'); - await page.pause(); // 2 + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); // 2 })(); const recorderPage = await recorderPageGetter(); await recorderPage.click('[title="Resume (F8)"]'); - await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")'); + await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause({ __testHookKeepTestTimeout: true }); // 2")'); expect(await sanitizeLog(recorderPage)).toEqual([ 'page.pause- XXms', 'page.click(page.locator(\'button\'))- XXms', @@ -232,16 +248,18 @@ it.describe('pause', () => { it.skip(trace === 'on'); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.context().tracing.start(); page.setDefaultTimeout(0); page.context().setDefaultNavigationTimeout(0); await page.context().tracing.stop(); - await page.pause(); // 2 + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); // 2 })(); const recorderPage = await recorderPageGetter(); await recorderPage.click('[title="Resume (F8)"]'); - await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")'); + await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause({ __testHookKeepTestTimeout: true }); // 2")'); expect(await sanitizeLog(recorderPage)).toEqual([ 'page.pause- XXms', 'page.pause', @@ -253,14 +271,16 @@ it.describe('pause', () => { it('should show expect.toHaveText', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await expect(page.locator('button')).toHaveText('Submit'); await expect(page.locator('button')).not.toHaveText('Submit2'); - await page.pause(); // 2 + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); // 2 })(); const recorderPage = await recorderPageGetter(); await recorderPage.click('[title="Resume (F8)"]'); - await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")'); + await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause({ __testHookKeepTestTimeout: true }); // 2")'); expect(await sanitizeLog(recorderPage)).toEqual([ 'page.pause- XXms', 'expect(page.locator(\'button\')).toHaveText()- XXms', @@ -274,7 +294,8 @@ it.describe('pause', () => { it('should highlight waitForEvent', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await Promise.all([ page.waitForEvent('console', msg => msg.type() === 'log' && msg.text() === '1'), page.click('button'), @@ -291,16 +312,18 @@ it.describe('pause', () => { it('should populate log with waitForEvent', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await Promise.all([ page.waitForEvent('console'), page.getByRole('button', { name: 'Submit' }).click(), ]); - await page.pause(); // 2 + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); // 2 })(); const recorderPage = await recorderPageGetter(); await recorderPage.click('[title="Resume (F8)"]'); - await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")'); + await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause({ __testHookKeepTestTimeout: true }); // 2")'); expect(await sanitizeLog(recorderPage)).toEqual([ 'page.pause- XXms', 'page.waitForEvent(console)', @@ -314,7 +337,8 @@ it.describe('pause', () => { it('should populate log with error', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.getByRole('button').isChecked(); })().catch(e => e); const recorderPage = await recorderPageGetter(); @@ -333,10 +357,12 @@ it.describe('pause', () => { it('should populate log with error in waitForEvent', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await Promise.all([ page.waitForEvent('console', { timeout: 1 }).catch(() => {}), - page.pause(), + // @ts-ignore + page.pause({ __testHookKeepTestTimeout: true }), ]); })(); const recorderPage = await recorderPageGetter(); @@ -356,7 +382,8 @@ it.describe('pause', () => { it('should pause on page close', async ({ page, recorderPageGetter }) => { const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.close(); })(); const recorderPage = await recorderPageGetter(); @@ -368,7 +395,8 @@ it.describe('pause', () => { it('should pause on context close', async ({ page, recorderPageGetter }) => { const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.context().close(); })(); const recorderPage = await recorderPageGetter(); @@ -382,7 +410,8 @@ it.describe('pause', () => { it('should highlight on explore', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); const recorderPage = await recorderPageGetter(); @@ -404,7 +433,8 @@ it.describe('pause', () => { try { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); const recorderPage = await recorderPageGetter(); @@ -432,7 +462,8 @@ it.describe('pause', () => { window.addEventListener(event, e => (window as any).log.push(e.type)); }); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); await page.keyboard.press('Enter'); await page.keyboard.press('A'); await page.keyboard.press('Shift+A'); @@ -467,7 +498,8 @@ it.describe('pause', () => { it('should highlight locators with custom testId', async ({ page, playwright, recorderPageGetter }) => { await page.setContent('
and me
'); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); playwright.selectors.setTestIdAttribute('data-custom-id'); await page.getByTestId('foo').click(); })(); @@ -486,11 +518,12 @@ it.describe('pause', () => { it('should record from debugger', async ({ page, recorderPageGetter }) => { await page.setContent(''); const scriptPromise = (async () => { - await page.pause(); + // @ts-ignore + await page.pause({ __testHookKeepTestTimeout: true }); })(); const recorderPage = await recorderPageGetter(); await expect(recorderPage.getByRole('combobox', { name: 'Source chooser' })).toHaveValue(/pause\.spec\.ts/); - await expect(recorderPage.locator('.source-line-paused')).toHaveText(/await page\.pause\(\)/); + await expect(recorderPage.locator('.source-line-paused')).toHaveText(/await page\.pause\(.*\)/); await recorderPage.getByRole('button', { name: 'Record' }).click(); const recorder = new Recorder(page, recorderPage); @@ -507,7 +540,7 @@ it.describe('pause', () => { async function sanitizeLog(recorderPage: Page): Promise { const results = []; for (const entry of await recorderPage.$$('.call-log-call')) { - const header = (await (await entry.$('.call-log-call-header'))!.textContent())!.replace(/— [\d.]+(ms|s)/, '- XXms'); + const header = (await (await entry.$('.call-log-call-header'))!.textContent())!.replace(/— [\d.]+(ms|s)/, '- XXms'); results.push(header.replace(/page\.waitForEvent\(console\).*/, 'page.waitForEvent(console)')); results.push(...await entry.$$eval('.call-log-message', ee => ee.map(e => { return (e.classList.contains('error') ? 'error: ' : '') + e.textContent; diff --git a/utils/doclint/missingDocs.js b/utils/doclint/missingDocs.js index 7fffc156dc..d18df13de2 100644 --- a/utils/doclint/missingDocs.js +++ b/utils/doclint/missingDocs.js @@ -120,7 +120,7 @@ function listMethods(rootNames, apiFileName) { function shouldSkipMethodByName(className, methodName) { if (methodName.startsWith('_') || methodName === 'T' || methodName === 'toString') return true; - if (/** @type {any} */(EventEmitter).prototype.hasOwnProperty(methodName)) + if (EventEmitter.prototype.hasOwnProperty(methodName)) return true; return false; } @@ -141,7 +141,7 @@ function listMethods(rootNames, apiFileName) { const memberType = checker.getTypeOfSymbolAtLocation(member, member.valueDeclaration); const signature = signatureForType(memberType); if (signature) - methods.set(name, new Set(signature.parameters.map(p => p.escapedName))); + methods.set(name, new Set(signature.parameters.filter(p => !p.escapedName.startsWith('_')).map(p => p.escapedName))); else methods.set(name, new Set()); }