chore: more passing async tests (#19014)

a handful more tests from #18880 that already pass on `main`, including
some existing tests that were previously skipped
main
Rich Harris 3 days ago committed by GitHub
parent a841a29407
commit 40bb08482f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,37 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
await tick();
const [x, y, shift] = target.querySelectorAll('button');
assert.deepEqual(logs, ['effect _ 0']);
// write x — the batch is pending on its async expression
x.click();
await tick();
assert.deepEqual(logs, ['effect _ 0']);
// the effect newly depends on `x` — it reads the held-back value (0)
// (at some point it read latest, but we decided against doing this)
y.click();
await tick();
assert.deepEqual(logs, ['effect _ 0', 'effect 0 1']);
// the effect re-runs and no longer depends on `x` at all
y.click();
await tick();
assert.deepEqual(logs, ['effect _ 0', 'effect 0 1', 'effect _ 2']);
// the pending batch settles and commits x = 1 — the effect no longer
// depends on `x`, so it should not re-run
shift.click();
await tick();
assert.deepEqual(logs, ['effect _ 0', 'effect 0 1', 'effect _ 2']);
assert.htmlEqual(
target.innerHTML,
'<p>1</p><button>x</button><button>y</button><button>shift</button>'
);
}
});

@ -0,0 +1,24 @@
<script>
let x = $state(0);
let y = $state(0);
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
$effect(() => {
if (y === 1) {
console.log(`effect ${x} ${y}`);
} else {
console.log(`effect _ ${y}`);
}
});
</script>
<p>{await delay(x)}</p>
<button onclick={() => x++}>x</button>
<button onclick={() => y++}>y</button>
<button onclick={() => deferred.shift()?.()}>shift</button>

@ -2,7 +2,6 @@ import { tick } from 'svelte';
import { test } from '../../test';
export default test({
skip: true, // TODO works on https://github.com/sveltejs/svelte/pull/17971
async test({ assert, target }) {
await tick();
const [a_b, a_c, b_d, shift, pop] = target.querySelectorAll('button');
@ -63,12 +62,12 @@ export default test({
`
);
pop.click(); // second b resolved, still pending: [b, a]
pop.click(); // second b resolved, still pending: [b, a] but b is now obsolete
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
a 1 | b 2 | c 0 | d 1
<button>a and b</button>
<button>a and c</button>
<button>b and d</button>
@ -79,18 +78,6 @@ export default test({
shift.click(); // first b resolved, first + last batch settled, still pending: [a]
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 1 | b 2 | c 0 | d 1
<button>a and b</button>
<button>a and c</button>
<button>b and d</button>
<button>shift</button>
<button>pop</button>
`
);
shift.click(); // all resolved
await tick();
assert.htmlEqual(

@ -2,7 +2,6 @@ import { tick } from 'svelte';
import { test } from '../../test';
export default test({
skip: true, // TODO works on https://github.com/sveltejs/svelte/pull/17971
async test({ assert, target }) {
await tick();
const [a_b, a_c, b_d, shift, pop] = target.querySelectorAll('button');
@ -63,12 +62,12 @@ export default test({
`
);
pop.click(); // second b resolved, still pending: [b, a]
pop.click(); // second b resolved, still pending: [b, a] with b obsolete so can render some already
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
a 1 | b 2 | c 0 | d 1
<button>a and b</button>
<button>a and c</button>
<button>b and d</button>
@ -78,22 +77,6 @@ export default test({
);
pop.click(); // second a resolved, first a/b now obsolete
// TODO would be nice to show final result here already, right now it doesn't because
// we have no handle on the already resolved first a anymore
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
<button>a and b</button>
<button>a and c</button>
<button>b and d</button>
<button>shift</button>
<button>pop</button>
`
);
shift.click(); // queue empty
await tick();
assert.htmlEqual(
target.innerHTML,

@ -2,7 +2,6 @@ import { tick } from 'svelte';
import { test } from '../../test';
export default test({
skip: true,
async test({ assert, target }) {
await tick();
const [a_b_fork, a_c, b_d, shift, pop, commit] = target.querySelectorAll('button');

@ -0,0 +1,52 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
const [x, y, resolve] = target.querySelectorAll('button');
x.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<h1>WORLD</h1>
`
);
y.click();
await tick();
// the new branch reads `upper` — a derived of the source the pending batch
// wrote — as a new dependency. It renders with the pre-write value 'WORLD',
// as the pending batch's write is hidden from the batch that commits
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<h1>WORLD</h1>
<p>WORLD</p>
`
);
resolve.click();
await tick();
// the pending batch discovers that the new branch depends on its write
// when it resumes, and commits it fully consistent
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<h1>UNIVERSE</h1>
universe
<p>UNIVERSE</p>
`
);
}
});

@ -0,0 +1,29 @@
<script>
let x = $state({ x: 'world' });
let y = $state(0);
let deferred = [];
const upper = $derived(x.x.toUpperCase());
function delay(s) {
const d = Promise.withResolvers();
deferred.push(() => d.resolve(s));
return d.promise;
}
</script>
<button onclick={() => (x = { x: 'universe' })}>x</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => deferred.shift()()}>resolve</button>
<h1>{upper}</h1>
{#if x.x === 'universe'}
{await delay(x.x)}
{/if}
{#if y > 0}
<p>{upper}</p>
{/if}

@ -2,7 +2,6 @@ import { tick } from 'svelte';
import { test } from '../../test';
export default test({
skip: true, // TODO works on https://github.com/sveltejs/svelte/pull/17971
async test({ assert, target }) {
const [x, y, resolve, commit] = target.querySelectorAll('button');

@ -0,0 +1,30 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
async test({ assert, target }) {
await tick();
const [update, show, resolve] = target.querySelectorAll('button');
update.click();
await tick();
show.click();
await tick();
// the template effect newly depends on `value`, which the pending batch
// has written — it reads the pre-write value and crashes
assert.htmlEqual(
target.innerHTML,
`
<button>update</button>
<button>show</button>
<button>resolve</button>
<p>crash</p>
`
);
resolve.click();
}
});

@ -0,0 +1,22 @@
<script>
let value = $state();
let show = $state(false);
const deferred = Promise.withResolvers();
function wait(value) {
return value === undefined ? '' : deferred.promise;
}
</script>
<button onclick={() => (value = { x: 1 })}>update</button>
<button onclick={() => (show = true)}>show</button>
<button onclick={() => deferred.resolve('')}>resolve</button>
{await wait(value)}
<svelte:boundary>
<p>{show ? value.x + value.x : ''}</p>
{#snippet failed()}
<p>crash</p>
{/snippet}
</svelte:boundary>

@ -0,0 +1,14 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
// A render effect in a branch that is created during traversal doesn't run right away (in async mode,
// it runs together with the other render effects of the batch), but its error is still caught by the boundary
export default test({
mode: ['client'],
test({ assert, target }) {
const [show] = target.querySelectorAll('button');
flushSync(() => show.click());
assert.htmlEqual(target.innerHTML, '<button>show</button><p>crash</p>');
}
});

@ -0,0 +1,16 @@
<script>
let value = $state();
let show = $state(false);
</script>
<button onclick={() => (show = true)}>show</button>
{#if show}
<svelte:boundary>
<p>{value.x}</p>
{#snippet failed()}
<p>crash</p>
{/snippet}
</svelte:boundary>
{/if}
Loading…
Cancel
Save