diff --git a/packages/playwright-test/src/matchers/expect.ts b/packages/playwright-test/src/matchers/expect.ts index a18214f538..b6c63888bc 100644 --- a/packages/playwright-test/src/matchers/expect.ts +++ b/packages/playwright-test/src/matchers/expect.ts @@ -250,11 +250,12 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler { const argsSuffix = computeArgsSuffix(matcherName, args); const defaultTitle = `expect${this._info.isPoll ? '.poll' : ''}${this._info.isSoft ? '.soft' : ''}${this._info.isNot ? '.not' : ''}.${matcherName}${argsSuffix}`; + const title = customMessage || defaultTitle; const wallTime = Date.now(); const step = matcherName !== 'toPass' ? testInfo._addStep({ location: stackFrames[0], category: 'expect', - title: trimLongString(customMessage || defaultTitle, 1024), + title: trimLongString(title, 1024), params: args[0] ? { expected: args[0] } : undefined, wallTime, infectParentStepsWithError: this._info.isSoft, @@ -301,7 +302,7 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler { if (this._info.isPoll || (matcherName in customAsyncMatchers && matcherName !== 'toPass')) { return (async () => { try { - const expectZone: ExpectZone = { title: defaultTitle, wallTime }; + const expectZone: ExpectZone = { title, wallTime }; await zones.run('expectZone', expectZone, async () => { // We assume that the matcher will read the current expect timeout the first thing. setCurrentExpectConfigureTimeout(this._info.timeout); diff --git a/tests/playwright-test/playwright.trace.spec.ts b/tests/playwright-test/playwright.trace.spec.ts index 0fd48c517a..ae785fea38 100644 --- a/tests/playwright-test/playwright.trace.spec.ts +++ b/tests/playwright-test/playwright.trace.spec.ts @@ -731,3 +731,38 @@ test('should not throw when screenshot on failure fails', async ({ runInlineTest // One screenshot for the page, no screenshot for pdf page since it should have failed. expect(attachedScreenshots.length).toBe(1); }); + +test('should use custom expect message in trace', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { use: { trace: { mode: 'on' } } }; + `, + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('fail', async ({ page }) => { + await expect( + page.getByRole('button', { name: 'Find a hotel' }), + 'expect to have text: find a hotel' + ).toHaveCount(0); + }); + `, + }, { workers: 1 }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + const trace = await parseTrace(testInfo.outputPath('test-results', 'a-fail', 'trace.zip')); + expect(trace.actionTree).toEqual([ + 'Before Hooks', + ' fixture: browser', + ' browserType.launch', + ' fixture: context', + ' browser.newContext', + ' tracing.start', + ' fixture: page', + ' browserContext.newPage', + 'expect to have text: find a hotel', + 'After Hooks', + ' fixture: page', + ' fixture: context', + ]); +});