recording.test.ts 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. import { expect, test } from "bun:test"
  2. import { mkdtemp, rm } from "node:fs/promises"
  3. import { tmpdir } from "node:os"
  4. import { join } from "node:path"
  5. import { SimulationRenderer } from "../src/frontend/renderer"
  6. import { Timeline, type Event } from "../src/recording"
  7. test("streams ANSI chunks into a versioned JSONL timeline", async () => {
  8. const directory = await mkdtemp(join(tmpdir(), "simulation-recording-"))
  9. const path = join(directory, "nested", "timeline.jsonl")
  10. try {
  11. const timeline = await Timeline.create(path, 80, 24)
  12. await new Promise<void>((resolve, reject) => {
  13. timeline.write(Buffer.from("\u001b[2Jhello"), (error) => (error ? reject(error) : resolve()))
  14. })
  15. expect(await timeline.finish()).toBe(path)
  16. await new Promise<void>((resolve) => timeline.write(Buffer.from("ignored"), () => resolve()))
  17. const events = (await Bun.file(path).text())
  18. .trim()
  19. .split("\n")
  20. .map((line) => JSON.parse(line) as Event)
  21. expect(events[0]).toEqual({ type: "header", version: 1, cols: 80, rows: 24, encoding: "base64" })
  22. expect(events[1]?.type).toBe("output")
  23. if (events[1]?.type !== "output") throw new Error("Missing output event")
  24. expect(Buffer.from(events[1].data, "base64").toString()).toBe("\u001b[2Jhello")
  25. expect(events[1].at_ms).toBeGreaterThanOrEqual(0)
  26. expect(events.at(-1)).toMatchObject({ type: "output", data: "" })
  27. } finally {
  28. await rm(directory, { recursive: true, force: true })
  29. }
  30. })
  31. test("captures native renderer output and finishes on destroy", async () => {
  32. const directory = await mkdtemp(join(tmpdir(), "simulation-renderer-recording-"))
  33. const path = join(directory, "timeline.jsonl")
  34. const renderer = await SimulationRenderer.create({}, path)
  35. try {
  36. await SimulationRenderer.setupFor(renderer)?.renderOnce()
  37. renderer.destroy()
  38. expect(await SimulationRenderer.finish(renderer)).toBe(path)
  39. const events = (await Bun.file(path).text())
  40. .trim()
  41. .split("\n")
  42. .map((line) => JSON.parse(line) as Event)
  43. expect(events.some((event) => event.type === "output")).toBe(true)
  44. } finally {
  45. if (!renderer.isDestroyed) renderer.destroy()
  46. await SimulationRenderer.finish(renderer)
  47. await rm(directory, { recursive: true, force: true })
  48. }
  49. })