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
1
{await delay(items[0])}
+ +{#if await delay(show)} +