diff --git a/src/server/supplements/inspectorController.ts b/src/server/supplements/inspectorController.ts index 8d8f8bdc9ca07..aa65e89a19008 100644 --- a/src/server/supplements/inspectorController.ts +++ b/src/server/supplements/inspectorController.ts @@ -75,6 +75,7 @@ export class InspectorController implements InstrumentationListener { case 'after': const originalMetadata = this._waitOperations.get(info.waitId)!; originalMetadata.endTime = metadata.endTime; + originalMetadata.error = info.error; this._waitOperations.delete(info.waitId); metadata = originalMetadata; break; diff --git a/src/server/supplements/recorderSupplement.ts b/src/server/supplements/recorderSupplement.ts index a39ef97282486..3de8ae642f6e8 100644 --- a/src/server/supplements/recorderSupplement.ts +++ b/src/server/supplements/recorderSupplement.ts @@ -447,8 +447,10 @@ export class RecorderSupplement { selector: metadata.params?.selector, }; let duration = metadata.endTime ? metadata.endTime - metadata.startTime : undefined; - if (duration && metadata.pauseStartTime && metadata.pauseEndTime) + if (typeof duration === 'number' && metadata.pauseStartTime && metadata.pauseEndTime) { duration -= (metadata.pauseEndTime - metadata.pauseStartTime); + duration = Math.max(duration, 0); + } logs.push({ id: metadata.id, messages: metadata.log, diff --git a/test/pause.spec.ts b/test/pause.spec.ts index d9140b327e8f5..841e9bd814645 100644 --- a/test/pause.spec.ts +++ b/test/pause.spec.ts @@ -163,6 +163,23 @@ describe('pause', (suite, { mode }) => { await scriptPromise; }); + it('should highlight waitForEvent', async ({page, recorderPageGetter}) => { + await page.setContent(''); + const scriptPromise = (async () => { + await page.pause(); + await Promise.all([ + page.waitForEvent('console'), + page.click('button'), + ]); + })(); + const recorderPage = await recorderPageGetter(); + await recorderPage.click('[title="Step over"]'); + await recorderPage.waitForSelector('.source-line-paused:has-text("page.click")'); + await recorderPage.waitForSelector('.source-line-running:has-text("page.waitForEvent")'); + await recorderPage.click('[title="Resume"]'); + await scriptPromise; + }); + it('should populate log with waitForEvent', async ({page, recorderPageGetter}) => { await page.setContent(''); const scriptPromise = (async () => { @@ -178,7 +195,7 @@ describe('pause', (suite, { mode }) => { await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")'); expect(await sanitizeLog(recorderPage)).toEqual([ 'page.pause- XXms', - 'page.waitForEvent(console)- XXms', + 'page.waitForEvent(console)', 'page.click(button)- XXms', 'page.pause', ]); @@ -200,12 +217,36 @@ describe('pause', (suite, { mode }) => { 'page.isChecked(button)- XXms', 'checking \"checked\" state of \"button\"', 'selector resolved to ', - 'Not a checkbox or radio button', + 'error: Not a checkbox or radio button', ]); const error = await scriptPromise; expect(error.message).toContain('Not a checkbox or radio button'); }); + it('should populate log with error in waitForEvent', async ({page, recorderPageGetter}) => { + await page.setContent(''); + const scriptPromise = (async () => { + await page.pause(); + await Promise.all([ + page.waitForEvent('console', { timeout: 1 }), + page.click('button'), + ]); + })().catch(() => {}); + const recorderPage = await recorderPageGetter(); + await recorderPage.click('[title="Step over"]'); + await recorderPage.waitForSelector('.source-line-paused:has-text("page.click")'); + await recorderPage.waitForSelector('.source-line-error:has-text("page.waitForEvent")'); + await recorderPage.click('[title="Resume"]'); + expect(await sanitizeLog(recorderPage)).toEqual([ + 'page.pause- XXms', + 'page.waitForEvent(console)', + 'waiting for event \"console\"', + 'error: Timeout while waiting for event \"console\"', + 'page.click(button)- XXms', + ]); + await scriptPromise; + }); + it('should pause on page close', async ({ page, recorderPageGetter }) => { const scriptPromise = (async () => { await page.pause(); @@ -234,13 +275,11 @@ describe('pause', (suite, { mode }) => { async function sanitizeLog(recorderPage: Page): Promise { const results = []; for (const entry of await recorderPage.$$('.call-log-call')) { - const header = await (await (await entry.$('.call-log-call-header')).textContent()).replace(/— \d+(\.\d+)?(ms|s)/, '- XXms'); - results.push(header); - results.push(...await entry.$$eval('.call-log-message', ee => ee.map(e => e.textContent))); - const errorElement = await entry.$('.call-log-error'); - const error = errorElement ? await errorElement.textContent() : undefined; - if (error) - results.push(error); + 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; + }))); } return results; }