@loadbare/app 0.5.6 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/dist/build/assemble.d.ts +1 -1
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +81 -7
- package/dist/build/assemble.js.map +1 -0
- package/dist/build/cli.d.ts +2 -2
- package/dist/build/cli.js +3 -2
- package/dist/build/cli.js.map +1 -0
- package/dist/build/elements.js +1 -0
- package/dist/build/elements.js.map +1 -0
- package/dist/build/expand.d.ts.map +1 -1
- package/dist/build/expand.js +20 -32
- package/dist/build/expand.js.map +1 -0
- package/dist/build/format.js +1 -0
- package/dist/build/format.js.map +1 -0
- package/dist/build/locations.d.ts +4 -4
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +16 -5
- package/dist/build/locations.js.map +1 -0
- package/dist/build/origins.d.ts +0 -13
- package/dist/build/origins.d.ts.map +1 -1
- package/dist/build/origins.js +33 -8
- package/dist/build/origins.js.map +1 -0
- package/dist/build/package-root.js +1 -0
- package/dist/build/package-root.js.map +1 -0
- package/dist/build/pages.d.ts +7 -3
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +14 -7
- package/dist/build/pages.js.map +1 -0
- package/dist/build/styles.js +1 -0
- package/dist/build/styles.js.map +1 -0
- package/dist/core/lb-constants.d.ts +15 -13
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +104 -53
- package/dist/core/lb-constants.js.map +1 -0
- package/dist/core/lb-types.d.ts +103 -62
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +12 -3
- package/dist/core/lb-types.js.map +1 -0
- package/dist/hub/lb-apply.d.ts +28 -4
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +227 -40
- package/dist/hub/lb-apply.js.map +1 -0
- package/dist/hub/lb-hub.browser.d.ts +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +165 -110
- package/dist/hub/lb-hub.browser.js.map +1 -0
- package/dist/server/lb-express.d.ts +8 -5
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +46 -33
- package/dist/server/lb-express.js.map +1 -0
- package/dist/server/lb-server.d.ts +67 -45
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +56 -18
- package/dist/server/lb-server.js.map +1 -0
- package/docs/TECHREF-1.0.md +1107 -0
- package/docs/analysis-accidental-complexity.md +149 -0
- package/docs/reference/builder.md +4 -4
- package/docs/reference/chrome.md +10 -9
- package/docs/reference/custom-elements.md +54 -46
- package/docs/reference/data-binding.md +179 -97
- package/docs/reference/overview.md +1 -1
- package/docs/reference/page-files.md +64 -49
- package/docs/reference/server.md +25 -6
- package/docs/reference/widgets.md +22 -30
- package/docs/roadmap.md +68 -22
- package/docs/testing.md +47 -17
- package/docs/theory.md +116 -3
- package/docs/tutorials/010-pages-and-navigation.md +8 -8
- package/docs/tutorials/040-displaying-data.md +9 -9
- package/docs/tutorials/050-actions.md +5 -5
- package/docs/tutorials/060-custom-element-code.md +1 -1
- package/docs/tutorials/065-conditional-rendering.md +4 -4
- package/docs/tutorials/070-displaying-a-list.md +24 -47
- package/docs/tutorials/072-inserting-into-a-list.md +18 -15
- package/docs/tutorials/074-deleting-from-a-list.md +15 -17
- package/docs/tutorials/076-updating-a-list-item.md +20 -22
- package/docs/tutorials/080-widget-requests.md +22 -35
- package/docs/tutorials/090-using-widget-libraries.md +1 -1
- package/package.json +2 -3
- package/dist/hub/lb-rows.d.ts +0 -18
- package/dist/hub/lb-rows.d.ts.map +0 -1
- package/dist/hub/lb-rows.js +0 -106
- package/dist/tests/assemble.test.d.ts +0 -8
- package/dist/tests/assemble.test.d.ts.map +0 -1
- package/dist/tests/assemble.test.js +0 -58
- package/dist/tests/elements.test.d.ts +0 -8
- package/dist/tests/elements.test.d.ts.map +0 -1
- package/dist/tests/elements.test.js +0 -118
- package/dist/tests/expand.test.d.ts +0 -10
- package/dist/tests/expand.test.d.ts.map +0 -1
- package/dist/tests/expand.test.js +0 -250
- package/dist/tests/fixtures/elements/collision/imports.d.ts +0 -3
- package/dist/tests/fixtures/elements/collision/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/collision/imports.js +0 -1
- package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts +0 -2
- package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.js +0 -1
- package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts +0 -2
- package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/local/widgets/app-box.browser.js +0 -1
- package/dist/tests/fixtures/elements/manifest/imports.d.ts +0 -3
- package/dist/tests/fixtures/elements/manifest/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/manifest/imports.js +0 -1
- package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts +0 -3
- package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/manifest-bad-entry/imports.js +0 -1
- package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts +0 -5
- package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/manifest-not-array/imports.js +0 -1
- package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts +0 -2
- package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/pkg/acme-widget.browser.js +0 -1
- package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts +0 -6
- package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/unmarked/widgets/app-box.js +0 -1
- package/dist/tests/helpers/console.d.ts +0 -20
- package/dist/tests/helpers/console.d.ts.map +0 -1
- package/dist/tests/helpers/console.js +0 -28
- package/dist/tests/helpers/dom.d.ts +0 -18
- package/dist/tests/helpers/dom.d.ts.map +0 -1
- package/dist/tests/helpers/dom.js +0 -22
- package/dist/tests/lb-apply.test.d.ts +0 -8
- package/dist/tests/lb-apply.test.d.ts.map +0 -1
- package/dist/tests/lb-apply.test.js +0 -153
- package/dist/tests/lb-express.test.d.ts +0 -14
- package/dist/tests/lb-express.test.d.ts.map +0 -1
- package/dist/tests/lb-express.test.js +0 -238
- package/dist/tests/lb-rows.test.d.ts +0 -12
- package/dist/tests/lb-rows.test.d.ts.map +0 -1
- package/dist/tests/lb-rows.test.js +0 -336
- package/dist/tests/lb-server.test.d.ts +0 -9
- package/dist/tests/lb-server.test.d.ts.map +0 -1
- package/dist/tests/lb-server.test.js +0 -495
- package/dist/tests/origins.test.d.ts +0 -10
- package/dist/tests/origins.test.d.ts.map +0 -1
- package/dist/tests/origins.test.js +0 -369
- package/dist/tests/pages.test.d.ts +0 -6
- package/dist/tests/pages.test.d.ts.map +0 -1
- package/dist/tests/pages.test.js +0 -98
- package/dist/tests/styles.test.d.ts +0 -7
- package/dist/tests/styles.test.d.ts.map +0 -1
- package/dist/tests/styles.test.js +0 -76
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The row machinery — see docs/testing.md, tier 3.
|
|
3
|
-
*
|
|
4
|
-
* This is the most intricate code in the framework and the most likely to
|
|
5
|
-
* break in ways nobody notices, because what it gets wrong is a row in the
|
|
6
|
-
* wrong place rather than an exception.
|
|
7
|
-
*
|
|
8
|
-
* The last describe holds the two properties. Reconciliation's contract is
|
|
9
|
-
* convergence, and two properties state it better than twenty examples.
|
|
10
|
-
*/
|
|
11
|
-
import assert from "node:assert/strict";
|
|
12
|
-
import { describe, it } from "node:test";
|
|
13
|
-
import { applyRows } from "../hub/lb-rows";
|
|
14
|
-
import { body } from "./helpers/dom";
|
|
15
|
-
import { captured } from "./helpers/console";
|
|
16
|
-
/**
|
|
17
|
-
* A list widget's subtree: the template a page author wrote, and nothing
|
|
18
|
-
* else. The default `place` puts rows in front of the template, so the
|
|
19
|
-
* template staying put is part of what is under test.
|
|
20
|
-
*/
|
|
21
|
-
function list(template = `<template lb-key="id"><p><span lb-cell="name"></span></p></template>`) {
|
|
22
|
-
return body(`<lb-list>${template}</lb-list>`).firstElementChild;
|
|
23
|
-
}
|
|
24
|
-
/** The keys of the rows showing, in document order. */
|
|
25
|
-
function keys(scope) {
|
|
26
|
-
return [...scope.querySelectorAll(`[lb-key]`)]
|
|
27
|
-
.filter((el) => el.localName !== "template")
|
|
28
|
-
.map((el) => el.getAttribute("lb-key"));
|
|
29
|
-
}
|
|
30
|
-
/** What each row is showing, in document order. */
|
|
31
|
-
function shown(scope) {
|
|
32
|
-
return [...scope.querySelectorAll(`[lb-key]`)]
|
|
33
|
-
.filter((el) => el.localName !== "template")
|
|
34
|
-
.map((el) => el.textContent.trim());
|
|
35
|
-
}
|
|
36
|
-
const rows = (list) => ({ op: "rows", rows: list });
|
|
37
|
-
const patch = (change) => ({ op: "patch", ...change });
|
|
38
|
-
// --- The whole set -------------------------------------------------------
|
|
39
|
-
describe("a rows result", () => {
|
|
40
|
-
it("clones the template once per tuple and fills it", () => {
|
|
41
|
-
const scope = list();
|
|
42
|
-
applyRows(scope, rows([
|
|
43
|
-
{ id: "1", name: "Ada" },
|
|
44
|
-
{ id: "2", name: "Bab" },
|
|
45
|
-
]));
|
|
46
|
-
assert.deepEqual(keys(scope), ["1", "2"]);
|
|
47
|
-
assert.deepEqual(shown(scope), ["Ada", "Bab"]);
|
|
48
|
-
});
|
|
49
|
-
it("carries the key value on the live row", () => {
|
|
50
|
-
// lb-key names the key column on the template and carries the key value
|
|
51
|
-
// on a row. One name in two positions, never ambiguous, because a
|
|
52
|
-
// template is not a row.
|
|
53
|
-
const scope = list();
|
|
54
|
-
applyRows(scope, rows([{ id: "7", name: "Ada" }]));
|
|
55
|
-
const row = scope.querySelector("p");
|
|
56
|
-
assert.equal(row.getAttribute("lb-key"), "7");
|
|
57
|
-
});
|
|
58
|
-
it("leaves the template where it was, after the rows", () => {
|
|
59
|
-
const scope = list();
|
|
60
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]));
|
|
61
|
-
assert.equal(scope.lastElementChild.localName, "template");
|
|
62
|
-
});
|
|
63
|
-
it("decides membership: a key that did not arrive is gone", () => {
|
|
64
|
-
const scope = list();
|
|
65
|
-
applyRows(scope, rows([
|
|
66
|
-
{ id: "1", name: "Ada" },
|
|
67
|
-
{ id: "2", name: "Bab" },
|
|
68
|
-
]));
|
|
69
|
-
applyRows(scope, rows([{ id: "2", name: "Bab" }]));
|
|
70
|
-
assert.deepEqual(keys(scope), ["2"]);
|
|
71
|
-
});
|
|
72
|
-
it("decides order: a row that already exists is placed again", () => {
|
|
73
|
-
const scope = list();
|
|
74
|
-
applyRows(scope, rows([
|
|
75
|
-
{ id: "1", name: "Ada" },
|
|
76
|
-
{ id: "2", name: "Bab" },
|
|
77
|
-
]));
|
|
78
|
-
applyRows(scope, rows([
|
|
79
|
-
{ id: "2", name: "Bab" },
|
|
80
|
-
{ id: "1", name: "Ada" },
|
|
81
|
-
]));
|
|
82
|
-
assert.deepEqual(keys(scope), ["2", "1"]);
|
|
83
|
-
});
|
|
84
|
-
it("updates a row it keeps rather than replacing it", () => {
|
|
85
|
-
// The row is the same element, so anything a widget put on it survives.
|
|
86
|
-
const scope = list();
|
|
87
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]));
|
|
88
|
-
const before = scope.querySelector("p");
|
|
89
|
-
applyRows(scope, rows([{ id: "1", name: "Grace" }]));
|
|
90
|
-
assert.equal(scope.querySelector("p"), before, "same element");
|
|
91
|
-
assert.deepEqual(shown(scope), ["Grace"]);
|
|
92
|
-
});
|
|
93
|
-
it("empties the list when the set is empty", () => {
|
|
94
|
-
const scope = list();
|
|
95
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]));
|
|
96
|
-
applyRows(scope, rows([]));
|
|
97
|
-
assert.deepEqual(keys(scope), []);
|
|
98
|
-
assert.equal(scope.querySelector("template"), scope.firstElementChild);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
// --- Only what changed ---------------------------------------------------
|
|
102
|
-
describe("a patch result", () => {
|
|
103
|
-
it("adds a row it names", () => {
|
|
104
|
-
const scope = list();
|
|
105
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]));
|
|
106
|
-
applyRows(scope, patch({ rows: [{ id: "2", name: "Bab" }] }));
|
|
107
|
-
assert.deepEqual(keys(scope), ["1", "2"]);
|
|
108
|
-
});
|
|
109
|
-
it("updates a row it names", () => {
|
|
110
|
-
const scope = list();
|
|
111
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]));
|
|
112
|
-
applyRows(scope, patch({ rows: [{ id: "1", name: "Grace" }] }));
|
|
113
|
-
assert.deepEqual(shown(scope), ["Grace"]);
|
|
114
|
-
});
|
|
115
|
-
it("drops a row it names", () => {
|
|
116
|
-
const scope = list();
|
|
117
|
-
applyRows(scope, rows([
|
|
118
|
-
{ id: "1", name: "Ada" },
|
|
119
|
-
{ id: "2", name: "Bab" },
|
|
120
|
-
]));
|
|
121
|
-
applyRows(scope, patch({ drop: ["1"] }));
|
|
122
|
-
assert.deepEqual(keys(scope), ["2"]);
|
|
123
|
-
});
|
|
124
|
-
it("leaves a row it did not name, in contents and in place", () => {
|
|
125
|
-
const scope = list();
|
|
126
|
-
applyRows(scope, rows([
|
|
127
|
-
{ id: "1", name: "Ada" },
|
|
128
|
-
{ id: "2", name: "Bab" },
|
|
129
|
-
{ id: "3", name: "Cai" },
|
|
130
|
-
]));
|
|
131
|
-
applyRows(scope, patch({ rows: [{ id: "2", name: "Grace" }] }));
|
|
132
|
-
assert.deepEqual(keys(scope), ["1", "2", "3"]);
|
|
133
|
-
assert.deepEqual(shown(scope), ["Ada", "Grace", "Cai"]);
|
|
134
|
-
});
|
|
135
|
-
it("does not re-place a row that already exists", () => {
|
|
136
|
-
// Today's behavior, and exactly the gap NEEDED-FOR-1.0 names: a row whose
|
|
137
|
-
// sort column changed stays where it was. This test states what is true
|
|
138
|
-
// now, and is the one that flips when that item closes.
|
|
139
|
-
const placed = [];
|
|
140
|
-
const place = (row, _tuple, template) => {
|
|
141
|
-
placed.push(row.getAttribute("lb-key"));
|
|
142
|
-
template.parentElement.insertBefore(row, template);
|
|
143
|
-
};
|
|
144
|
-
const scope = list();
|
|
145
|
-
applyRows(scope, rows([
|
|
146
|
-
{ id: "1", name: "Ada" },
|
|
147
|
-
{ id: "2", name: "Bab" },
|
|
148
|
-
]), place);
|
|
149
|
-
placed.length = 0;
|
|
150
|
-
applyRows(scope, patch({ rows: [{ id: "1", name: "Grace" }] }), place);
|
|
151
|
-
assert.deepEqual(placed, [], "an existing row is filled, never placed");
|
|
152
|
-
});
|
|
153
|
-
it("places a row it has not seen before", () => {
|
|
154
|
-
const placed = [];
|
|
155
|
-
const place = (row, _tuple, template) => {
|
|
156
|
-
placed.push(row.getAttribute("lb-key"));
|
|
157
|
-
template.parentElement.insertBefore(row, template);
|
|
158
|
-
};
|
|
159
|
-
const scope = list();
|
|
160
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]), place);
|
|
161
|
-
placed.length = 0;
|
|
162
|
-
applyRows(scope, patch({ rows: [{ id: "2", name: "Bab" }] }), place);
|
|
163
|
-
assert.deepEqual(placed, ["2"]);
|
|
164
|
-
});
|
|
165
|
-
it("ignores a drop for a row that is not showing", () => {
|
|
166
|
-
const scope = list();
|
|
167
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]));
|
|
168
|
-
assert.doesNotThrow(() => applyRows(scope, patch({ drop: ["9"] })));
|
|
169
|
-
assert.deepEqual(keys(scope), ["1"]);
|
|
170
|
-
});
|
|
171
|
-
it("naming nothing changes nothing", () => {
|
|
172
|
-
const scope = list();
|
|
173
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]));
|
|
174
|
-
const before = scope.innerHTML;
|
|
175
|
-
applyRows(scope, patch({}));
|
|
176
|
-
assert.equal(scope.innerHTML, before);
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
// --- Placement -----------------------------------------------------------
|
|
180
|
-
describe("placement", () => {
|
|
181
|
-
it("hands the widget the row, the tuple and the template", () => {
|
|
182
|
-
const seen = [];
|
|
183
|
-
const place = (row, tuple, template) => {
|
|
184
|
-
seen.push([row.localName, tuple, template.localName]);
|
|
185
|
-
template.parentElement.insertBefore(row, template);
|
|
186
|
-
};
|
|
187
|
-
const scope = list();
|
|
188
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]), place);
|
|
189
|
-
assert.deepEqual(seen, [["p", { id: "1", name: "Ada" }, "template"]]);
|
|
190
|
-
});
|
|
191
|
-
it("fills the row before the widget places it", () => {
|
|
192
|
-
// The attributes are already there when the row upgrades, which is the
|
|
193
|
-
// same thing that makes hydration and refresh one operation everywhere
|
|
194
|
-
// else. A widget that reads a cell to decide where the row goes — every
|
|
195
|
-
// sorting or grouping widget — depends on this ordering.
|
|
196
|
-
let atPlacement = "";
|
|
197
|
-
const place = (row, _tuple, template) => {
|
|
198
|
-
atPlacement = row.textContent.trim();
|
|
199
|
-
template.parentElement.insertBefore(row, template);
|
|
200
|
-
};
|
|
201
|
-
applyRows(list(), rows([{ id: "1", name: "Ada" }]), place);
|
|
202
|
-
assert.equal(atPlacement, "Ada");
|
|
203
|
-
});
|
|
204
|
-
it("hands the widget a row that is not yet in the document", () => {
|
|
205
|
-
let connected = true;
|
|
206
|
-
const place = (row, _tuple, template) => {
|
|
207
|
-
connected = row.parentElement !== null;
|
|
208
|
-
template.parentElement.insertBefore(row, template);
|
|
209
|
-
};
|
|
210
|
-
applyRows(list(), rows([{ id: "1", name: "Ada" }]), place);
|
|
211
|
-
assert.equal(connected, false, "the row arrives detached");
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
// --- How many are showing ------------------------------------------------
|
|
215
|
-
describe("the row count", () => {
|
|
216
|
-
it("is stamped on the widget", () => {
|
|
217
|
-
// The one conditional a page cannot be sent: the server answers with rows
|
|
218
|
-
// and says nothing about how many survived reconciliation. It is here so
|
|
219
|
-
// that "no members yet" is a stylesheet rule rather than code in every
|
|
220
|
-
// list widget.
|
|
221
|
-
const scope = list();
|
|
222
|
-
applyRows(scope, rows([
|
|
223
|
-
{ id: "1", name: "Ada" },
|
|
224
|
-
{ id: "2", name: "Bab" },
|
|
225
|
-
]));
|
|
226
|
-
assert.equal(scope.getAttribute("data-rows"), "2");
|
|
227
|
-
});
|
|
228
|
-
it("is zero for an empty set", () => {
|
|
229
|
-
const scope = list();
|
|
230
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]));
|
|
231
|
-
applyRows(scope, rows([]));
|
|
232
|
-
assert.equal(scope.getAttribute("data-rows"), "0");
|
|
233
|
-
});
|
|
234
|
-
it("reports the same fact the same way after a set and after a patch", () => {
|
|
235
|
-
const viaRows = list();
|
|
236
|
-
applyRows(viaRows, rows([
|
|
237
|
-
{ id: "1", name: "Ada" },
|
|
238
|
-
{ id: "2", name: "Bab" },
|
|
239
|
-
]));
|
|
240
|
-
const viaPatch = list();
|
|
241
|
-
applyRows(viaPatch, rows([
|
|
242
|
-
{ id: "1", name: "Ada" },
|
|
243
|
-
{ id: "3", name: "Cai" },
|
|
244
|
-
]));
|
|
245
|
-
applyRows(viaPatch, patch({ rows: [{ id: "2", name: "Bab" }], drop: ["3"] }));
|
|
246
|
-
assert.equal(viaPatch.getAttribute("data-rows"), viaRows.getAttribute("data-rows"));
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
// --- What it refuses to guess at ----------------------------------------
|
|
250
|
-
describe("applyRows reports and stops", () => {
|
|
251
|
-
it("when the widget has no template to clone", async () => {
|
|
252
|
-
const scope = body(`<lb-list></lb-list>`).firstElementChild;
|
|
253
|
-
const { error } = await captured(() => applyRows(scope, rows([{ id: "1" }])));
|
|
254
|
-
assert.match(error.join("\n"), /<lb-list> has no <template> to clone/);
|
|
255
|
-
assert.equal(scope.children.length, 0, "nothing was invented");
|
|
256
|
-
});
|
|
257
|
-
it("when the template does not name the key column", async () => {
|
|
258
|
-
const scope = list(`<template><p></p></template>`);
|
|
259
|
-
const { error } = await captured(() => applyRows(scope, rows([{ id: "1" }])));
|
|
260
|
-
assert.match(error.join("\n"), /has no lb-key naming the column that identifies a row/);
|
|
261
|
-
assert.deepEqual(keys(scope), []);
|
|
262
|
-
});
|
|
263
|
-
it("does not stamp a count it could not compute", async () => {
|
|
264
|
-
const scope = body(`<lb-list></lb-list>`).firstElementChild;
|
|
265
|
-
await captured(() => applyRows(scope, rows([{ id: "1" }])));
|
|
266
|
-
assert.equal(scope.hasAttribute("data-rows"), false);
|
|
267
|
-
});
|
|
268
|
-
it("when a tuple has no key cell, and lands its siblings anyway", async () => {
|
|
269
|
-
const scope = list();
|
|
270
|
-
const { error } = await captured(() => applyRows(scope, rows([
|
|
271
|
-
{ id: "1", name: "Ada" },
|
|
272
|
-
{ name: "nameless" },
|
|
273
|
-
{ id: "2", name: "Bab" },
|
|
274
|
-
])));
|
|
275
|
-
assert.match(error.join("\n"), /a tuple for <lb-list> has no 'id' cell/);
|
|
276
|
-
assert.deepEqual(keys(scope), ["1", "2"]);
|
|
277
|
-
});
|
|
278
|
-
it("and a keyless tuple does not take a surviving row down with it", async () => {
|
|
279
|
-
// A tuple with no key contributes no key to the arrived set, so the
|
|
280
|
-
// reconciliation that follows must not read its absence as a removal.
|
|
281
|
-
const scope = list();
|
|
282
|
-
applyRows(scope, rows([{ id: "1", name: "Ada" }]));
|
|
283
|
-
await captured(() => applyRows(scope, rows([{ id: "1", name: "Ada" }, { name: "nameless" }])));
|
|
284
|
-
assert.deepEqual(keys(scope), ["1"]);
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
// --- The properties ------------------------------------------------------
|
|
288
|
-
describe("reconciliation converges", () => {
|
|
289
|
-
const SET = [
|
|
290
|
-
{ id: "1", name: "Ada" },
|
|
291
|
-
{ id: "2", name: "Bab" },
|
|
292
|
-
{ id: "3", name: "Cai" },
|
|
293
|
-
];
|
|
294
|
-
it("so applying the same set twice is applying it once", () => {
|
|
295
|
-
const once = list();
|
|
296
|
-
applyRows(once, rows(SET));
|
|
297
|
-
const twice = list();
|
|
298
|
-
applyRows(twice, rows(SET));
|
|
299
|
-
applyRows(twice, rows(SET));
|
|
300
|
-
assert.equal(twice.innerHTML, once.innerHTML);
|
|
301
|
-
assert.equal(twice.getAttribute("data-rows"), once.getAttribute("data-rows"));
|
|
302
|
-
});
|
|
303
|
-
it("so a set reached through any patches is the set reached from empty", () => {
|
|
304
|
-
// Every history below ends at SET. A reconciler that leaks state — a row
|
|
305
|
-
// it forgot to remove, an order it kept from before, a count it stamped
|
|
306
|
-
// from the wrong branch — fails here and nowhere else.
|
|
307
|
-
const histories = [
|
|
308
|
-
[],
|
|
309
|
-
[rows([])],
|
|
310
|
-
[rows([{ id: "9", name: "Zed" }])],
|
|
311
|
-
[rows(SET.slice().reverse())],
|
|
312
|
-
[
|
|
313
|
-
rows([{ id: "1", name: "Wrong" }]),
|
|
314
|
-
patch({ rows: [{ id: "5", name: "Eve" }] }),
|
|
315
|
-
],
|
|
316
|
-
[rows(SET), patch({ drop: ["2"] })],
|
|
317
|
-
[
|
|
318
|
-
rows(SET),
|
|
319
|
-
patch({ rows: [{ id: "3", name: "Changed" }] }),
|
|
320
|
-
patch({ drop: ["1"] }),
|
|
321
|
-
],
|
|
322
|
-
[patch({ rows: [{ id: "4", name: "Dot" }] }), patch({ drop: ["4"] })],
|
|
323
|
-
];
|
|
324
|
-
const fresh = list();
|
|
325
|
-
applyRows(fresh, rows(SET));
|
|
326
|
-
const expected = fresh.innerHTML;
|
|
327
|
-
for (const [index, history] of histories.entries()) {
|
|
328
|
-
const scope = list();
|
|
329
|
-
for (const step of history)
|
|
330
|
-
applyRows(scope, step);
|
|
331
|
-
applyRows(scope, rows(SET));
|
|
332
|
-
assert.equal(scope.innerHTML, expected, `history ${index} did not converge`);
|
|
333
|
-
assert.equal(scope.getAttribute("data-rows"), String(SET.length));
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The engine — see docs/testing.md, tier 2.
|
|
3
|
-
*
|
|
4
|
-
* `createHub` takes a plain object and returns an object. Nothing in
|
|
5
|
-
* server/lb-server.ts opens a socket or knows what a request is, so every
|
|
6
|
-
* fixture here is a counting stub and the whole tier runs in node.
|
|
7
|
-
*/
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=lb-server.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lb-server.test.d.ts","sourceRoot":"","sources":["../../tests/lb-server.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|