From 2de0a9300427f3e5a45c2aa8d690afb4f2fa38ac Mon Sep 17 00:00:00 2001 From: Dmitry Baev Date: Tue, 14 Jan 2025 11:06:01 +0000 Subject: [PATCH] add support for actual and expected values in errors (via #1215) --- packages/allure-js-commons/src/model.ts | 2 + packages/allure-js-commons/src/sdk/utils.ts | 8 +- .../allure-js-commons/test/sdk/utils.spec.ts | 49 ++++++++++++ .../allure-vitest/test/spec/expect.test.ts | 74 +++++++++++++++++++ 4 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 packages/allure-vitest/test/spec/expect.test.ts diff --git a/packages/allure-js-commons/src/model.ts b/packages/allure-js-commons/src/model.ts index 6a275368f..9327de9a3 100644 --- a/packages/allure-js-commons/src/model.ts +++ b/packages/allure-js-commons/src/model.ts @@ -35,6 +35,8 @@ export type ParameterOptions = Pick; export interface StatusDetails { message?: string; trace?: string; + actual?: string; + expected?: string; } // don't use the interface as is, use Results types instead diff --git a/packages/allure-js-commons/src/sdk/utils.ts b/packages/allure-js-commons/src/sdk/utils.ts index 755f17aa1..dfdf3a068 100644 --- a/packages/allure-js-commons/src/sdk/utils.ts +++ b/packages/allure-js-commons/src/sdk/utils.ts @@ -44,13 +44,15 @@ export const stripAnsi = (str: string): string => { return str.replace(regex, ""); }; -export const getMessageAndTraceFromError = ( - error: Error | { message?: string; stack?: string }, -): Pick => { +export const getMessageAndTraceFromError = (error: Error | { message?: string; stack?: string }): StatusDetails => { const { message, stack } = error; + const actual = "actual" in error && error.actual !== undefined ? { actual: serialize(error.actual) } : {}; + const expected = "expected" in error && error.expected !== undefined ? { expected: serialize(error.expected) } : {}; return { message: message ? stripAnsi(message) : undefined, trace: stack ? stripAnsi(stack) : undefined, + ...actual, + ...expected, }; }; diff --git a/packages/allure-js-commons/test/sdk/utils.spec.ts b/packages/allure-js-commons/test/sdk/utils.spec.ts index ba49ac5c5..b19d1715c 100644 --- a/packages/allure-js-commons/test/sdk/utils.spec.ts +++ b/packages/allure-js-commons/test/sdk/utils.spec.ts @@ -5,6 +5,7 @@ import type { FixtureResult, StepResult, TestResult } from "../../src/model.js"; import { allureLabelRegexp, extractMetadataFromString, + getMessageAndTraceFromError, getStatusFromError, isAnyStepFailed, isMetadataTag, @@ -505,3 +506,51 @@ describe("serialize", () => { }); }); }); + +describe("getMessageAndTraceFromError", () => { + it("should return message from error", () => { + const result = getMessageAndTraceFromError(new Error("some message")); + expect(result).toMatchObject({ + message: "some message", + }); + }); + + it("should return trace from error", () => { + const result = getMessageAndTraceFromError(new Error("some message")); + expect(result).toMatchObject({ + trace: expect.stringMatching(/allure-js-commons.test.sdk.utils\.spec\.ts/), + }); + }); + + it("should return actual from error", () => { + const error: Error & { actual?: string } = new Error("some message"); + error.actual = "some actual value"; + const result = getMessageAndTraceFromError(error); + expect(result).toMatchObject({ + actual: "some actual value", + }); + }); + + it("should ignore undefined actual value", () => { + const error: Error & { actual?: string } = new Error("some message"); + error.actual = undefined; + const result = getMessageAndTraceFromError(error); + expect(result).not.toHaveProperty("actual"); + }); + + it("should return expected from error", () => { + const error: Error & { expected?: string } = new Error("some message"); + error.expected = "some expected value"; + const result = getMessageAndTraceFromError(error); + expect(result).toMatchObject({ + expected: "some expected value", + }); + }); + + it("should ignore undefined expected value", () => { + const error: Error & { expected?: string } = new Error("some message"); + error.expected = undefined; + const result = getMessageAndTraceFromError(error); + expect(result).not.toHaveProperty("expected"); + }); +}); diff --git a/packages/allure-vitest/test/spec/expect.test.ts b/packages/allure-vitest/test/spec/expect.test.ts new file mode 100644 index 000000000..de3b17f38 --- /dev/null +++ b/packages/allure-vitest/test/spec/expect.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it } from "vitest"; +import { runVitestInlineTest } from "../utils.js"; + +describe("expect", () => { + it("should add actual and expected values when using expect", async () => { + const { tests } = await runVitestInlineTest(` + import { test, expect } from "vitest"; + + test("fail test", () => { + expect("a value").toEqual("the other one"); + }); + + `); + + expect(tests).toHaveLength(1); + expect(tests).toMatchObject([ + { + name: "fail test", + status: "failed", + statusDetails: { + message: "expected 'a value' to deeply equal 'the other one'", + expected: "the other one", + actual: "a value", + }, + }, + ]); + }); + it("should add actual and expected values when using expect with match object", async () => { + const { tests } = await runVitestInlineTest(` + import { test, expect } from "vitest"; + + test("fail test", () => { + expect({ nested: { obj: "value", n: 123}}).toMatchObject({ nested: { obj: "some"} }); + }); + + `); + + expect(tests).toHaveLength(1); + expect(tests).toMatchObject([ + { + name: "fail test", + status: "failed", + statusDetails: { + expected: "Object {\n" + ' "nested": Object {\n' + ' "obj": "some",\n' + " },\n" + "}", + actual: "Object {\n" + ' "nested": Object {\n' + ' "obj": "value",\n' + " },\n" + "}", + }, + }, + ]); + }); + + // this is the way vitest process errors + it("should add actual and expected values when regular exception is thrown", async () => { + const { tests } = await runVitestInlineTest(` + import { test, expect } from "vitest"; + + test("fail test", () => { + throw new Error("fail!") + }); + + `); + + expect(tests).toHaveLength(1); + expect(tests).toMatchObject([ + { + name: "fail test", + status: "broken", + statusDetails: { + expected: "undefined", + actual: "undefined", + }, + }, + ]); + }); +});