Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117import assert from "node:assert/strict";import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";import { tmpdir } from "node:os";import { join } from "node:path";import test from "node:test";import { PieceRevisions, validatePieceSource } from "../src/revisions.mjs";import { LivePiece } from "../src/live.mjs";
async function setup(t) { const root = await mkdtemp(join(tmpdir(), "easel-revision-")); t.after(() => rm(root, { recursive: true, force: true })); const file = join(root, "piece.mjs"); return { root, file, history: new PieceRevisions(file, { root: join(root, "history") }) };}
test("revisions survive restart, deduplicate saves, and rollback appends", async (t) => { const { root, file, history } = await setup(t); const first = "export function paint() {}\n"; const second = "export function paint({ wipe }) { wipe(0); }\n"; await writeFile(file, first); assert.equal(history.capture(first).version, 0); assert.equal(history.capture(first).version, 0); history.capture(second); await writeFile(file, second); const reopened = new PieceRevisions(file, { root: join(root, "history") }); const restored = await reopened.restore(0); assert.equal(restored.version, 2); assert.equal(restored.restoredFrom, 0); assert.equal(await readFile(file, "utf8"), first); assert.deepEqual(reopened.list().map((v) => v.source), [first, second, first]); await assert.rejects(reopened.restore(99), /No saved/);});
test("validation parses without executing and rejects incomplete JavaScript", async () => { await validatePieceSource('throw new Error("must not execute"); export const x = 1;', "piece.mjs"); await validatePieceSource('import missing from "./does-not-exist.mjs"; await missing(); export {missing};', "piece.mjs"); await assert.rejects(validatePieceSource("export function paint( {", "piece.mjs"), /invalid JavaScript/); await assert.rejects(validatePieceSource("export const x = 1; export const x = 2;", "piece.mjs"), /invalid JavaScript/);});
test("file watcher versions external edits and never pushes unfinished JavaScript", async (t) => { const { root, history } = await setup(t); let pushes = 0; const live = new LivePiece({ directory: root, slug: "piece", fetch: async () => { pushes++; return new Response("ok"); } }); Object.defineProperty(live, "history", { get: () => history }); live.create(); assert.equal(history.list()[0].version, 0, "blank is saved synchronously before any prompt or checkpoint"); assert.equal(history.list()[0].source, live.source()); await live.checkpoint(); t.after(() => live.unwatch()); const errors = []; live.watch((e) => errors.push(e)); await writeFile(live.file, "export function paint( {"); await new Promise((resolve) => setTimeout(resolve, 400)); assert.equal(pushes, 0); assert.equal(history.list().length, 1); assert.equal(errors.length, 1); const landed = new Promise((resolve) => live.once("push", resolve)); await writeFile(live.file, "export function paint() {}\n"); await landed; assert.equal(history.list().length, 2); await writeFile(live.file, "export function broken("); const restored = await live.rollback(0); assert.equal(restored.version, 2, "a broken current edit does not prevent recovery");});
test("live uploads serialize so old saves cannot overtake newer versions", async (t) => { const { root, history } = await setup(t); let releaseFirst; const gate = new Promise((resolve) => { releaseFirst = resolve; }); const seen = []; let firstStarted; const started = new Promise((resolve) => { firstStarted = resolve; }); const live = new LivePiece({ directory: root, slug: "piece", fetch: async (_url, options) => { seen.push(JSON.parse(options.body).source); if (seen.length === 1) { firstStarted(); await gate; } return new Response("ok"); } }); Object.defineProperty(live, "history", { get: () => history }); await writeFile(live.file, "// first\n"); const first = live.push(); await started; await writeFile(live.file, "// second\n"); const second = live.push(); assert.equal(seen.length, 1); assert.equal(live.sending, true); releaseFirst(); await Promise.all([first, second]); assert.deepEqual(seen, ["// first\n", "// second\n"]); assert.equal(live.ahead, false); assert.equal(live.sending, false);});
test("a failed old upload does not discard a queued newer save", async (t) => { const { root, history } = await setup(t); let failFirst, firstStarted; const started = new Promise((resolve) => { firstStarted = resolve; }); const blocked = new Promise((_resolve, reject) => { failFirst = reject; }); const seen = []; const live = new LivePiece({ directory: root, slug: "piece", fetch: async (_url, options) => { seen.push(JSON.parse(options.body).source); if (seen.length === 1) { firstStarted(); await blocked; } return new Response("ok"); } }); Object.defineProperty(live, "history", { get: () => history }); await writeFile(live.file, "// old\n"); const old = live.push(); const failure = assert.rejects(old, /offline/); await started; await writeFile(live.file, "// latest\n"); const latest = live.push(); failFirst(new Error("offline")); await failure; assert.equal(await latest, true); assert.deepEqual(seen, ["// old\n", "// latest\n"]);});