chore: lots of async tests (#17997)

extracting this from #17971 because even if that PR is not merged these
tests show that there's still a lot of cases where we're buggy,
particularly around forking and new branches
pull/17999/head
Simon H 5 months ago committed by GitHub
parent 6e52f40d41
commit 5e8662fb23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,107 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
await tick();
const [a_b, a_c, b_d, shift, pop] = target.querySelectorAll('button');
a_b.click();
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>
`
);
a_c.click();
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>
`
);
b_d.click();
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();
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();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 1 | b 1 | 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();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 2 | b 1 | c 1 | 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();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 2 | b 2 | c 1 | d 1
<button>a and b</button>
<button>a and c</button>
<button>b and d</button>
<button>shift</button>
<button>pop</button>
`
);
}
});

@ -0,0 +1,26 @@
<script>
let a = $state(0);
let b = $state(0);
let c = $state(0);
let d = $state(0);
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
a {await delay(a)} | b {await delay(b)} | c {c} | d {d}
<button onclick={() => {a++;b++;}}>
a and b
</button>
<button onclick={() => {a++;c++;}}>
a and c
</button>
<button onclick={() => {b++;d++;}}>
b and d
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>

@ -0,0 +1,82 @@
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');
a_b.click();
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>
`
);
a_c.click();
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>
`
);
b_d.click();
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>
`
);
pop.click(); // second b resolved, blocked on first batch because a still pending
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>
`
);
for (let i = 0; i < 3; i++) {
pop.click(); // second a resolved, first a/b now obsolete; empty queue
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 2 | b 2 | c 1 | d 1
<button>a and b</button>
<button>a and c</button>
<button>b and d</button>
<button>shift</button>
<button>pop</button>
`
);
}
}
});

@ -0,0 +1,26 @@
<script>
let a = $state(0);
let b = $state(0);
let c = $state(0);
let d = $state(0);
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
a {await delay(a)} | b {await delay(b)} | c {c} | d {d}
<button onclick={() => {a++;b++;}}>
a and b
</button>
<button onclick={() => {a++;c++;}}>
a and c
</button>
<button onclick={() => {b++;d++;}}>
b and d
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>

@ -0,0 +1,108 @@
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');
a_b.click();
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>
`
);
a_c.click();
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>
`
);
b_d.click();
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(); // first a resolved, still pending: [b, a, b]
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>
`
);
pop.click(); // second b resolved, still pending: [b, a]
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(); // 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(
target.innerHTML,
`
a 2 | b 2 | c 1 | d 1
<button>a and b</button>
<button>a and c</button>
<button>b and d</button>
<button>shift</button>
<button>pop</button>
`
);
}
});

@ -0,0 +1,26 @@
<script>
let a = $state(0);
let b = $state(0);
let c = $state(0);
let d = $state(0);
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
a {await delay(a)} | b {await delay(b)} | c {c} | d {d}
<button onclick={() => {a++;b++;}}>
a and b
</button>
<button onclick={() => {a++;c++;}}>
a and c
</button>
<button onclick={() => {b++;d++;}}>
b and d
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>

@ -0,0 +1,110 @@
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');
a_b.click();
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>
`
);
a_c.click();
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>
`
);
b_d.click();
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(); // first a resolved, still pending: [b, a, b]
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>
`
);
pop.click(); // second b resolved, still pending: [b, a]
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>
`
);
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,
`
a 2 | b 2 | c 1 | d 1
<button>a and b</button>
<button>a and c</button>
<button>b and d</button>
<button>shift</button>
<button>pop</button>
`
);
}
});

@ -0,0 +1,26 @@
<script>
let a = $state(0);
let b = $state(0);
let c = $state(0);
let d = $state(0);
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
a {await delay(a)} | b {await delay(b)} | c {c} | d {d}
<button onclick={() => {a++;b++;}}>
a and b
</button>
<button onclick={() => {a++;c++;}}>
a and c
</button>
<button onclick={() => {b++;d++;}}>
b and d
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>

@ -0,0 +1,118 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
await tick();
const [a, c, shift, pop] = target.querySelectorAll('button');
a.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
c.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
shift.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
// how it's on main
shift.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 1 | d 1
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
shift.click();
await tick();
shift.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 1 | b 2 | c 1 | d 3
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
// how it's on https://github.com/sveltejs/svelte/pull/17971
// shift.click();
// await tick();
// assert.htmlEqual(
// target.innerHTML,
// `
// a 0 | b 0 | c 0 | d 0
// <button>a++</button>
// <button>c++</button>
// <button>shift</button>
// <button>pop</button>
// `
// );
// shift.click();
// await tick();
// assert.htmlEqual(
// target.innerHTML,
// `
// a 1 | b 2 | c 0 | d 2
// <button>a++</button>
// <button>c++</button>
// <button>shift</button>
// <button>pop</button>
// `
// );
// shift.click();
// await tick();
// assert.htmlEqual(
// target.innerHTML,
// `
// a 1 | b 2 | c 1 | d 3
// <button>a++</button>
// <button>c++</button>
// <button>shift</button>
// <button>pop</button>
// `
// );
}
});

@ -0,0 +1,23 @@
<script>
let a = $state(0);
let b = $derived(await delay(a * 2));
let c = $state(0);
let d = $derived(await delay(b + c));
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
a {a} | b {b} | c {c} | d {d}
<button onclick={() => {a++;}}>
a++
</button>
<button onclick={() => {c++;}}>
c++
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>

@ -0,0 +1,76 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
await tick();
const [a, c, shift, pop] = target.querySelectorAll('button');
a.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
c.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
// Although the second batch is eventually connected to the first one, we can't see that
// at this point yet and so the second one flushes right away.
pop.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 1 | d 1
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
pop.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 1 | d 1
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
pop.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 1 | b 2 | c 1 | d 3
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
}
});

@ -0,0 +1,23 @@
<script>
let a = $state(0);
let b = $derived(await delay(a * 2));
let c = $state(0);
let d = $derived(await delay(b + c));
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
a {a} | b {b} | c {c} | d {d}
<button onclick={() => {a++;}}>
a++
</button>
<button onclick={() => {c++;}}>
c++
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>

@ -0,0 +1,129 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
await tick();
const [a, c, shift, pop] = target.querySelectorAll('button');
a.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
c.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
shift.click(); // schedules second step of first batch and schedules rerun of second batch
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 0 | b 0 | c 0 | d 0
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
// how it's on main
pop.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 1 | b 2 | c 0 | d 2
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
shift.click(); // obsolete second batch promise (already rejected)
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 1 | b 2 | c 0 | d 2
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
shift.click(); // first batch resolves
await tick();
assert.htmlEqual(
target.innerHTML,
`
a 1 | b 2 | c 1 | d 3
<button>a++</button>
<button>c++</button>
<button>shift</button>
<button>pop</button>
`
);
// how it's on https://github.com/sveltejs/svelte/pull/17971
// pop.click(); // second batch resolves but knows it needs to wait on first batch
// await tick();
// assert.htmlEqual(
// target.innerHTML,
// `
// a 0 | b 0 | c 0 | d 0
// <button>a++</button>
// <button>c++</button>
// <button>shift</button>
// <button>pop</button>
// `
// );
// shift.click(); // obsolete second batch promise (already rejected)
// await tick();
// assert.htmlEqual(
// target.innerHTML,
// `
// a 0 | b 0 | c 0 | d 0
// <button>a++</button>
// <button>c++</button>
// <button>shift</button>
// <button>pop</button>
// `
// );
// shift.click(); // first batch resolves, with it second can now resolve as well
// await tick();
// assert.htmlEqual(
// target.innerHTML,
// `
// a 1 | b 2 | c 1 | d 3
// <button>a++</button>
// <button>c++</button>
// <button>shift</button>
// <button>pop</button>
// `
// );
}
});

@ -0,0 +1,23 @@
<script>
let a = $state(0);
let b = $derived(await delay(a * 2));
let c = $state(0);
let d = $derived(await delay(b + c));
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
a {a} | b {b} | c {c} | d {d}
<button onclick={() => {a++;}}>
a++
</button>
<button onclick={() => {c++;}}>
c++
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>

@ -0,0 +1,34 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
await tick();
const [a_b_fork, a_c, shift, pop, commit] = target.querySelectorAll('button');
const [p] = target.querySelectorAll('p');
a_b_fork.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0');
a_c.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0');
pop.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 0 | c 1');
shift.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 0 | c 1');
shift.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 0 | c 1');
commit.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 1 | c 1');
}
});

@ -0,0 +1,27 @@
<script>
import { fork } from 'svelte';
let a = $state(0);
let b = $state(0);
let c = $state(0);
let f;
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<p>a {await delay(a)} | b {await delay(b)} | c {c}</p>
<button onclick={() => {f = fork(() => {a++;b++;});}}>
a and b (fork)
</button>
<button onclick={() => {a++;c++;}}>
a and c
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>
<button onclick={() => f.commit()}>commit fork</button>

@ -0,0 +1,42 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
await tick();
const [a_b_fork, a_c, b_d, shift, pop, commit] = target.querySelectorAll('button');
const [p] = target.querySelectorAll('p');
a_b_fork.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0 | d 0');
a_c.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0 | d 0');
b_d.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0 | d 0');
pop.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 1 | c 0 | d 1');
pop.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 1 | c 1 | d 1');
shift.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 1 | c 1 | d 1');
shift.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 1 | c 1 | d 1');
commit.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 1 | c 1 | d 1');
}
});

@ -0,0 +1,31 @@
<script>
import { fork } from 'svelte';
let a = $state(0);
let b = $state(0);
let c = $state(0);
let d = $state(0);
let f;
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<p>a {await delay(a)} | b {await delay(b)} | c {c} | d {d}</p>
<button onclick={() => {f = fork(() => {a++;b++;});}}>
a and b (fork)
</button>
<button onclick={() => {a++;c++;}}>
a and c
</button>
<button onclick={() => {b++;d++;}}>
b and d
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>
<button onclick={() => f.commit()}>commit fork</button>

@ -0,0 +1,43 @@
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');
const [p] = target.querySelectorAll('p');
a_b_fork.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0 | d 0');
a_c.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0 | d 0');
b_d.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0 | d 0');
shift.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0 | d 0');
shift.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 0 | b 0 | c 0 | d 0');
shift.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 0 | c 1 | d 0');
shift.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 1 | c 1 | d 1');
commit.click();
await tick();
assert.htmlEqual(p.innerHTML, 'a 1 | b 1 | c 1 | d 1');
}
});

@ -0,0 +1,31 @@
<script>
import { fork } from 'svelte';
let a = $state(0);
let b = $state(0);
let c = $state(0);
let d = $state(0);
let f;
const deferred = [];
function delay(value) {
if (!value) return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<p>a {await delay(a)} | b {await delay(b)} | c {c} | d {d}</p>
<button onclick={() => {f = fork(() => {a++;b++;});}}>
a and b (fork)
</button>
<button onclick={() => {a++;c++;}}>
a and c
</button>
<button onclick={() => {b++;d++;}}>
b and d
</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>
<button onclick={() => f.commit()}>commit fork</button>

@ -0,0 +1,7 @@
<script>
let { x } = $props();
console.log(x);
$effect(() => console.log('$effect: '+ x))
</script>
{x}

@ -0,0 +1,38 @@
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, logs }) {
const [x, y, resolve] = target.querySelectorAll('button');
x.click();
await tick();
y.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
` // if this shows world world - that would also be ok
);
resolve.click();
await tick();
assert.deepEqual(logs, ['universe', 'universe', '$effect: universe', '$effect: universe']);
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
universe
universe
universe
`
);
}
});

@ -0,0 +1,28 @@
<script>
import Child from './Child.svelte';
let x = $state('world');
let y = $state(0);
let deferred = [];
function delay(s) {
const d = Promise.withResolvers();
deferred.push(() => d.resolve(s))
return d.promise;
}
</script>
<button onclick={() => x = 'universe'}>x</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => deferred.shift()()}>resolve</button>
{#if x === 'universe'}
{await delay(x)}
<Child {x} />
{/if}
{#if y > 0}
<Child {x} />
{/if}

@ -0,0 +1,11 @@
<script>
let { x } = $props();
</script>
<!-- checks direct source, indirect derived, block effect, async effect -->
{x}
{JSON.stringify(x)}
{#if x === 'universe'}universe{:else}world{/if}
{#if JSON.stringify(x) === '"universe"'}universe{:else}world{/if}
{await Promise.resolve(x)}
{await Promise.resolve(JSON.stringify(x))}

@ -0,0 +1,49 @@
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] = target.querySelectorAll('button');
x.click();
await tick();
y.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<hr>
` // if this shows world world "world" world world world "world" - then this would also be ok
);
resolve.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
universe
universe
"universe"
universe
universe
universe
"universe"
<hr>
universe
"universe"
universe
universe
universe
"universe"
`
);
}
});

@ -0,0 +1,30 @@
<script>
import Child from './Child.svelte';
let x = $state('world');
let y = $state(0);
let deferred = [];
function delay(s) {
const d = Promise.withResolvers();
deferred.push(() => d.resolve(s))
return d.promise;
}
</script>
<button onclick={() => x = 'universe'}>x</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => deferred.shift()()}>resolve</button>
{#if x === 'universe'}
{await delay(x)}
<Child {x} />
{/if}
<hr>
{#if y > 0}
<Child {x} />
{/if}

@ -0,0 +1,11 @@
<script>
let { x } = $props();
</script>
<!-- checks direct source, indirect derived, block effect, async effect -->
{x}
{JSON.stringify(x)}
{#if x === 'universe'}universe{:else}world{/if}
{#if JSON.stringify(x) === '"universe"'}universe{:else}world{/if}
{await Promise.resolve(x)}
{await Promise.resolve(JSON.stringify(x))}

@ -0,0 +1,61 @@
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] = target.querySelectorAll('button');
x.click();
await tick();
y.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<hr>
`
);
resolve.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<hr>
` // if this shows world world "world" world world world "world" - then this would also be ok
);
resolve.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
universe
universe
"universe"
universe
universe
universe
"universe"
<hr>
universe
"universe"
universe
universe
universe
"universe"
`
);
}
});

@ -0,0 +1,34 @@
<script>
import Child from './Child.svelte';
let x = $state('world');
let y = $state(0);
const deferred = [];
function delay(value) {
if (value !== 'universe') return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
function delay2(value) {
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<button onclick={() => (x = 'universe')}>x</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => deferred.pop()?.()}>resolve</button>
{#if x === 'universe'}
{await delay(x)}
<Child {x} />
{/if}
<hr>
{#if y > 0}
<Child x={await delay2(x)} />
{/if}

@ -0,0 +1,11 @@
<script>
let { x } = $props();
</script>
<!-- checks direct source, indirect derived, block effect, async effect -->
{x}
{JSON.stringify(x)}
{#if x === 'universe'}universe{:else}world{/if}
{#if JSON.stringify(x) === '"universe"'}universe{:else}world{/if}
{await Promise.resolve(x)}
{await Promise.resolve(JSON.stringify(x))}

@ -0,0 +1,171 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
skip: true, // TODO more combinations pass on https://github.com/sveltejs/svelte/pull/17971
timeout: 20_000,
async test({ assert, target }) {
const [x, fork_x, y, fork_y, shift, pop, commit_x, commit_y, reset] =
target.querySelectorAll('button');
const initial = `
<button>x</button>
<button>x (fork)</button>
<button>y++</button>
<button>y++ (fork)</button>
<button>shift</button>
<button>pop</button>
<button>commit x</button>
<button>commit y</button>
<button>reset</button>
<hr>
`;
const final = `
<button>x</button>
<button>x (fork)</button>
<button>y++</button>
<button>y++ (fork)</button>
<button>shift</button>
<button>pop</button>
<button>commit x</button>
<button>commit y</button>
<button>reset</button>
universe
universe
"universe"
universe
universe
universe
"universe"
<hr>
universe
"universe"
universe
universe
universe
"universe"
`;
/** @param {HTMLElement} button */
async function click(button) {
button.click();
await tick();
}
/**
* Generate all permutations of an array.
* @param {HTMLElement[]} actions
* @returns {HTMLElement[][]}
*/
function permutations(actions) {
if (actions.length <= 1) return [actions];
/** @type {HTMLElement[][]} */
const result = [];
for (let i = 0; i < actions.length; i++) {
const head = actions[i];
const rest = actions.slice(0, i).concat(actions.slice(i + 1));
for (const tail of permutations(rest)) {
result.push([head, ...tail]);
}
}
return result;
}
/**
* Keep only valid orders where fork commits happen after their fork action.
* @param {HTMLElement[]} order
*/
function is_valid_order(order) {
const x_fork_index = order.indexOf(fork_x);
const commit_x_index = order.indexOf(commit_x);
if (commit_x_index !== -1 && (x_fork_index === -1 || commit_x_index < x_fork_index)) {
return false;
}
const y_fork_index = order.indexOf(fork_y);
const commit_y_index = order.indexOf(commit_y);
if (commit_y_index !== -1 && (y_fork_index === -1 || commit_y_index < y_fork_index)) {
return false;
}
return true;
}
/**
* Four control scenarios:
* - x direct, y direct
* - x direct, y via fork+commit
* - x via fork+commit, y direct
* - x via fork+commit, y via fork+commit
*/
const control_scenarios = [
[x, y],
[x, fork_y, commit_y],
[fork_x, commit_x, y],
[fork_x, commit_x, fork_y, commit_y]
];
const control_orders = control_scenarios.flatMap((scenario) =>
permutations(scenario).filter(is_valid_order)
);
/**
* All shift/pop combinations for draining async work.
* We click three times because this scenario can queue up to 3 deferred resolutions.
*/
const resolve_orders = [
[shift, shift, shift],
[shift, pop, pop],
[pop, shift, shift],
[pop, pop, pop]
];
for (const controls of control_orders) {
for (const resolves of resolve_orders) {
for (const action of controls) {
await click(action);
}
for (const action of resolves) {
await click(action);
}
const failure_msg = `Failed for: ${controls
.map((btn) => btn.textContent)
.concat(...resolves.map((btn) => btn.textContent))
.join(', ')}`;
assert.htmlEqual(target.innerHTML, final, failure_msg);
await click(reset);
assert.htmlEqual(target.innerHTML, initial, failure_msg);
}
}
const other_scenarios = [
[x, shift, y, shift, shift],
[x, shift, y, pop, pop],
[fork_x, shift, y, shift, commit_x, shift],
[fork_x, shift, y, pop, commit_x, pop],
[y, shift, x, shift, shift],
[y, shift, x, pop, pop],
[fork_y, shift, x, shift, commit_y, shift],
[fork_y, shift, x, pop, commit_y, pop]
];
for (const scenario of other_scenarios) {
for (const action of scenario) {
await click(action);
}
const failure_msg = `Failed for: ${scenario.map((btn) => btn.textContent).join(', ')}`;
assert.htmlEqual(target.innerHTML, final, failure_msg);
await click(reset);
assert.htmlEqual(target.innerHTML, initial, failure_msg);
}
}
});

@ -0,0 +1,41 @@
<script>
import { fork } from 'svelte';
import Child from './Child.svelte';
let x = $state('world');
let y = $state(0);
let fx;
let fy;
const deferred = [];
function delay(value) {
if (value !== 'universe') return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
function delay2(value) {
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<button onclick={() => (x = 'universe')}>x</button>
<button onclick={() => (fx = fork(() => {x = 'universe';}))}>x (fork)</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => (fy = fork(() => {y++;}))}>y++ (fork)</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>
<button onclick={() => fx.commit()}>commit x</button>
<button onclick={() => fy.commit()}>commit y</button>
<button onclick={() => {x = 'world'; y = 0;}}>reset</button>
{#if x === 'universe'}
{await delay(x)}
<Child {x} />
{/if}
<hr>
{#if y > 0}
<Child x={await delay2(x)} />
{/if}

@ -0,0 +1,11 @@
<script>
let { x } = $props();
</script>
<!-- checks direct source, indirect derived, block effect, async effect -->
{x}
{JSON.stringify(x)}
{#if x === 'universe'}universe{:else}world{/if}
{#if JSON.stringify(x) === '"universe"'}universe{:else}world{/if}
{await Promise.resolve(x)}
{await Promise.resolve(JSON.stringify(x))}

@ -0,0 +1,90 @@
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, shift, pop, commit] = target.querySelectorAll('button');
x.click();
await tick();
y.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
<hr>
`
);
commit.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
<hr>
`
);
shift.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
universe
universe
"universe"
universe
universe
universe
"universe"
<hr>
`
);
shift.click();
await tick();
shift.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
universe
universe
"universe"
universe
universe
universe
"universe"
<hr>
universe
"universe"
universe
universe
universe
"universe"
`
);
}
});

@ -0,0 +1,36 @@
<script>
import { fork } from 'svelte';
import Child from './Child.svelte';
let x = $state('world');
let y = $state(0);
let f;
const deferred = [];
function delay(value) {
if (value !== 'universe') return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
function delay2(value) {
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<button onclick={() => (f = fork(() => {x = 'universe';}))}>x</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>
<button onclick={() => f.commit()}>commit</button>
{#if x === 'universe'}
{await delay(x)}
<Child {x} />
{/if}
<hr>
{#if y > 0}
<Child x={await delay2(x)} />
{/if}

@ -0,0 +1,11 @@
<script>
let { x } = $props();
</script>
<!-- checks direct source, indirect derived, block effect, async effect -->
{x}
{JSON.stringify(x)}
{#if x === 'universe'}universe{:else}world{/if}
{#if JSON.stringify(x) === '"universe"'}universe{:else}world{/if}
{await Promise.resolve(x)}
{await Promise.resolve(JSON.stringify(x))}

@ -0,0 +1,71 @@
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, shift, pop, commit] = target.querySelectorAll('button');
y.click();
await tick();
x.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
<hr>
`
);
commit.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
<hr>
`
);
shift.click();
await tick();
shift.click();
await tick();
shift.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
universe
universe
"universe"
universe
universe
universe
"universe"
<hr>
universe
"universe"
universe
universe
universe
"universe"
`
);
}
});

@ -0,0 +1,36 @@
<script>
import { fork } from 'svelte';
import Child from './Child.svelte';
let x = $state('world');
let y = $state(0);
let f;
const deferred = [];
function delay(value) {
if (value !== 'universe') return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
function delay2(value) {
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<button onclick={() => (f = fork(() => {x = 'universe';}))}>x</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>
<button onclick={() => f.commit()}>commit</button>
{#if x === 'universe'}
{await delay(x)}
<Child {x} />
{/if}
<hr>
{#if y > 0}
<Child x={await delay2(x)} />
{/if}

@ -0,0 +1,11 @@
<script>
let { x } = $props();
</script>
<!-- checks direct source, indirect derived, block effect, async effect -->
{x}
{JSON.stringify(x)}
{#if x === 'universe'}universe{:else}world{/if}
{#if JSON.stringify(x) === '"universe"'}universe{:else}world{/if}
{await Promise.resolve(x)}
{await Promise.resolve(JSON.stringify(x))}

@ -0,0 +1,70 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
const [x, y, shift, pop, commit] = target.querySelectorAll('button');
y.click();
await tick();
x.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
<hr>
`
);
commit.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
<hr>
`
);
pop.click();
await tick();
pop.click();
await tick();
pop.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>shift</button>
<button>pop</button>
<button>commit</button>
universe
universe
"universe"
universe
universe
universe
"universe"
<hr>
universe
"universe"
universe
universe
universe
"universe"
`
);
}
});

@ -0,0 +1,36 @@
<script>
import { fork } from 'svelte';
import Child from './Child.svelte';
let x = $state('world');
let y = $state(0);
let f;
const deferred = [];
function delay(value) {
if (value !== 'universe') return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
function delay2(value) {
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<button onclick={() => (f = fork(() => {x = 'universe';}))}>x</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => deferred.shift()?.()}>shift</button>
<button onclick={() => deferred.pop()?.()}>pop</button>
<button onclick={() => f.commit()}>commit</button>
{#if x === 'universe'}
{await delay(x)}
<Child {x} />
{/if}
<hr>
{#if y > 0}
<Child x={await delay2(x)} />
{/if}

@ -0,0 +1,11 @@
<script>
let { x } = $props();
</script>
<!-- checks direct source, indirect derived, block effect, async effect -->
{x}
{JSON.stringify(x)}
{#if x === 'universe'}universe{:else}world{/if}
{#if JSON.stringify(x) === '"universe"'}universe{:else}world{/if}
{await Promise.resolve(x)}
{await Promise.resolve(JSON.stringify(x))}

@ -0,0 +1,76 @@
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');
x.click();
await tick();
y.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<button>commit</button>
<hr>
world
"world"
world
world
world
"world"
`
);
commit.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<button>commit</button>
<hr>
world
"world"
world
world
world
"world"
`
);
resolve.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<button>commit</button>
universe
universe
"universe"
universe
universe
universe
"universe"
<hr>
universe
"universe"
universe
universe
universe
"universe"
`
);
}
});

@ -0,0 +1,32 @@
<script>
import { fork } from 'svelte';
import Child from './Child.svelte';
let x = $state('world');
let y = $state(0);
let f;
const deferred = [];
function delay(value) {
if (value !== 'universe') return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<button onclick={() => (f = fork(() => {x = 'universe';}))}>x</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => deferred.pop()?.()}>resolve</button>
<button onclick={() => f.commit()}>commit</button>
{#if x === 'universe'}
{await delay(x)}
<Child {x} />
{/if}
<hr>
{#if y > 0}
<Child {x} />
{/if}

@ -0,0 +1,11 @@
<script>
let { x } = $props();
</script>
<!-- checks direct source, indirect derived, block effect, async effect -->
{x}
{JSON.stringify(x)}
{#if x === 'universe'}universe{:else}world{/if}
{#if JSON.stringify(x) === '"universe"'}universe{:else}world{/if}
{await Promise.resolve(x)}
{await Promise.resolve(JSON.stringify(x))}

@ -0,0 +1,85 @@
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');
x.click();
await tick();
y.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<button>commit</button>
<hr>
`
);
commit.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<button>commit</button>
<hr>
`
);
resolve.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<button>commit</button>
<hr>
world
"world"
world
world
world
"world"
`
);
resolve.click();
await tick();
resolve.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`
<button>x</button>
<button>y++</button>
<button>resolve</button>
<button>commit</button>
universe
universe
"universe"
universe
universe
universe
"universe"
<hr>
universe
"universe"
universe
universe
universe
"universe"
`
);
}
});

@ -0,0 +1,36 @@
<script>
import { fork } from 'svelte';
import Child from './Child.svelte';
let x = $state('world');
let y = $state(0);
let f;
const deferred = [];
function delay(value) {
if (value !== 'universe') return value;
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
function delay2(value) {
return new Promise((resolve) => deferred.push(() => resolve(value)));
}
</script>
<button onclick={() => (f = fork(() => {x = 'universe';}))}>x</button>
<button onclick={() => y++}>y++</button>
<button onclick={() => deferred.pop()?.()}>resolve</button>
<button onclick={() => f.commit()}>commit</button>
{#if x === 'universe'}
{await delay(x)}
<Child {x} />
{/if}
<hr>
{#if y > 0}
<Child x={await delay2(x)} />
{/if}

@ -4,6 +4,7 @@ import { it } from 'vitest';
export interface BaseTest {
skip?: boolean;
solo?: boolean;
timeout?: number;
}
/**
@ -30,7 +31,7 @@ export function suite<Test extends BaseTest>(fn: (config: Test, test_dir: string
await for_each_dir<Test>(cwd, samples_dir, (config, dir) => {
let it_fn = config.skip ? it.skip : config.solo ? it.only : it;
it_fn(dir, () => fn(config, `${cwd}/${samples_dir}/${dir}`));
it_fn(dir, { timeout: config.timeout }, () => fn(config, `${cwd}/${samples_dir}/${dir}`));
});
}
};
@ -57,7 +58,7 @@ export function suite_with_variants<Test extends BaseTest, Variants extends stri
const solo = config.solo;
let it_fn = skip ? it.skip : solo ? it.only : it;
it_fn(`${dir} (${variant})`, async () => {
it_fn(`${dir} (${variant})`, { timeout: config.timeout }, async () => {
if (!called_common) {
called_common = true;
common = await common_setup(config, `${cwd}/${samples_dir}/${dir}`);

Loading…
Cancel
Save