diff --git a/packages/playwright/src/common/ipc.ts b/packages/playwright/src/common/ipc.ts index 9fd819c6bc..806aed0532 100644 --- a/packages/playwright/src/common/ipc.ts +++ b/packages/playwright/src/common/ipc.ts @@ -20,7 +20,6 @@ import { serializeCompilationCache } from '../transform/compilationCache'; import type { ConfigLocation, FullConfigInternal } from './config'; import type { ReporterDescription, TestInfoError, TestStatus } from '../../types/test'; -import type { MatcherResultProperty } from '../matchers/matcherHint'; import type { SerializedCompilationCache } from '../transform/compilationCache'; export type ConfigCLIOverrides = { @@ -83,9 +82,7 @@ export type AttachmentPayload = { stepId?: string; }; -export type TestInfoErrorImpl = TestInfoError & { - matcherResult?: MatcherResultProperty; -}; +export type TestInfoErrorImpl = TestInfoError; export type TestEndPayload = { testId: string; diff --git a/packages/playwright/src/common/process.ts b/packages/playwright/src/common/process.ts index bfe2e9c533..b29f2d6926 100644 --- a/packages/playwright/src/common/process.ts +++ b/packages/playwright/src/common/process.ts @@ -119,6 +119,13 @@ function sendMessageToParent(message: { method: string, params?: any }) { try { process.send!(message); } catch (e) { + try { + // By default, the IPC messages are serialized as JSON. + JSON.stringify(message); + } catch { + // Always throw serialization errors. + throw e; + } // Can throw when closing. } } diff --git a/packages/playwright/src/worker/util.ts b/packages/playwright/src/worker/util.ts index 1f8cbc7d48..a999050ee9 100644 --- a/packages/playwright/src/worker/util.ts +++ b/packages/playwright/src/worker/util.ts @@ -14,14 +14,10 @@ * limitations under the License. */ -import { ExpectError } from '../matchers/matcherHint'; import { serializeError } from '../util'; import type { TestInfoErrorImpl } from '../common/ipc'; export function testInfoError(error: Error | any): TestInfoErrorImpl { - const result = serializeError(error); - if (error instanceof ExpectError) - result.matcherResult = error.matcherResult; - return result; + return serializeError(error); } diff --git a/tests/playwright-test/basic.spec.ts b/tests/playwright-test/basic.spec.ts index ce6825c559..73ce104ba0 100644 --- a/tests/playwright-test/basic.spec.ts +++ b/tests/playwright-test/basic.spec.ts @@ -617,3 +617,48 @@ test('should fail when test.fail.only passes unexpectedly', async ({ runInlineTe expect(result.output).not.toContain('test1 should not run'); expect(result.output).not.toContain('test3 should not run'); }); + +test('should serialize circular objects', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'one-failure.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('circular dependency', () => { + const a = {}; + a.b = a; + expect(1).toEqual(a); + }); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.passed).toBe(0); + expect(result.failed).toBe(1); + expect(result.output).toContain('Expected: {"b": [Circular]}'); +}); + +test('should serialize BigInt', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'one-failure.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('BigInt', () => { + expect(1).toEqual(1n); + }); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.failed).toBe(1); + expect(result.output).toContain('Expected: 1n'); +}); + +test('should report serialization error', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'one-failure.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('function', () => { + expect(1).toEqual({ a: () => {} }); + }); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.passed).toBe(0); + expect(result.output).toContain('Expected: {\"a\": [Function a]}'); +});