From 90dc9fc329f61404dc3b5307d0e7b50b00c051bf Mon Sep 17 00:00:00 2001 From: Nic <162764842+Nic-Polumeyv@users.noreply.github.com> Date: Mon, 12 Oct 2026 23:58:41 -0400 Subject: [PATCH 1/2] test: cover branch commit order and merged blocking pending --- .../async-branch-commit-order/_config.js | 31 +++++++++++++++ .../async-branch-commit-order/main.svelte | 30 +++++++++++++++ .../async-merge-blocking-pending/_config.js | 38 +++++++++++++++++++ .../async-merge-blocking-pending/main.svelte | 38 +++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/main.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/_config.js b/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/_config.js new file mode 100644 index 0000000000..a006f88c29 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/_config.js @@ -0,0 +1,31 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +const buttons = + ''; + +export default test({ + async test({ assert, target }) { + await tick(); + const [a, b, resolve_a1, resolve_b1] = target.querySelectorAll('button'); + + assert.htmlEqual(target.innerHTML, `
0
0
${buttons}`); + + // batch A waits on its key; batch B registers its key right away + a.click(); + await tick(); + b.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `0
0
${buttons}`); + + // the older batch commits first + resolve_a1.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `0
1
${buttons}`); + + // the newer batch commits with its own branch + resolve_b1.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `1
B
${buttons}`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/main.svelte new file mode 100644 index 0000000000..c232f535a9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/main.svelte @@ -0,0 +1,30 @@ + + +{await delay(`b${b}`, b)}
+ +{#key k} +{k}
+{/key} + + + + + diff --git a/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/_config.js b/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/_config.js new file mode 100644 index 0000000000..be95f3a159 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/_config.js @@ -0,0 +1,38 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +const buttons = + ''; + +export default test({ + async test({ assert, target, logs }) { + await tick(); + const [X, Y, Z, W, resolve_e, resolve_x1] = target.querySelectorAll('button'); + + assert.htmlEqual(target.innerHTML, `n = 0
x = 0
${buttons}`); + + // X is deferred on its own await + X.click(); + await tick(); + // Y is deferred on the await inside the new branch + Y.click(); + await tick(); + // Z hides that branch again, merges into Y, which merges into X + Z.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `n = 0
x = 0
${buttons}`); + assert.deepEqual(logs, ['delay x0', 'delay x1', 'delay e']); + + // the branch's await resolves while it is hidden + resolve_e.click(); + await tick(); + // W shows the branch again and merges into X + W.click(); + await tick(); + + // nothing is pending any more, so X commits + resolve_x1.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `n = 4
x = 1
cond: e
${buttons}`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/main.svelte new file mode 100644 index 0000000000..d4d03aa6eb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/main.svelte @@ -0,0 +1,38 @@ + + +n = {n}
+x = {await delay(`x${x}`, x)}
+ {#if cond} +cond: {await delay('e', 'e')}
+ {/if} + + {#snippet pending()} +loading
+ {/snippet} +0
0
${buttons}`); + const [a, b, shift] = target.querySelectorAll('button'); // batch A waits on its key; batch B registers its key right away a.click(); await tick(); b.click(); await tick(); - assert.htmlEqual(target.innerHTML, `0
0
${buttons}`); // the older batch commits first - resolve_a1.click(); + shift.click(); await tick(); assert.htmlEqual(target.innerHTML, `0
1
${buttons}`); // the newer batch commits with its own branch - resolve_b1.click(); + shift.click(); await tick(); assert.htmlEqual(target.innerHTML, `1
B
${buttons}`); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/main.svelte index c232f535a9..d0b1a57053 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/async-branch-commit-order/main.svelte @@ -3,22 +3,17 @@ let b = $state(0); let pend = false; - const pending = new Map(); + const deferred = []; - function delay(key, value) { + function delay(value) { if (!pend) return value; - return new Promise((resolve) => pending.set(key, () => resolve(value))); + return new Promise((resolve) => deferred.push(() => resolve(value))); } - function resolve(key) { - pending.get(key)(); - pending.delete(key); - } - - const k = $derived(b === 0 ? await delay(`a${a}`, a) : 'B'); + const k = $derived(b === 0 ? await delay(a) : 'B'); -{await delay(`b${b}`, b)}
+{await delay(b)}
{#key k}{k}
@@ -26,5 +21,4 @@ - - + diff --git a/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/_config.js b/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/_config.js index be95f3a159..9cd8eba0b3 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/_config.js @@ -1,37 +1,33 @@ import { tick } from 'svelte'; import { test } from '../../test'; -const buttons = - ''; +const buttons = ''; export default test({ - async test({ assert, target, logs }) { + async test({ assert, target }) { await tick(); - const [X, Y, Z, W, resolve_e, resolve_x1] = target.querySelectorAll('button'); - - assert.htmlEqual(target.innerHTML, `n = 0
x = 0
${buttons}`); + const [x, toggle, pop] = target.querySelectorAll('button'); // X is deferred on its own await - X.click(); + x.click(); await tick(); // Y is deferred on the await inside the new branch - Y.click(); + toggle.click(); await tick(); // Z hides that branch again, merges into Y, which merges into X - Z.click(); + toggle.click(); await tick(); assert.htmlEqual(target.innerHTML, `n = 0
x = 0
${buttons}`); - assert.deepEqual(logs, ['delay x0', 'delay x1', 'delay e']); // the branch's await resolves while it is hidden - resolve_e.click(); + pop.click(); await tick(); // W shows the branch again and merges into X - W.click(); + toggle.click(); await tick(); // nothing is pending any more, so X commits - resolve_x1.click(); + pop.click(); await tick(); assert.htmlEqual(target.innerHTML, `n = 4
x = 1
cond: e
${buttons}`); } diff --git a/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/main.svelte index d4d03aa6eb..d4cbd8c5b6 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/async-merge-blocking-pending/main.svelte @@ -4,25 +4,19 @@ let cond = $state(false); let pend = false; - const pending = new Map(); + const deferred = []; - function delay(key, value) { - console.log(`delay ${key}`); + function delay(value) { if (!pend) return value; - return new Promise((resolve) => pending.set(key, () => resolve(value))); - } - - function resolve(key) { - pending.get(key)(); - pending.delete(key); + return new Promise((resolve) => deferred.push(() => resolve(value))); }n = {n}
-x = {await delay(`x${x}`, x)}
+x = {await delay(x)}
{#if cond} -cond: {await delay('e', 'e')}
+cond: {await delay('e')}
{/if} {#snippet pending()} @@ -30,9 +24,6 @@ {/snippet}