From 0d51d2512801c9bf4983a9a4457e1bbc02da5a02 Mon Sep 17 00:00:00 2001 From: ugin <157204743+ugin-man@users.noreply.github.com> Date: Wed, 9 Sep 2026 02:05:56 +0900 Subject: [PATCH 1/3] test: cover arrows in combined pipeline visualizations --- tests/pipeline-arrows.test.ts | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/pipeline-arrows.test.ts diff --git a/tests/pipeline-arrows.test.ts b/tests/pipeline-arrows.test.ts new file mode 100644 index 0000000..1c0d15f --- /dev/null +++ b/tests/pipeline-arrows.test.ts @@ -0,0 +1,91 @@ +import { expect, test } from "bun:test" +import type { GraphicsObject } from "graphics-debug" +import { BasePipelineSolver } from "../lib/BasePipelineSolver" +import { BaseSolver } from "../lib/BaseSolver" + +const arrowGraphics = (label: string): GraphicsObject => ({ + arrows: [ + { + start: { x: 1, y: 2 }, + end: { x: 3, y: 4 }, + label, + color: "red", + doubleSided: true, + }, + ], + points: [{ x: 5, y: 6, label }], +}) + +class ArrowSolver extends BaseSolver { + label: string + + constructor(label: string) { + super() + this.label = label + } + + override _step() { + this.solved = true + } + + override visualize(): GraphicsObject { + return arrowGraphics(this.label) + } +} + +class ArrowPipeline extends BasePipelineSolver { + pipelineDef = this.inputProblem.map((solverName) => ({ + solverName, + solverClass: ArrowSolver, + getConstructorParams: () => [solverName], + })) +} + +test("combining completed stages preserves arrows and their stage metadata", () => { + const pipeline = new ArrowPipeline(["first", "second"]) + pipeline.solve() + + const graphics = pipeline.visualize() + expect(graphics.arrows).toEqual([ + { ...arrowGraphics("first").arrows![0], step: 0 }, + { ...arrowGraphics("second").arrows![0], step: 1 }, + ]) + expect(graphics.points?.map((point) => point.label)).toEqual([ + "first", + "second", + ]) +}) + +test("combining initial, stage and final visualizations preserves every arrow", () => { + class HookPipeline extends ArrowPipeline { + override initialVisualize(): GraphicsObject { + return arrowGraphics("initial") + } + + override finalVisualize(): GraphicsObject { + return arrowGraphics("final") + } + } + + const pipeline = new HookPipeline(["first"]) + pipeline.solve() + expect(pipeline.visualize().arrows?.map((arrow) => arrow.label)).toEqual([ + "initial", + "first", + "final", + ]) +}) + +test("an active stage still exposes its arrows directly", () => { + const pipeline = new ArrowPipeline(["first", "second"]) + pipeline.step() + expect(pipeline.visualize().arrows).toEqual(arrowGraphics("first").arrows) +}) + +test("a single completed stage still exposes its arrows", () => { + const pipeline = new ArrowPipeline(["first"]) + pipeline.solve() + expect(pipeline.visualize().arrows).toEqual([ + { ...arrowGraphics("first").arrows![0], step: 0 }, + ]) +}) From 103359f7f3419b1008030db0107b09c3901399a2 Mon Sep 17 00:00:00 2001 From: ugin <157204743+ugin-man@users.noreply.github.com> Date: Wed, 9 Sep 2026 02:08:23 +0900 Subject: [PATCH 2/3] fix: preserve arrows when merging pipeline visualizations --- lib/BasePipelineSolver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/BasePipelineSolver.ts b/lib/BasePipelineSolver.ts index 83219bd..7d5cf06 100644 --- a/lib/BasePipelineSolver.ts +++ b/lib/BasePipelineSolver.ts @@ -206,6 +206,7 @@ export abstract class BasePipelineSolver extends BaseSolver { lines: visualizations.flatMap((v) => v.lines || []), circles: visualizations.flatMap((v) => v.circles || []), texts: visualizations.flatMap((v) => v.texts || []), + arrows: visualizations.flatMap((v) => v.arrows || []), } } From 144e572c04db638d840cee425a74926dc88eb267 Mon Sep 17 00:00:00 2001 From: ugin <157204743+ugin-man@users.noreply.github.com> Date: Wed, 9 Sep 2026 02:10:23 +0900 Subject: [PATCH 3/3] test: use arrow fields supported by graphics-debug 0.0.76 --- tests/pipeline-arrows.test.ts | 75 +++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/tests/pipeline-arrows.test.ts b/tests/pipeline-arrows.test.ts index 1c0d15f..dc921f3 100644 --- a/tests/pipeline-arrows.test.ts +++ b/tests/pipeline-arrows.test.ts @@ -3,25 +3,33 @@ import type { GraphicsObject } from "graphics-debug" import { BasePipelineSolver } from "../lib/BasePipelineSolver" import { BaseSolver } from "../lib/BaseSolver" -const arrowGraphics = (label: string): GraphicsObject => ({ +const arrowGraphics = (x: number): GraphicsObject => ({ arrows: [ { - start: { x: 1, y: 2 }, - end: { x: 3, y: 4 }, - label, + start: { x, y: 2 }, + end: { x: x + 1, y: 4 }, color: "red", doubleSided: true, }, ], - points: [{ x: 5, y: 6, label }], + points: [{ x, y: 6, label: String(x) }], }) +// Arrow labels and step metadata are not part of graphics-debug 0.0.76. +const arrowShapes = (graphics: GraphicsObject) => + graphics.arrows?.map(({ start, end, color, doubleSided }) => ({ + start, + end, + color, + doubleSided, + })) + class ArrowSolver extends BaseSolver { - label: string + x: number - constructor(label: string) { + constructor(x: number) { super() - this.label = label + this.x = x } override _step() { @@ -29,63 +37,60 @@ class ArrowSolver extends BaseSolver { } override visualize(): GraphicsObject { - return arrowGraphics(this.label) + return arrowGraphics(this.x) } } -class ArrowPipeline extends BasePipelineSolver { - pipelineDef = this.inputProblem.map((solverName) => ({ - solverName, +class ArrowPipeline extends BasePipelineSolver { + pipelineDef = this.inputProblem.map((x) => ({ + solverName: `stage${x}`, solverClass: ArrowSolver, - getConstructorParams: () => [solverName], + getConstructorParams: () => [x], })) } -test("combining completed stages preserves arrows and their stage metadata", () => { - const pipeline = new ArrowPipeline(["first", "second"]) +test("combining completed stages preserves arrow geometry and styles", () => { + const pipeline = new ArrowPipeline([1, 2]) pipeline.solve() const graphics = pipeline.visualize() - expect(graphics.arrows).toEqual([ - { ...arrowGraphics("first").arrows![0], step: 0 }, - { ...arrowGraphics("second").arrows![0], step: 1 }, - ]) - expect(graphics.points?.map((point) => point.label)).toEqual([ - "first", - "second", + expect(arrowShapes(graphics)).toEqual([ + ...arrowShapes(arrowGraphics(1))!, + ...arrowShapes(arrowGraphics(2))!, ]) + expect(graphics.points?.map((point) => point.step)).toEqual([0, 1]) }) test("combining initial, stage and final visualizations preserves every arrow", () => { class HookPipeline extends ArrowPipeline { override initialVisualize(): GraphicsObject { - return arrowGraphics("initial") + return arrowGraphics(0) } override finalVisualize(): GraphicsObject { - return arrowGraphics("final") + return arrowGraphics(3) } } - const pipeline = new HookPipeline(["first"]) + const pipeline = new HookPipeline([1]) pipeline.solve() - expect(pipeline.visualize().arrows?.map((arrow) => arrow.label)).toEqual([ - "initial", - "first", - "final", + expect(pipeline.visualize().arrows?.map((arrow) => arrow.start.x)).toEqual([ + 0, 1, 3, ]) }) test("an active stage still exposes its arrows directly", () => { - const pipeline = new ArrowPipeline(["first", "second"]) + const pipeline = new ArrowPipeline([1, 2]) pipeline.step() - expect(pipeline.visualize().arrows).toEqual(arrowGraphics("first").arrows) + expect(arrowShapes(pipeline.visualize())).toEqual( + arrowShapes(arrowGraphics(1)), + ) }) test("a single completed stage still exposes its arrows", () => { - const pipeline = new ArrowPipeline(["first"]) + const pipeline = new ArrowPipeline([1]) pipeline.solve() - expect(pipeline.visualize().arrows).toEqual([ - { ...arrowGraphics("first").arrows![0], step: 0 }, - ]) + expect(arrowShapes(pipeline.visualize())).toEqual( + arrowShapes(arrowGraphics(1)), + ) })