mirror of https://github.com/sveltejs/svelte
teemingc-hydration-regression-test
parent
458397909c
commit
9f12fdd61f
@ -0,0 +1,30 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
// Like `async-derived-read-keeps-maybe-dirty`, but the `<span>` also reads `s`, which makes it DIRTY
|
||||
// rather than MAYBE_DIRTY, so A doesn't evaluate `d` even when checking the `<span>` effect eagerly
|
||||
// (`is_dirty` returns right away). C's read of `d` must still not mark it as clean
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
async test({ assert, target, logs }) {
|
||||
await tick();
|
||||
const [s, u, d, , resolve_b] = target.querySelectorAll('button');
|
||||
|
||||
// batch A: `d` becomes 10, but is not evaluated yet (the `<span>` is deferred)
|
||||
s.click();
|
||||
await tick();
|
||||
|
||||
// batch B
|
||||
u.click();
|
||||
await tick();
|
||||
|
||||
// batch C writes `s` and reads `d` (20) while A and B are pending
|
||||
d.click();
|
||||
await tick();
|
||||
|
||||
// B sees A's change (but not C's), so `d` is 10 in its world
|
||||
resolve_b.click();
|
||||
await tick();
|
||||
assert.equal(logs[0], 'probe 10');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,44 @@
|
||||
<script>
|
||||
let s = $state(0);
|
||||
let u = $state(0);
|
||||
let w = $state(0);
|
||||
let show = $state(true);
|
||||
let d = $derived(s * 10);
|
||||
|
||||
const pending = new Map();
|
||||
|
||||
function delay(name, value) {
|
||||
if (!value) return value;
|
||||
return new Promise((resolve) => pending.set(name, () => resolve(value)));
|
||||
}
|
||||
|
||||
function probe(value) {
|
||||
console.log(`probe ${value}`);
|
||||
return value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => s++}>s</button>
|
||||
<button onclick={() => u++}>u</button>
|
||||
<button
|
||||
onclick={() => {
|
||||
s++;
|
||||
d;
|
||||
w++;
|
||||
}}>d</button
|
||||
>
|
||||
<button onclick={() => pending.get('a')?.()}>resolve a</button>
|
||||
<button onclick={() => pending.get('b')?.()}>resolve b</button>
|
||||
<button onclick={() => pending.get('c')?.()}>resolve c</button>
|
||||
|
||||
<p>{await delay('a', s)}</p>
|
||||
<p>{await delay('c', w)}</p>
|
||||
<!-- inside a branch, so that B's traversal doesn't reach (and stash) the effect reading `d` -->
|
||||
{#if show}
|
||||
<!-- reads `s` directly, so the effect is DIRTY, so even if we were to eagerly do is_dirty in batch A it wouldn't evaluate `d` -->
|
||||
<span>{s}-{d}</span>
|
||||
{/if}
|
||||
|
||||
{#if await delay('b', u)}
|
||||
<i>{await probe(d)}</i>
|
||||
{/if}
|
||||
@ -0,0 +1,31 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
// Like `async-derived-assign-keeps-maybe-dirty`, but C reads `d` in the event handler instead of
|
||||
// assigning to it. That records `d`'s stale value (A never evaluated it) as C's previous value, and
|
||||
// must not mark `d` as clean: B never stashed a reaction that reads `d`, so it wouldn't re-validate
|
||||
// it and would use that stale value
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
async test({ assert, target, logs }) {
|
||||
await tick();
|
||||
const [s, u, d, , resolve_b] = target.querySelectorAll('button');
|
||||
|
||||
// batch A: `d` becomes 10, but is not evaluated yet (the `<span>` is deferred)
|
||||
s.click();
|
||||
await tick();
|
||||
|
||||
// batch B
|
||||
u.click();
|
||||
await tick();
|
||||
|
||||
// batch C writes `s` and reads `d` (20) while A and B are pending
|
||||
d.click();
|
||||
await tick();
|
||||
|
||||
// B sees A's change (but not C's), so `d` is 10 in its world
|
||||
resolve_b.click();
|
||||
await tick();
|
||||
assert.equal(logs[0], 'probe 10');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,43 @@
|
||||
<script>
|
||||
let s = $state(0);
|
||||
let u = $state(0);
|
||||
let w = $state(0);
|
||||
let show = $state(true);
|
||||
let d = $derived(s * 10);
|
||||
|
||||
const pending = new Map();
|
||||
|
||||
function delay(name, value) {
|
||||
if (!value) return value;
|
||||
return new Promise((resolve) => pending.set(name, () => resolve(value)));
|
||||
}
|
||||
|
||||
function probe(value) {
|
||||
console.log(`probe ${value}`);
|
||||
return value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => s++}>s</button>
|
||||
<button onclick={() => u++}>u</button>
|
||||
<button
|
||||
onclick={() => {
|
||||
s++;
|
||||
d;
|
||||
w++;
|
||||
}}>d</button
|
||||
>
|
||||
<button onclick={() => pending.get('a')?.()}>resolve a</button>
|
||||
<button onclick={() => pending.get('b')?.()}>resolve b</button>
|
||||
<button onclick={() => pending.get('c')?.()}>resolve c</button>
|
||||
|
||||
<p>{await delay('a', s)}</p>
|
||||
<p>{await delay('c', w)}</p>
|
||||
<!-- inside a branch, so that B's traversal doesn't reach (and stash) the effect reading `d` -->
|
||||
{#if show}
|
||||
<span>{d}</span>
|
||||
{/if}
|
||||
|
||||
{#if await delay('b', u)}
|
||||
<i>{await probe(d)}</i>
|
||||
{/if}
|
||||
Loading…
Reference in new issue