From 83715d80f120fe0eb0454cb877ba6be8123fde4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Sat, 4 Jul 2026 17:04:00 +0200 Subject: [PATCH 1/2] add tests --- test/variable/dispose-test.js | 100 ++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 test/variable/dispose-test.js diff --git a/test/variable/dispose-test.js b/test/variable/dispose-test.js new file mode 100644 index 0000000..79667ab --- /dev/null +++ b/test/variable/dispose-test.js @@ -0,0 +1,100 @@ +import { Runtime } from "@observablehq/runtime"; +import assert from "assert"; +import { delay, sleep } from "./valueof.js"; + +describe("variable.dispose", () => { + it("prevents a subsequent delete from notifying the observer", async () => { + // https://github.com/observablehq/notebook-kit/issues/174 + const runtime = new Runtime(); + const main = runtime.module(); + const log = []; + const foo = main + .variable({ fulfilled: (value) => log.push(`a-${value}`) }) + .define([], () => 1); + await sleep(); + foo.dispose(); + foo.delete(); + main + .variable({ fulfilled: (value) => log.push(`b-${value}`) }) + .define([], () => 2); + await sleep(); + assert.deepStrictEqual(log, ["a-1", "b-2"]); // not followed by "a-undefined" + }); + it("prevents future computation", async () => { + const runtime = new Runtime(); + const main = runtime.module(); + const log = []; + const foo = main + .variable({ fulfilled: (value) => log.push(value) }) + .define([], () => 1); + await sleep(); + foo.dispose(); + foo.define([], () => 2); + await sleep(); + assert.deepStrictEqual(log, [1]); + }); + it("cancels an in-flight computation", async () => { + const runtime = new Runtime(); + const main = runtime.module(); + const log = []; + const foo = main + .variable({ fulfilled: (value) => log.push(value) }) + .define([], () => delay(1, 100)); + await sleep(); + foo.dispose(); + await sleep(200); + assert.deepStrictEqual(log, []); + }); + it("invalidates the variable", async () => { + const runtime = new Runtime(); + const main = runtime.module(); + const log = []; + const foo = main + .variable(true) + .define(["invalidation"], async (invalidation) => { + await invalidation; + log.push("invalidation"); + }); + await sleep(); + foo.dispose(); + await sleep(); + assert.deepStrictEqual(log, ["invalidation"]); + }); + it("terminates generators", async () => { + const runtime = new Runtime(); + const main = runtime.module(); + const log = []; + const foo = main.variable(true).define([], function* () { + try { + while (true) yield; + } finally { + log.push("return"); + } + }); + await sleep(); + foo.dispose(); + await sleep(); + assert.deepStrictEqual(log, ["return"]); + }); + it("terminates async generators", async () => { + const runtime = new Runtime(); + const main = runtime.module(); + const log = []; + let when = "before"; + const foo = main.variable(true).define([], async function* () { + try { + while (true) { + await sleep(20); + yield log.push(when); + } + } finally { + log.push("return"); + } + }); + await sleep(50); // 50ms gives time to log two values + foo.dispose(); + when = "after"; + await sleep(50); // logs one last value and then terminates + assert.deepStrictEqual(log, ["before", "before", "after", "return"]); + }); +}); From 66f0bd3a483ca1a95d3c4531d1c3abbd56691ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Sat, 4 Jul 2026 22:45:10 +0200 Subject: [PATCH 2/2] prettier; deflake async generator test --- test/variable/dispose-test.js | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/test/variable/dispose-test.js b/test/variable/dispose-test.js index 79667ab..d2addd3 100644 --- a/test/variable/dispose-test.js +++ b/test/variable/dispose-test.js @@ -1,6 +1,6 @@ -import { Runtime } from "@observablehq/runtime"; +import {Runtime} from "@observablehq/runtime"; import assert from "assert"; -import { delay, sleep } from "./valueof.js"; +import {delay, sleep} from "./valueof.js"; describe("variable.dispose", () => { it("prevents a subsequent delete from notifying the observer", async () => { @@ -9,13 +9,13 @@ describe("variable.dispose", () => { const main = runtime.module(); const log = []; const foo = main - .variable({ fulfilled: (value) => log.push(`a-${value}`) }) + .variable({fulfilled: (value) => log.push(`a-${value}`)}) .define([], () => 1); await sleep(); foo.dispose(); foo.delete(); main - .variable({ fulfilled: (value) => log.push(`b-${value}`) }) + .variable({fulfilled: (value) => log.push(`b-${value}`)}) .define([], () => 2); await sleep(); assert.deepStrictEqual(log, ["a-1", "b-2"]); // not followed by "a-undefined" @@ -25,7 +25,7 @@ describe("variable.dispose", () => { const main = runtime.module(); const log = []; const foo = main - .variable({ fulfilled: (value) => log.push(value) }) + .variable({fulfilled: (value) => log.push(value)}) .define([], () => 1); await sleep(); foo.dispose(); @@ -38,7 +38,7 @@ describe("variable.dispose", () => { const main = runtime.module(); const log = []; const foo = main - .variable({ fulfilled: (value) => log.push(value) }) + .variable({fulfilled: (value) => log.push(value)}) .define([], () => delay(1, 100)); await sleep(); foo.dispose(); @@ -80,21 +80,19 @@ describe("variable.dispose", () => { const runtime = new Runtime(); const main = runtime.module(); const log = []; - let when = "before"; const foo = main.variable(true).define([], async function* () { try { while (true) { - await sleep(20); - yield log.push(when); + await sleep(); + yield; } } finally { log.push("return"); } }); - await sleep(50); // 50ms gives time to log two values + await sleep(); foo.dispose(); - when = "after"; - await sleep(50); // logs one last value and then terminates - assert.deepStrictEqual(log, ["before", "before", "after", "return"]); + await sleep(); + assert.deepStrictEqual(log, [ "return"]); }); });