diff --git a/packages/bun-types/test.d.ts b/packages/bun-types/test.d.ts index 9550d16ece..a6a8ad11ff 100644 --- a/packages/bun-types/test.d.ts +++ b/packages/bun-types/test.d.ts @@ -697,6 +697,40 @@ declare module "bun:test" { * Ensures that a specific number of assertions are made */ assertions(neededAssertions: number): void; + + /** + * Add a custom snapshot serializer to customize how values are formatted in snapshots. + * + * @example + * class Point { + * constructor(public x: number, public y: number) {} + * } + * + * expect.addSnapshotSerializer({ + * test: (val) => val instanceof Point, + * serialize: (val) => `Point(${val.x}, ${val.y})`, + * }); + * + * expect(new Point(1, 2)).toMatchInlineSnapshot(`Point(1, 2)`); + * + * @param serializer The snapshot serializer configuration + */ + addSnapshotSerializer(serializer: { + /** + * Test function to determine if this serializer should be used for a value + */ + test: (val: any) => boolean; + /** + * Serialize function to convert the value to a string. + * Either `serialize` or `print` must be provided. + */ + serialize?: (val: any) => string; + /** + * Print function to convert the value to a string (alternative to serialize). + * Either `serialize` or `print` must be provided. + */ + print?: (val: any) => string; + }): void; } /** diff --git a/test/js/bun/test/snapshot-serializers.test.ts b/test/js/bun/test/snapshot-serializers.test.ts new file mode 100644 index 0000000000..5226daa0c8 --- /dev/null +++ b/test/js/bun/test/snapshot-serializers.test.ts @@ -0,0 +1,70 @@ +import { expect, test } from "bun:test"; + +class Point { + constructor( + public x: number, + public y: number, + ) {} +} + +class Color { + constructor(public name: string) {} +} + +class Size { + constructor( + public width: number, + public height: number, + ) {} +} + +// Add serializers at the top level +expect.addSnapshotSerializer({ + test: val => val instanceof Point, + serialize: val => `Point(${val.x}, ${val.y})`, +}); + +expect.addSnapshotSerializer({ + test: val => val instanceof Color, + serialize: val => `Color[${val.name}]`, +}); + +// Add a second Point serializer to test that most recent wins +expect.addSnapshotSerializer({ + test: val => val instanceof Point, + serialize: val => `OVERRIDE: Point(${val.x}, ${val.y})`, +}); + +expect.addSnapshotSerializer({ + test: val => val instanceof Size, + print: val => `Size{${val.width}x${val.height}}`, +}); + +test("snapshot serializers work for custom formatting", () => { + const color = new Color("red"); + expect(color).toMatchInlineSnapshot(`Color[red]`); +}); + +test("most recently added serializer is used when multiple match", () => { + // The second Point serializer should be used (most recent wins) + const point = new Point(10, 20); + expect(point).toMatchInlineSnapshot(`OVERRIDE: Point(10, 20)`); +}); + +test("snapshot serializer with 'print' instead of 'serialize'", () => { + const size = new Size(100, 200); + expect(size).toMatchInlineSnapshot(`Size{100x200}`); +}); + +test("snapshot serializers apply to object fields", () => { + const obj = { + color: new Color("blue"), + size: new Size(640, 480), + }; + expect(obj).toMatchInlineSnapshot(` + { + "color": Color[blue], + "size": Size{640x480}, + } + `); +});