mirror of https://github.com/sveltejs/svelte
parent
33e3540bb9
commit
ab75873a57
@ -0,0 +1,21 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
const buttons = `<button>fork</button><button>show</button><button>commit</button>`;
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const [fork, show, commit] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
fork.click();
|
||||||
|
await tick();
|
||||||
|
|
||||||
|
show.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>0</p>`);
|
||||||
|
|
||||||
|
commit.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<span>two</span><p>2</p>`);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<script>
|
||||||
|
import { fork } from 'svelte';
|
||||||
|
|
||||||
|
let s1 = $state(0);
|
||||||
|
let show = $state(false);
|
||||||
|
let d2 = $derived(s1 * 2);
|
||||||
|
let f;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button onclick={() => (f = fork(() => (s1 = 1)))}>fork</button>
|
||||||
|
<button onclick={() => (show = true)}>show</button>
|
||||||
|
<button onclick={() => f.commit()}>commit</button>
|
||||||
|
|
||||||
|
{#if d2 === 2}<span>two</span>{/if}
|
||||||
|
{#if show}
|
||||||
|
<p>{d2}</p>
|
||||||
|
{/if}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
import { untrack } from 'svelte';
|
||||||
|
let { a, b, c, slow, fast } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p>{await slow(a)}</p>
|
||||||
|
<p>{await slow(b)}</p>
|
||||||
|
<p>{await fast(`${c}:${untrack(() => `${a}${b}`)}`)}</p>
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
const buttons = `
|
||||||
|
<button>a</button>
|
||||||
|
<button>b</button>
|
||||||
|
<button>c</button>
|
||||||
|
<button>resolve</button>
|
||||||
|
<button>resolve last</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// B3 reads values held by both B1 and B2 and therefore depends on both. When B3 merges
|
||||||
|
// into B2 (the nearest earlier related batch), B2 must inherit B3's dependency on B1,
|
||||||
|
// otherwise B2+B3 commit before B1 and leak B1's uncommitted value into the DOM
|
||||||
|
export default test({
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const [a, b, c, resolve, resolve_last] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>0</p><p>0</p><p>0:00</p>`);
|
||||||
|
|
||||||
|
a.click(); // B1: slow(a=1) pending
|
||||||
|
await tick();
|
||||||
|
b.click(); // B2: slow(b=1) pending
|
||||||
|
await tick();
|
||||||
|
c.click(); // B3: reads a=1 (held by B1) and b=1 (held by B2) -> depends on both, merges into B2
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>0</p><p>0</p><p>0:00</p>`);
|
||||||
|
|
||||||
|
// B2 finishes first: it must still wait for B1
|
||||||
|
resolve_last.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>0</p><p>0</p><p>0:00</p>`);
|
||||||
|
|
||||||
|
// B1 finishes: everything commits together
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>1</p><p>1</p><p>1:11</p>`);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<script>
|
||||||
|
import Child from './Child.svelte';
|
||||||
|
let a = $state(0);
|
||||||
|
let b = $state(0);
|
||||||
|
let c = $state(0);
|
||||||
|
|
||||||
|
const resolvers = [];
|
||||||
|
function slow(v) {
|
||||||
|
return new Promise((r) => resolvers.push(() => r(v)));
|
||||||
|
}
|
||||||
|
function fast(v) {
|
||||||
|
return Promise.resolve(v);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button onclick={() => a++}>a</button>
|
||||||
|
<button onclick={() => b++}>b</button>
|
||||||
|
<button onclick={() => c++}>c</button>
|
||||||
|
<button onclick={() => resolvers.shift()?.()}>resolve</button>
|
||||||
|
<button onclick={() => resolvers.pop()?.()}>resolve last</button>
|
||||||
|
|
||||||
|
<svelte:boundary>
|
||||||
|
{#snippet pending()}loading{/snippet}
|
||||||
|
<Child {a} {b} {c} {slow} {fast} />
|
||||||
|
</svelte:boundary>
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
const buttons = `
|
||||||
|
<button>flip</button>
|
||||||
|
<button>a</button>
|
||||||
|
<button>b</button>
|
||||||
|
<button>resolve</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// A reaction that reads its existing dependencies in a different order than last time
|
||||||
|
// is not a "new reader" of those dependencies — it must keep seeing its own batch's view,
|
||||||
|
// not the latest (uncommitted) value of a later pending batch
|
||||||
|
export default test({
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const [flip, a, , resolve] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>a0b0</p><p>true</p><p>a0</p>`);
|
||||||
|
|
||||||
|
flip.click(); // B1: cond = false, slow(cond) pending
|
||||||
|
await tick();
|
||||||
|
a.click(); // B2: a = 'a1', slow(a) pending
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>a0b0</p><p>true</p><p>a0</p>`);
|
||||||
|
|
||||||
|
// B1 commits while B2 is still pending. The flipped branch reads `b` then `a` (reordered
|
||||||
|
// deps) — `a` must still resolve to B1's view (a0), not B2's uncommitted a1
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>b0a0</p><p>false</p><p>a0</p>`);
|
||||||
|
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>b0a1</p><p>false</p><p>a1</p>`);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
<script>
|
||||||
|
let cond = $state(true);
|
||||||
|
let a = $state('a0');
|
||||||
|
let b = $state('b0');
|
||||||
|
|
||||||
|
const resolvers = [];
|
||||||
|
function slow(v) {
|
||||||
|
return new Promise((r) => resolvers.push(() => r(v)));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button onclick={() => (cond = !cond)}>flip</button>
|
||||||
|
<button onclick={() => (a = "a1")}>a</button>
|
||||||
|
<button onclick={() => (b = "b1")}>b</button>
|
||||||
|
<button onclick={() => resolvers.shift()?.()}>resolve</button>
|
||||||
|
|
||||||
|
<svelte:boundary>
|
||||||
|
{#snippet pending()}loading{/snippet}
|
||||||
|
<p>{cond ? a + b : b + a}</p>
|
||||||
|
<p>{await slow(cond)}</p>
|
||||||
|
<p>{await slow(a)}</p>
|
||||||
|
</svelte:boundary>
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
let { a, b, c, slow, track } = $props();
|
||||||
|
let sa = $derived(await slow(a));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p>{sa}</p>
|
||||||
|
<p>{await slow(b + c)}</p>
|
||||||
|
<p>{await track(sa, b, c)}</p>
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
const buttons = `<button>a</button><button>bc</button><button>resolve</button>`;
|
||||||
|
|
||||||
|
// An effect that runs in an earlier batch and reads _several_ values that a later batch
|
||||||
|
// holds newer versions of must only be re-run once in that later batch, not once per value
|
||||||
|
export default test({
|
||||||
|
async test({ assert, target, instance }) {
|
||||||
|
const [a, bc, resolve] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
assert.deepEqual(instance.get_calls(), ['0/0/0']);
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>0</p><p>0</p><p>0/0/0</p>`);
|
||||||
|
|
||||||
|
// B1: a++ -> `sa` pending
|
||||||
|
a.click();
|
||||||
|
await tick();
|
||||||
|
assert.deepEqual(instance.get_calls(), ['0/0/0']);
|
||||||
|
|
||||||
|
// B2: b++, c++ -> `slow(b + c)` pending, `track` re-runs with B2's view
|
||||||
|
bc.click();
|
||||||
|
await tick();
|
||||||
|
assert.deepEqual(instance.get_calls(), ['0/0/0', '0/1/1']);
|
||||||
|
|
||||||
|
// resolve `sa` in B1: `track` re-runs in B1 (reading B1's stale view of b/c) and,
|
||||||
|
// because it read two values held by B2, exactly once more in B2
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
assert.deepEqual(instance.get_calls(), ['0/0/0', '0/1/1', '1/0/0', '1/1/1']);
|
||||||
|
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
resolve.click();
|
||||||
|
await tick();
|
||||||
|
}
|
||||||
|
assert.deepEqual(instance.get_calls(), ['0/0/0', '0/1/1', '1/0/0', '1/1/1']);
|
||||||
|
assert.htmlEqual(target.innerHTML, `${buttons}<p>1</p><p>2</p><p>1/1/1</p>`);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
<script>
|
||||||
|
import Child from './Child.svelte';
|
||||||
|
let a = $state(0);
|
||||||
|
let b = $state(0);
|
||||||
|
let c = $state(0);
|
||||||
|
let calls = [];
|
||||||
|
|
||||||
|
const resolvers = [];
|
||||||
|
function slow(v) {
|
||||||
|
return new Promise((r) => resolvers.push(() => r(v)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_calls() {
|
||||||
|
return calls;
|
||||||
|
}
|
||||||
|
|
||||||
|
function track(...args) {
|
||||||
|
calls.push(args.join('/'));
|
||||||
|
return slow(args.join('/'));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button onclick={() => a++}>a</button>
|
||||||
|
<button onclick={() => { b++; c++; }}>bc</button>
|
||||||
|
<button onclick={() => resolvers.shift()?.()}>resolve</button>
|
||||||
|
|
||||||
|
<svelte:boundary>
|
||||||
|
{#snippet pending()}loading{/snippet}
|
||||||
|
<Child {a} {b} {c} {slow} {track} />
|
||||||
|
</svelte:boundary>
|
||||||
Loading…
Reference in new issue