diff --git a/docs/src/test-api/class-testinfoerror.md b/docs/src/test-api/class-testinfoerror.md index de0a561176..d5e3cb1cf2 100644 --- a/docs/src/test-api/class-testinfoerror.md +++ b/docs/src/test-api/class-testinfoerror.md @@ -4,29 +4,42 @@ Information about an error thrown during test execution. +## property: TestInfoError.actual +* since: v1.49 +- type: ?<[any]> + +Actual value. + +## property: TestInfoError.expected +* since: v1.49 +- type: ?<[any]> + +Expected value. + +## property: TestInfoError.log +* since: v1.49 +- type: ?<[Array]<[string]>> + +Call log. + ## property: TestInfoError.message * since: v1.10 - type: ?<[string]> Error message. Set when [Error] (or its subclass) has been thrown. +## property: TestInfoError.shortMessage +* since: v1.49 +- type: ?<[string]> + +Failure message. + ## property: TestInfoError.stack * since: v1.10 - type: ?<[string]> Error stack. Set when [Error] (or its subclass) has been thrown. -## property: TestInfoError.matcherResult -* since: v1.49 -- type: ?<[Object]> - - `name` <[string]> - - `message` ?<[string]> Failure message - - `log` ?<[Array]<[string]>> Call log - - `expected` ?<[any]> - - `actual` ?<[any]> - -Expect matcher result. - ## property: TestInfoError.value * since: v1.10 - type: ?<[string]> diff --git a/docs/src/test-reporter-api/class-testerror.md b/docs/src/test-reporter-api/class-testerror.md index 7df42482fa..e35e51c433 100644 --- a/docs/src/test-reporter-api/class-testerror.md +++ b/docs/src/test-reporter-api/class-testerror.md @@ -4,12 +4,41 @@ Information about an error thrown during test execution. +## property: TestError.actual +* since: v1.49 +- type: ?<[any]> + +Actual value. + +## property: TestError.expected +* since: v1.49 +- type: ?<[any]> + +Expected value. + +## property: TestError.log +* since: v1.49 +- type: ?<[Array]<[string]>> + +Call log. + ## property: TestError.message * since: v1.10 - type: ?<[string]> Error message. Set when [Error] (or its subclass) has been thrown. +## property: TestError.message +* since: v1.10 +- type: ?<[string]> + +Error message. Set when [Error] (or its subclass) has been thrown. +## property: TestError.shortMessage +* since: v1.49 +- type: ?<[string]> + +Failure message. + ## property: TestError.stack * since: v1.10 - type: ?<[string]> @@ -28,17 +57,6 @@ The value that was thrown. Set when anything except the [Error] (or its subclass Error location in the source code. -## property: TestError.matcherResult -* since: v1.49 -- type: ?<[Object]> - - `name` <[string]> - - `message` ?<[string]> Failure message - - `log` ?<[Array]<[string]>> Call log - - `expected` ?<[any]> - - `actual` ?<[any]> - -Expect matcher result. - ## property: TestError.snippet * since: v1.33 - type: ?<[string]> diff --git a/packages/html-reporter/src/testResultView.tsx b/packages/html-reporter/src/testResultView.tsx index bef5368811..525e1a484f 100644 --- a/packages/html-reporter/src/testResultView.tsx +++ b/packages/html-reporter/src/testResultView.tsx @@ -152,21 +152,23 @@ export const TestResultView: React.FC<{ function classifyErrors(testErrors: ErrorDetails[], diffs: ImageDiff[]) { return testErrors.map(error => { - if (error.matcherResult?.name === 'toHaveScreenshot' && error.matcherResult?.actual && error.matcherResult?.expected) { + if (error.shortMessage?.includes('Screenshot comparison failed:') && error.actual && error.expected) { const matchingDiff = diffs.find(diff => { const attachmentName = diff.actual?.attachment.name; - return attachmentName && error.matcherResult?.actual.endsWith(attachmentName); + return attachmentName && error.actual.endsWith(attachmentName); }); const errorSuffix = ['Call log:', - ...(error.matcherResult.log?.map(line => ' - ' + line) || []), + ...(error.log?.map(line => ' - ' + line) || []), '', error.snippet, + '', + error.callStack, ].join('\n'); if (matchingDiff) { return { type: 'screenshot', diff: matchingDiff, - errorPrefix: error.matcherResult?.message, + errorPrefix: error.shortMessage, errorSuffix }; } diff --git a/packages/html-reporter/src/types.ts b/packages/html-reporter/src/types.ts index dd3ce4f07d..ad06ba1b6e 100644 --- a/packages/html-reporter/src/types.ts +++ b/packages/html-reporter/src/types.ts @@ -83,19 +83,15 @@ export type TestCase = Omit & { results: TestResult[]; }; -type MatcherResult = { - name: string; - message?: string; - log?: string[]; - expected?: any; - actual?: any; -}; - export type ErrorDetails = { message: string; location?: Location; - matcherResult?: MatcherResult; + shortMessage?: string; + log?: string[]; + expected?: any; + actual?: any; snippet?: string; + callStack?: string; }; export type TestAttachment = { diff --git a/packages/playwright/src/reporters/base.ts b/packages/playwright/src/reporters/base.ts index 7e01d003ef..f27b803389 100644 --- a/packages/playwright/src/reporters/base.ts +++ b/packages/playwright/src/reporters/base.ts @@ -29,19 +29,20 @@ type Annotation = { location?: Location; }; -type MatcherResult = { - name: string; - message?: string; - log?: string[]; - expected?: any; - actual?: any; -}; - type ErrorDetails = { message: string; location?: Location; - matcherResult?: MatcherResult; + callStack?: string; +}; + +type TestResultErrorDetails = ErrorDetails & { + shortMessage?: string; + log?: string[]; + expected?: any; + actual?: any; + snippet?: string; + stack?: string; }; type TestSummary = { @@ -374,8 +375,8 @@ function quotePathIfNeeded(path: string): string { return path; } -export function formatResultFailure(test: TestCase, result: TestResult, initialIndent: string, highlightCode: boolean): ErrorDetails[] { - const errorDetails: ErrorDetails[] = []; +export function formatResultFailure(test: TestCase, result: TestResult, initialIndent: string, highlightCode: boolean): TestResultErrorDetails[] { + const errorDetails: TestResultErrorDetails[] = []; if (result.status === 'passed' && test.expectedStatus === 'failed') { errorDetails.push({ @@ -393,8 +394,12 @@ export function formatResultFailure(test: TestCase, result: TestResult, initialI errorDetails.push({ message: indent(formattedError.message, initialIndent), location: formattedError.location, - matcherResult: error.matcherResult, + shortMessage: error.shortMessage, + log: error.log, + expected: error.expected, + actual: error.actual, snippet: error.snippet, + callStack: formattedError.callStack, }); } return errorDetails; @@ -474,9 +479,12 @@ export function formatError(error: TestError, highlightCode: boolean): ErrorDeta tokens.push(snippet); } + let callStack; if (parsedStack && parsedStack.stackLines.length) { tokens.push(''); tokens.push(colors.dim(parsedStack.stackLines.join('\n'))); + // TODO: pass raw lines. + callStack = colors.dim(parsedStack.stackLines.join('\n')); } let location = error.location; @@ -486,6 +494,7 @@ export function formatError(error: TestError, highlightCode: boolean): ErrorDeta return { location, message: tokens.join('\n'), + callStack, }; } diff --git a/packages/playwright/src/reporters/json.ts b/packages/playwright/src/reporters/json.ts index e2b7ddf872..df66bf5d4b 100644 --- a/packages/playwright/src/reporters/json.ts +++ b/packages/playwright/src/reporters/json.ts @@ -222,7 +222,8 @@ class JSONReporter implements ReporterV2 { } private _serializeError(error: TestError): JSONReportError { - return formatError(error, true); + const { message, location } = formatError(error, true); + return { message, location }; } private _serializeTestStep(step: TestStep): JSONReportTestStep { diff --git a/packages/playwright/src/util.ts b/packages/playwright/src/util.ts index 4abdec02d1..3cc4024e71 100644 --- a/packages/playwright/src/util.ts +++ b/packages/playwright/src/util.ts @@ -30,26 +30,25 @@ import type { MatcherResult } from './matchers/matcherHint'; const PLAYWRIGHT_TEST_PATH = path.join(__dirname, '..'); const PLAYWRIGHT_CORE_PATH = path.dirname(require.resolve('playwright-core/package.json')); -export function filterStackTrace(e: Error): { message: string, stack: string, matcherResult: TestInfoError['matcherResult'] } { +export function filterStackTrace(e: Error): Omit { const name = e.name ? e.name + ': ' : ''; if (process.env.PWDEBUGIMPL) - return { message: name + e.message, stack: e.stack || '', matcherResult: filterMatcherResult(e) }; + return { message: name + e.message, stack: e.stack || '', ...filterExpectDetails(e) }; const stackLines = stringifyStackFrames(filteredStackTrace(e.stack?.split('\n') || [])); return { - matcherResult: filterMatcherResult(e), + ...filterExpectDetails(e), message: name + e.message, stack: `${name}${e.message}${stackLines.map(line => '\n' + line).join('')}` }; } -function filterMatcherResult(e: Error): TestInfoError['matcherResult'] | undefined { +function filterExpectDetails(e: Error): Pick { const matcherResult = (e as any).matcherResult as MatcherResult; if (!matcherResult) - return undefined; + return {}; return { - name: matcherResult.name, - message: matcherResult.shortMessage, + shortMessage: matcherResult.shortMessage, log: matcherResult.log, expected: matcherResult.expected, actual: matcherResult.actual, diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 112bdc72e2..d2d1490c1f 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -8380,31 +8380,30 @@ export interface TestInfo { */ export interface TestInfoError { /** - * Expect matcher result. + * Actual value. */ - matcherResult?: { - name: string; + actual?: any; - /** - * Failure message - */ - message?: string; + /** + * Expected value. + */ + expected?: any; - /** - * Call log - */ - log?: Array; - - expected?: any; - - actual?: any; - }; + /** + * Call log. + */ + log?: Array; /** * Error message. Set when [Error] (or its subclass) has been thrown. */ message?: string; + /** + * Failure message. + */ + shortMessage?: string; + /** * Error stack. Set when [Error] (or its subclass) has been thrown. */ diff --git a/packages/playwright/types/testReporter.d.ts b/packages/playwright/types/testReporter.d.ts index 5316ce3ff0..6ab3f7d77f 100644 --- a/packages/playwright/types/testReporter.d.ts +++ b/packages/playwright/types/testReporter.d.ts @@ -554,37 +554,36 @@ export interface TestCase { * Information about an error thrown during test execution. */ export interface TestError { + /** + * Actual value. + */ + actual?: any; + + /** + * Expected value. + */ + expected?: any; + /** * Error location in the source code. */ location?: Location; /** - * Expect matcher result. + * Call log. */ - matcherResult?: { - name: string; - - /** - * Failure message - */ - message?: string; - - /** - * Call log - */ - log?: Array; - - expected?: any; - - actual?: any; - }; + log?: Array; /** * Error message. Set when [Error] (or its subclass) has been thrown. */ message?: string; + /** + * Failure message. + */ + shortMessage?: string; + /** * Source code snippet with highlighted error. */