From 86eb09b76ef9c81e8b34adf7eaaf63974a66aaa9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 13 Oct 2026 13:40:24 -0400 Subject: [PATCH] chore: even more async tests (#18990) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extracting some tests from #18983 — these pass on `main`, `pt-1`, `pt-2` and `pt-3`. The others in that PR don't pass on `main`, so will be dealt with separately --- .../_config.js | 33 ++++++++++++++++ .../main.svelte | 25 ++++++++++++ .../_config.js | 33 ++++++++++++++++ .../main.svelte | 32 +++++++++++++++ .../async-fork-commit-new-reader/_config.js | 35 +++++++++++++++++ .../async-fork-commit-new-reader/main.svelte | 25 ++++++++++++ .../async-stale-read-many-sources/_config.js | 39 +++++++++++++++++++ .../async-stale-read-many-sources/main.svelte | 23 +++++++++++ 8 files changed, 245 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-equal-real-value-hidden/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-equal-real-value-hidden/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-write-keeps-batch-derived/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-write-keeps-batch-derived/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-fork-commit-new-reader/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-fork-commit-new-reader/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-stale-read-many-sources/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-stale-read-many-sources/main.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-equal-real-value-hidden/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-equal-real-value-hidden/_config.js new file mode 100644 index 0000000000..d319831f2e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-equal-real-value-hidden/_config.js @@ -0,0 +1,33 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +const buttons = + ''; + +export default test({ + mode: ['client'], + async test({ assert, target }) { + await tick(); + const [x, s, resolve_a, resolve_b] = target.querySelectorAll('button'); + assert.htmlEqual(target.innerHTML, `${buttons}

0:5

5

`); + + // batch A: `t` becomes truthy (pending) + x.click(); + await tick(); + + // batch B: `d` becomes 0 (pending) + s.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

0:5

5

`); + + // In A's world, `d` is recomputed to 0. That's the real value, but not the value + // A sees for it (the one from before B's change), so A has to record it + resolve_a.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

1:0

5

`); + + resolve_b.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

1:0

0

`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-equal-real-value-hidden/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-equal-real-value-hidden/main.svelte new file mode 100644 index 0000000000..9fe34a5e12 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-equal-real-value-hidden/main.svelte @@ -0,0 +1,25 @@ + + + + + + + +{#if d >= 0} +

{t}:{d}

+{/if} + +

{await delay('b', s, 5)}

diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-write-keeps-batch-derived/_config.js b/packages/svelte/tests/runtime-runes/samples/async-effect-write-keeps-batch-derived/_config.js new file mode 100644 index 0000000000..fb0b6e8a4f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-write-keeps-batch-derived/_config.js @@ -0,0 +1,33 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +const buttons = ''; + +// While batch A (x) commits, a pre effect writes to `s`. That marks `label` (via `big`) as +// maybe dirty, but it doesn't actually change. `label` must still show the value from A's +// point of view, not the one from the later, still-pending batch B (t). +export default test({ + async test({ assert, target }) { + await tick(); + const [x, t, shift] = target.querySelectorAll('button'); + + // A + x.click(); + await tick(); + + // B + t.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

0

0

0 false-0

`); + + // A commits, B is still pending + shift.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

1

0

1 false-0

`); + + // B commits + shift.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

1

1

1 false-1

`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-write-keeps-batch-derived/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-effect-write-keeps-batch-derived/main.svelte new file mode 100644 index 0000000000..1791827eae --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-write-keeps-batch-derived/main.svelte @@ -0,0 +1,32 @@ + + + + + + +

{await push(x)}

+

{await push(t)}

+ +{#key label}{/key} + +

{x} {label}

diff --git a/packages/svelte/tests/runtime-runes/samples/async-fork-commit-new-reader/_config.js b/packages/svelte/tests/runtime-runes/samples/async-fork-commit-new-reader/_config.js new file mode 100644 index 0000000000..a9e860bec7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-fork-commit-new-reader/_config.js @@ -0,0 +1,35 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +const buttons = + ''; + +// The template effect is deferred by the fork (it depends on `s` via `big`), but before the +// fork is committed, it re-runs in the real world and starts reading `s` directly. +// Committing the fork (while another batch is pending) must still update it. +export default test({ + async test({ assert, target }) { + await tick(); + const [u, f, cond, commit, shift] = target.querySelectorAll('button'); + + // unrelated pending batch + u.click(); + await tick(); + + f.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

0

false

`); + + cond.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

0

0

`); + + commit.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

0

1

`); + + shift.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

1

1

`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-fork-commit-new-reader/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-fork-commit-new-reader/main.svelte new file mode 100644 index 0000000000..a52d6b4dfd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-fork-commit-new-reader/main.svelte @@ -0,0 +1,25 @@ + + + + + + + + +

{await push(u)}

+

{cond ? s : big}

diff --git a/packages/svelte/tests/runtime-runes/samples/async-stale-read-many-sources/_config.js b/packages/svelte/tests/runtime-runes/samples/async-stale-read-many-sources/_config.js new file mode 100644 index 0000000000..f5318b930a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-stale-read-many-sources/_config.js @@ -0,0 +1,39 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +const buttons = ''; +/** @param {number} offset */ +const list = (offset) => Array.from({ length: 2000 }, (_, i) => `${i + offset},`).join(''); + +// When the branch is created in B1, the each block reads all 2000 items, each of which B2 holds +// a newer value of. The each block must be scheduled to re-run in B2 once, not once per stale +// value — otherwise B2 is flushed 2000 times (each flush being O(size of B2)), which takes seconds +export default test({ + mode: ['client'], + async test({ assert, target }) { + await tick(); + const [show, items, shift] = target.querySelectorAll('button'); + + // B1: show = true -> pending + show.click(); + await tick(); + + // B2: increment all items -> pending + items.click(); + await tick(); + + // B1 resolves -> the branch is created, reading 2000 values that are stale relative to B2 + const start = performance.now(); + shift.click(); + await tick(); + const duration = performance.now() - start; + + assert.ok(duration < 1000, `took ${duration}ms`); + assert.htmlEqual(target.innerHTML, `${buttons}

0

${list(0)}
`); + + // B2 resolves + shift.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `${buttons}

1

${list(1)}
`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-stale-read-many-sources/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-stale-read-many-sources/main.svelte new file mode 100644 index 0000000000..f932241cc6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-stale-read-many-sources/main.svelte @@ -0,0 +1,23 @@ + + + + + + +

{await delay(items[0])}

+ +{#if await delay(show)} +
+ {#each items as item}{item},{/each} +
+{/if}