mirror of https://github.com/sveltejs/svelte
parent
7018c09247
commit
18219ad1ff
@ -0,0 +1,70 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
/** @param {number} n */
|
||||
const html = (n) => `<p>c</p><section>${n}|${n}</section>`;
|
||||
|
||||
// Branches with async expressions (with and without dependencies) that fork `a` created are revealed
|
||||
// by another batch (a real one, or fork `b`), before or after `a`'s results are in. The revealing batch
|
||||
// has to run the async expressions itself, since `a`'s results only exist in `a`. When `a` is discarded
|
||||
// the other batch's results are shown; when it's committed, its own results are shown (nothing is fetched
|
||||
// again), even though the other batch's results come in later.
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
async test({ assert, target, instance }) {
|
||||
const [fork_a, show, fork_b, reset, resolve_all, discard_a, commit_a, commit_b] =
|
||||
target.querySelectorAll('button');
|
||||
const [output] = target.querySelectorAll('main');
|
||||
|
||||
const settle = async () => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await tick();
|
||||
resolve_all.click();
|
||||
}
|
||||
await tick();
|
||||
};
|
||||
|
||||
for (const revealer of ['real', 'fork']) {
|
||||
for (const finish of ['discard', 'commit']) {
|
||||
for (const resolved of [true, false]) {
|
||||
const mode = `${revealer} reveals, a ${finish}ed, a's results ${resolved ? 'resolved' : 'pending'}`;
|
||||
|
||||
try {
|
||||
fork_a.click();
|
||||
if (resolved) {
|
||||
resolve_all.click();
|
||||
await tick();
|
||||
}
|
||||
assert.deepEqual(instance.log.splice(0), ['cc', 'n1']);
|
||||
|
||||
if (revealer === 'real') show.click();
|
||||
else fork_b.click();
|
||||
await tick();
|
||||
assert.htmlEqual(output.innerHTML, '');
|
||||
assert.deepEqual(instance.log.splice(0), ['cc', 'n0']);
|
||||
|
||||
if (finish === 'discard') discard_a.click();
|
||||
else commit_a.click();
|
||||
await settle();
|
||||
assert.htmlEqual(
|
||||
output.innerHTML,
|
||||
finish === 'commit' ? html(1) : revealer === 'real' ? html(0) : ''
|
||||
);
|
||||
|
||||
commit_b.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html(finish === 'commit' ? 1 : 0));
|
||||
assert.deepEqual(instance.log, []);
|
||||
|
||||
reset.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, '');
|
||||
} catch (e) {
|
||||
/** @type {Error} */ (e).message = `${mode}: ${/** @type {Error} */ (e).message}`;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,53 @@
|
||||
<script>
|
||||
import { fork } from 'svelte';
|
||||
|
||||
let show = $state(false);
|
||||
let n = $state(0);
|
||||
export const log = [];
|
||||
const registry = [];
|
||||
let a;
|
||||
let b;
|
||||
|
||||
function f(tag, value) {
|
||||
log.push(`${tag}${value}`);
|
||||
return new Promise((resolve) => registry.push(() => resolve(value)));
|
||||
}
|
||||
|
||||
function set(values) {
|
||||
if ('show' in values) show = values.show;
|
||||
if ('n' in values) n = values.n;
|
||||
}
|
||||
|
||||
function resolve_all() {
|
||||
for (const r of registry.splice(0)) r();
|
||||
}
|
||||
|
||||
function discard_a() {
|
||||
a?.discard();
|
||||
a = undefined;
|
||||
}
|
||||
|
||||
function commit_a() {
|
||||
a?.commit();
|
||||
a = undefined;
|
||||
}
|
||||
|
||||
function commit_b() {
|
||||
b?.commit();
|
||||
b = undefined;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => (a = fork(() => set({ show: true, n: 1 })))}>fork a</button>
|
||||
<button onclick={() => set({ show: true })}>show</button>
|
||||
<button onclick={() => (b = fork(() => set({ show: true })))}>fork b</button>
|
||||
<button onclick={() => set({ show: false, n: 0 })}>reset</button>
|
||||
<button onclick={resolve_all}>resolve all</button>
|
||||
<button onclick={discard_a}>discard a</button>
|
||||
<button onclick={commit_a}>commit a</button>
|
||||
<button onclick={commit_b}>commit b</button>
|
||||
|
||||
<main>
|
||||
{#if show}<p>{await f('c', 'c')}</p>{/if}
|
||||
{#if show}<section>{n}|{await f('n', n)}</section>{/if}
|
||||
</main>
|
||||
@ -0,0 +1,33 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
async test({ assert, target }) {
|
||||
const [fork_a, fork_b, resolve_one, resolve_all, discard_a, commit_b] =
|
||||
target.querySelectorAll('button');
|
||||
const [output] = target.querySelectorAll('main');
|
||||
|
||||
fork_a.click();
|
||||
await tick();
|
||||
resolve_one.click();
|
||||
await tick();
|
||||
fork_b.click();
|
||||
await tick();
|
||||
resolve_one.click();
|
||||
await tick();
|
||||
discard_a.click();
|
||||
await tick();
|
||||
resolve_one.click();
|
||||
await tick();
|
||||
commit_b.click();
|
||||
await tick();
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
resolve_all.click();
|
||||
await tick();
|
||||
}
|
||||
|
||||
assert.htmlEqual(output.innerHTML, '<section>s</section>');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,23 @@
|
||||
<script>
|
||||
import { fork } from 'svelte';
|
||||
|
||||
let show = $state(false);
|
||||
const registry = [];
|
||||
let a;
|
||||
let b;
|
||||
|
||||
function f(value) {
|
||||
return new Promise((resolve) => registry.push(() => resolve(value)));
|
||||
}
|
||||
|
||||
function resolve_all() { for (const r of registry.splice(0)) r(); }
|
||||
</script>
|
||||
|
||||
<button onclick={() => (a = fork(() => (show = true)))}>fork a</button>
|
||||
<button onclick={() => (b = fork(() => (show = true)))}>fork b</button>
|
||||
<button onclick={() => registry.shift()?.()}>resolve one</button>
|
||||
<button onclick={resolve_all}>resolve all</button>
|
||||
<button onclick={() => a?.discard()}>discard a</button>
|
||||
<button onclick={() => b?.commit()}>commit b</button>
|
||||
|
||||
<main>{#if show}<section>{await f('s')}</section>{/if}</main>
|
||||
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
let { f, n } = $props();
|
||||
let x = $derived(await f(n));
|
||||
</script>
|
||||
|
||||
<p>{n}|{x}</p>
|
||||
@ -0,0 +1,53 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
// A component with a top-level `await` inside a branch that a fork created is revealed by a real batch
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
async test({ assert, target, instance }) {
|
||||
const [fork_a, show, reset, resolve_all, discard, commit] = target.querySelectorAll('button');
|
||||
const [output] = target.querySelectorAll('main');
|
||||
|
||||
const settle = async () => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await tick();
|
||||
resolve_all.click();
|
||||
}
|
||||
await tick();
|
||||
};
|
||||
|
||||
for (const finish of ['discard', 'commit']) {
|
||||
for (const resolved of [true, false]) {
|
||||
const mode = `${finish}, ${resolved ? 'resolved' : 'pending'}`;
|
||||
|
||||
try {
|
||||
fork_a.click();
|
||||
await tick();
|
||||
if (resolved) {
|
||||
resolve_all.click();
|
||||
await tick();
|
||||
}
|
||||
assert.deepEqual(instance.log.splice(0), [1]);
|
||||
|
||||
show.click();
|
||||
await tick();
|
||||
assert.deepEqual(instance.log.splice(0), [0]);
|
||||
|
||||
if (finish === 'discard') discard.click();
|
||||
else commit.click();
|
||||
await settle();
|
||||
const n = finish === 'commit' ? 1 : 0;
|
||||
assert.htmlEqual(output.innerHTML, `<p>${n}|${n}</p>`);
|
||||
assert.deepEqual(instance.log, []);
|
||||
|
||||
reset.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, '');
|
||||
} catch (e) {
|
||||
/** @type {Error} */ (e).message = `${mode}: ${/** @type {Error} */ (e).message}`;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,33 @@
|
||||
<script>
|
||||
import { fork } from 'svelte';
|
||||
import Child from './Child.svelte';
|
||||
|
||||
let show = $state(false);
|
||||
let n = $state(0);
|
||||
export const log = [];
|
||||
const registry = [];
|
||||
let a;
|
||||
|
||||
function f(value) {
|
||||
log.push(value);
|
||||
return new Promise((resolve) => registry.push(() => resolve(value)));
|
||||
}
|
||||
|
||||
function set(values) {
|
||||
if ('show' in values) show = values.show;
|
||||
if ('n' in values) n = values.n;
|
||||
}
|
||||
|
||||
function resolve_all() {
|
||||
for (const r of registry.splice(0)) r();
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => (a = fork(() => set({ show: true, n: 1 })))}>fork</button>
|
||||
<button onclick={() => set({ show: true })}>show</button>
|
||||
<button onclick={() => set({ show: false, n: 0 })}>reset</button>
|
||||
<button onclick={resolve_all}>resolve all</button>
|
||||
<button onclick={() => a?.discard()}>discard</button>
|
||||
<button onclick={() => a?.commit()}>commit</button>
|
||||
|
||||
<main>{#if show}<Child {f} {n} />{/if}</main>
|
||||
@ -0,0 +1,53 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
// The fallback of an each block, created by a fork, is adopted by a real batch: it has to run the
|
||||
// fallback's async expression itself, since the fork's result only exists in the fork
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
async test({ assert, target, instance }) {
|
||||
const [fork_empty, empty, reset, resolve_all, discard, commit] =
|
||||
target.querySelectorAll('button');
|
||||
const [output] = target.querySelectorAll('main');
|
||||
|
||||
const settle = async () => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await tick();
|
||||
resolve_all.click();
|
||||
}
|
||||
await tick();
|
||||
};
|
||||
|
||||
for (const finish of ['discard', 'commit']) {
|
||||
for (const resolved of [true, false]) {
|
||||
const mode = `${finish}, ${resolved ? 'resolved' : 'pending'}`;
|
||||
|
||||
try {
|
||||
fork_empty.click();
|
||||
if (resolved) {
|
||||
resolve_all.click();
|
||||
await tick();
|
||||
}
|
||||
assert.deepEqual(instance.log.splice(0), ['empty']);
|
||||
|
||||
empty.click();
|
||||
await tick();
|
||||
assert.deepEqual(instance.log.splice(0), ['empty']);
|
||||
|
||||
if (finish === 'discard') discard.click();
|
||||
else commit.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, '<p>empty</p>');
|
||||
assert.deepEqual(instance.log, []);
|
||||
|
||||
reset.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, '<b>0</b>');
|
||||
} catch (e) {
|
||||
/** @type {Error} */ (e).message = `${mode}: ${/** @type {Error} */ (e).message}`;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,26 @@
|
||||
<script>
|
||||
import { fork } from 'svelte';
|
||||
|
||||
let list = $state([0]);
|
||||
export const log = [];
|
||||
const registry = [];
|
||||
let current_fork;
|
||||
|
||||
function f(value) {
|
||||
log.push(value);
|
||||
return new Promise((resolve) => registry.push(() => resolve(value)));
|
||||
}
|
||||
|
||||
function resolve_all() {
|
||||
for (const r of registry.splice(0)) r();
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => (current_fork = fork(() => (list = [])))}>fork empty</button>
|
||||
<button onclick={() => (list = [])}>empty</button>
|
||||
<button onclick={() => (list = [0])}>reset</button>
|
||||
<button onclick={resolve_all}>resolve all</button>
|
||||
<button onclick={() => current_fork?.discard()}>discard</button>
|
||||
<button onclick={() => current_fork?.commit()}>commit</button>
|
||||
|
||||
<main>{#each list as x (x)}<b>{x}</b>{:else}<p>{await f('empty')}</p>{/each}</main>
|
||||
@ -0,0 +1,128 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
/** @param {number[]} list */
|
||||
const html = (list) =>
|
||||
list.map((x) => `<b>${x}|${x}|c</b>`).join('') + list.map((x) => `<s>${x}|${x}</s>`).join('');
|
||||
|
||||
// Items of keyed and unkeyed each blocks that were created by a fork (and whose async expressions
|
||||
// therefore only have results in that fork) are adopted by a real batch. The real batch has to run
|
||||
// their async expressions itself — with dependencies (unkeyed `x`) or without (keyed `x` is a plain
|
||||
// value, `'c'` is a constant) — whether the fork is discarded or committed afterwards. When the
|
||||
// fork is committed, its own results are used: nothing is fetched again.
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
async test({ assert, target, instance }) {
|
||||
const [
|
||||
reset_list,
|
||||
fork_list,
|
||||
real_list,
|
||||
fork_append_2,
|
||||
append_2,
|
||||
append_5,
|
||||
resolve_all,
|
||||
discard,
|
||||
commit
|
||||
] = target.querySelectorAll('button');
|
||||
const [output] = target.querySelectorAll('main');
|
||||
|
||||
const settle = async () => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await tick();
|
||||
resolve_all.click();
|
||||
}
|
||||
await tick();
|
||||
};
|
||||
|
||||
const reset = async () => {
|
||||
reset_list.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1]));
|
||||
instance.log.length = 0;
|
||||
};
|
||||
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1]));
|
||||
instance.log.length = 0;
|
||||
|
||||
for (const finish of ['discard', 'commit']) {
|
||||
try {
|
||||
// the fork creates items, the real batch reuses some of them (with equal and different values)
|
||||
fork_list.click();
|
||||
assert.deepEqual(
|
||||
instance.log.sort(),
|
||||
['cc', 'cc', 'k3', 'k2', 'u0', 'u1', 'u2', 'u3'].sort()
|
||||
);
|
||||
instance.log.length = 0;
|
||||
|
||||
real_list.click();
|
||||
if (finish === 'discard') discard.click();
|
||||
else commit.click();
|
||||
await settle();
|
||||
// the real write came last, so it wins either way
|
||||
assert.htmlEqual(output.innerHTML, html([3, 0, 1]));
|
||||
await reset();
|
||||
|
||||
// the fork's results are already in when the real batch adopts its items with the same values
|
||||
fork_append_2.click();
|
||||
resolve_all.click();
|
||||
await tick();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1]));
|
||||
instance.log.length = 0;
|
||||
|
||||
append_2.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1, 2]));
|
||||
// the fork's results only exist in the fork, so the real batch has to fetch them itself
|
||||
assert.deepEqual(instance.log.sort(), ['cc', 'k2', 'u2']);
|
||||
instance.log.length = 0;
|
||||
|
||||
if (finish === 'discard') discard.click();
|
||||
else commit.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1, 2]));
|
||||
assert.deepEqual(instance.log, []);
|
||||
await reset();
|
||||
|
||||
// the fork is finished while the real batch that adopted its items is still pending
|
||||
fork_append_2.click();
|
||||
instance.log.length = 0;
|
||||
append_2.click();
|
||||
await tick();
|
||||
assert.deepEqual(instance.log.sort(), ['cc', 'k2', 'u2']);
|
||||
instance.log.length = 0;
|
||||
|
||||
if (finish === 'discard') discard.click();
|
||||
else commit.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1, 2]));
|
||||
assert.deepEqual(instance.log, []);
|
||||
await reset();
|
||||
|
||||
// a real batch adopts an item with a different value after the fork's results are in
|
||||
// (TODO also commit the fork here, once the fork re-running the unkeyed item's expression
|
||||
// with the real world's new value no longer keeps the fork's outdated result)
|
||||
if (finish === 'discard') {
|
||||
fork_append_2.click();
|
||||
resolve_all.click();
|
||||
await tick();
|
||||
instance.log.length = 0;
|
||||
|
||||
append_5.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1, 5]));
|
||||
// keyed: a new item, unkeyed: the adopted item's input changed (and the fork revalidates it)
|
||||
assert.deepEqual(instance.log.sort(), ['cc', 'k5', 'u5', 'u5']);
|
||||
|
||||
discard.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1, 5]));
|
||||
await reset();
|
||||
}
|
||||
} catch (e) {
|
||||
/** @type {Error} */ (e).message = `${finish}: ${/** @type {Error} */ (e).message}`;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,35 @@
|
||||
<script>
|
||||
import { fork } from 'svelte';
|
||||
|
||||
let list = $state([0, 1]);
|
||||
export const log = [];
|
||||
const registry = [];
|
||||
let current_fork;
|
||||
|
||||
function f(tag, value) {
|
||||
log.push(`${tag}${value}`);
|
||||
return new Promise((resolve) => registry.push(() => resolve(value)));
|
||||
}
|
||||
|
||||
function resolve_all() {
|
||||
for (const r of registry.splice(0)) r();
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => (list = [0, 1])}>reset</button>
|
||||
<button onclick={() => (current_fork = fork(() => (list = [3, 2, 1, 0])))}>fork list</button>
|
||||
<button onclick={() => (list = [3, 0, 1])}>real list</button>
|
||||
<button onclick={() => (current_fork = fork(() => (list = [0, 1, 2])))}>fork append 2</button>
|
||||
<button onclick={() => (list = [0, 1, 2])}>append 2</button>
|
||||
<button onclick={() => (list = [0, 1, 5])}>append 5</button>
|
||||
<button onclick={resolve_all}>resolve all</button>
|
||||
<button onclick={() => current_fork?.discard()}>discard</button>
|
||||
<button onclick={() => current_fork?.commit()}>commit</button>
|
||||
|
||||
<main>
|
||||
<svelte:boundary>
|
||||
{#each list as x (x)}<b>{x}|{await f('k', x)}|{await f('c', 'c')}</b>{/each}
|
||||
{#each list as x}<s>{x}|{await f('u', x)}</s>{/each}
|
||||
{#snippet pending()}{/snippet}
|
||||
</svelte:boundary>
|
||||
</main>
|
||||
@ -0,0 +1,25 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
async test({ assert, target }) {
|
||||
const [fork_list, real_list, resolve_all, discard] = target.querySelectorAll('button');
|
||||
const [output] = target.querySelectorAll('main');
|
||||
|
||||
resolve_all.click();
|
||||
await tick();
|
||||
assert.htmlEqual(output.innerHTML, '<s>0|0</s><s>1|1</s>');
|
||||
|
||||
fork_list.click();
|
||||
real_list.click();
|
||||
discard.click();
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await tick();
|
||||
resolve_all.click();
|
||||
}
|
||||
await tick();
|
||||
assert.htmlEqual(output.innerHTML, '<s>3|3</s><s>0|0</s><s>1|1</s>');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,25 @@
|
||||
<script>
|
||||
import { fork } from 'svelte';
|
||||
|
||||
let list = $state([0, 1]);
|
||||
const registry = [];
|
||||
let current_fork;
|
||||
|
||||
function f(value) {
|
||||
return new Promise((resolve) => registry.push(() => resolve(value)));
|
||||
}
|
||||
|
||||
function resolve_all() { for (const r of registry.splice(0)) r(); }
|
||||
</script>
|
||||
|
||||
<button onclick={() => (current_fork = fork(() => (list = [3, 2, 1, 0])))}>fork list</button>
|
||||
<button onclick={() => (list = [3, 0, 1])}>real list</button>
|
||||
<button onclick={resolve_all}>resolve all</button>
|
||||
<button onclick={() => current_fork?.discard()}>discard</button>
|
||||
|
||||
<main>
|
||||
<svelte:boundary>
|
||||
{#each list as x}<s>{x}|{await f(x)}</s>{/each}
|
||||
{#snippet pending()}{/snippet}
|
||||
</svelte:boundary>
|
||||
</main>
|
||||
@ -0,0 +1,78 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
/**
|
||||
* @param {number[]} list
|
||||
* @param {boolean} show
|
||||
*/
|
||||
const html = (list, show) =>
|
||||
list.map((x) => `<div>${x}|${x}${show ? `<p>${x}</p>` : ''}</div>`).join('');
|
||||
|
||||
// A fork creates an each item, and branches inside of it (and inside an existing item) that contain
|
||||
// async expressions. A real batch adopts the item first and the branches later, or both at once.
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
async test({ assert, target, instance }) {
|
||||
const [fork_a, list_and_show, list, show, reset, resolve_all, discard, commit] =
|
||||
target.querySelectorAll('button');
|
||||
const [output] = target.querySelectorAll('main');
|
||||
|
||||
const settle = async () => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await tick();
|
||||
resolve_all.click();
|
||||
}
|
||||
await tick();
|
||||
};
|
||||
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0], false));
|
||||
instance.log.length = 0;
|
||||
|
||||
for (const together of [false, true]) {
|
||||
for (const finish of ['discard', 'commit']) {
|
||||
const mode = `${together ? 'together' : 'separately'}, ${finish}`;
|
||||
|
||||
try {
|
||||
fork_a.click();
|
||||
resolve_all.click();
|
||||
await tick();
|
||||
assert.deepEqual(instance.log.splice(0).sort(), ['p0', 'p1', 'x1']);
|
||||
|
||||
if (!together) {
|
||||
// the item is adopted, the branch inside it stays the fork's
|
||||
list.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1], false));
|
||||
assert.deepEqual(instance.log.splice(0), ['x1']);
|
||||
|
||||
show.click();
|
||||
} else {
|
||||
list_and_show.click();
|
||||
}
|
||||
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1], true));
|
||||
assert.deepEqual(
|
||||
instance.log.splice(0).sort(),
|
||||
together ? ['p0', 'p1', 'x1'] : ['p0', 'p1']
|
||||
);
|
||||
|
||||
if (finish === 'discard') discard.click();
|
||||
else commit.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0, 1], true));
|
||||
assert.deepEqual(instance.log, []);
|
||||
|
||||
reset.click();
|
||||
await settle();
|
||||
assert.htmlEqual(output.innerHTML, html([0], false));
|
||||
instance.log.length = 0;
|
||||
} catch (e) {
|
||||
/** @type {Error} */ (e).message = `${mode}: ${/** @type {Error} */ (e).message}`;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,41 @@
|
||||
<script>
|
||||
import { fork } from 'svelte';
|
||||
|
||||
let list = $state([0]);
|
||||
let show = $state(false);
|
||||
export const log = [];
|
||||
const registry = [];
|
||||
let a;
|
||||
|
||||
function f(tag, value) {
|
||||
log.push(`${tag}${value}`);
|
||||
return new Promise((resolve) => registry.push(() => resolve(value)));
|
||||
}
|
||||
|
||||
function set(values) {
|
||||
if ('list' in values) list = values.list;
|
||||
if ('show' in values) show = values.show;
|
||||
}
|
||||
|
||||
function resolve_all() {
|
||||
for (const r of registry.splice(0)) r();
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => (a = fork(() => set({ list: [0, 1], show: true })))}>fork</button>
|
||||
<button onclick={() => set({ list: [0, 1], show: true })}>list and show</button>
|
||||
<button onclick={() => set({ list: [0, 1] })}>list</button>
|
||||
<button onclick={() => set({ show: true })}>show</button>
|
||||
<button onclick={() => set({ list: [0], show: false })}>reset</button>
|
||||
<button onclick={resolve_all}>resolve all</button>
|
||||
<button onclick={() => a?.discard()}>discard</button>
|
||||
<button onclick={() => a?.commit()}>commit</button>
|
||||
|
||||
<main>
|
||||
<svelte:boundary>
|
||||
{#each list as x (x)}
|
||||
<div>{x}|{await f('x', x)}{#if show}<p>{await f('p', x)}</p>{/if}</div>
|
||||
{/each}
|
||||
{#snippet pending()}{/snippet}
|
||||
</svelte:boundary>
|
||||
</main>
|
||||
Loading…
Reference in new issue