@loadbare/app 0.6.0 → 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/dist/build/assemble.js +1 -0
- package/dist/build/assemble.js.map +1 -0
- package/dist/build/cli.js +1 -0
- 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.js +1 -0
- 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 +2 -2
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +3 -4
- package/dist/build/locations.js.map +1 -0
- package/dist/build/origins.js +1 -0
- 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 +4 -0
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +5 -0
- 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.js +1 -0
- package/dist/core/lb-constants.js.map +1 -0
- package/dist/core/lb-types.d.ts +1 -1
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +2 -1
- package/dist/core/lb-types.js.map +1 -0
- package/dist/hub/lb-apply.d.ts +11 -1
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +50 -13
- 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 +117 -109
- package/dist/hub/lb-hub.browser.js.map +1 -0
- package/dist/server/lb-express.d.ts +1 -1
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +1 -0
- package/dist/server/lb-express.js.map +1 -0
- package/dist/server/lb-server.d.ts +1 -1
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +2 -1
- package/dist/server/lb-server.js.map +1 -0
- package/docs/TECHREF-1.0.md +149 -42
- package/docs/analysis-accidental-complexity.md +149 -0
- package/docs/reference/custom-elements.md +10 -17
- package/docs/reference/data-binding.md +56 -27
- package/docs/reference/server.md +18 -0
- package/docs/reference/widgets.md +3 -3
- package/docs/theory.md +114 -1
- package/docs/tutorials/072-inserting-into-a-list.md +14 -11
- package/docs/tutorials/080-widget-requests.md +9 -26
- package/package.json +2 -2
- 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 -210
- 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 -253
- 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/helpers/hub.d.ts +0 -73
- package/dist/tests/helpers/hub.d.ts.map +0 -1
- package/dist/tests/helpers/hub.js +0 -151
- 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 -177
- 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 -243
- package/dist/tests/lb-hub.test.d.ts +0 -14
- package/dist/tests/lb-hub.test.d.ts.map +0 -1
- package/dist/tests/lb-hub.test.js +0 -319
- package/dist/tests/lb-list.test.d.ts +0 -12
- package/dist/tests/lb-list.test.d.ts.map +0 -1
- package/dist/tests/lb-list.test.js +0 -339
- 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 -546
- 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 -387
- 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 -148
- 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,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"}
|
|
@@ -1,546 +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
|
-
import assert from "node:assert/strict";
|
|
9
|
-
import { describe, it } from "node:test";
|
|
10
|
-
import { createHub, list, patch, row, } from "../server/lb-server";
|
|
11
|
-
import { captured } from "./helpers/console";
|
|
12
|
-
/**
|
|
13
|
-
* A context to compare by identity.
|
|
14
|
-
*
|
|
15
|
-
* The engine never reads the context — it only hands it on — so what a test
|
|
16
|
-
* needs of one is that it can tell the same object apart from another. The
|
|
17
|
-
* cast is because an application fills the interface in by declaration
|
|
18
|
-
* merging, and the demo's is the one in this compilation.
|
|
19
|
-
*/
|
|
20
|
-
function context() {
|
|
21
|
-
return {};
|
|
22
|
-
}
|
|
23
|
-
/** A query that answers a fixed row and records that it ran. */
|
|
24
|
-
function stub(log, name, answer) {
|
|
25
|
-
return row((ctx) => {
|
|
26
|
-
log.push(name);
|
|
27
|
-
seen.set(name, ctx);
|
|
28
|
-
return answer;
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
const seen = new Map();
|
|
32
|
-
function pages(entries) {
|
|
33
|
-
const map = {};
|
|
34
|
-
for (const [name, entry] of Object.entries(entries)) {
|
|
35
|
-
map[name] = { queries: entry.queries, requests: entry.requests ?? {} };
|
|
36
|
-
}
|
|
37
|
-
return map;
|
|
38
|
-
}
|
|
39
|
-
// --- Entering a page ----------------------------------------------------
|
|
40
|
-
describe("entering a page", () => {
|
|
41
|
-
it("runs onPageEnter before any query, then the whole query set", async () => {
|
|
42
|
-
const log = [];
|
|
43
|
-
const hub = createHub(pages({
|
|
44
|
-
report: {
|
|
45
|
-
queries: {
|
|
46
|
-
head: stub(log, "head", { title: "Q1" }),
|
|
47
|
-
body: stub(log, "body", { line: "one" }),
|
|
48
|
-
},
|
|
49
|
-
requests: {
|
|
50
|
-
onPageEnter: () => void log.push("onPageEnter"),
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
}));
|
|
54
|
-
const data = await hub.dataForPage("report", context());
|
|
55
|
-
assert.deepEqual(log, ["onPageEnter", "head", "body"]);
|
|
56
|
-
assert.deepEqual(data, { head: { title: "Q1" }, body: { line: "one" } });
|
|
57
|
-
});
|
|
58
|
-
it("runs the query set when the page declares no requests", async () => {
|
|
59
|
-
const log = [];
|
|
60
|
-
const hub = createHub(pages({ report: { queries: { head: stub(log, "head", { a: "1" }) } } }));
|
|
61
|
-
assert.deepEqual(await hub.dataForPage("report", context()), {
|
|
62
|
-
head: { a: "1" },
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
it("hands every query the context it was called with", async () => {
|
|
66
|
-
// The context is request-scoped — an authenticated database handle is the
|
|
67
|
-
// expected case — so which one a query gets is the whole of the contract.
|
|
68
|
-
seen.clear();
|
|
69
|
-
const log = [];
|
|
70
|
-
const hub = createHub(pages({
|
|
71
|
-
report: {
|
|
72
|
-
queries: {
|
|
73
|
-
head: stub(log, "head", {}),
|
|
74
|
-
body: stub(log, "body", {}),
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
}));
|
|
78
|
-
const ctx = context();
|
|
79
|
-
await hub.dataForPage("report", ctx);
|
|
80
|
-
assert.equal(seen.get("head"), ctx);
|
|
81
|
-
assert.equal(seen.get("body"), ctx);
|
|
82
|
-
});
|
|
83
|
-
it("answers nothing for a page it does not have, and says so", async () => {
|
|
84
|
-
const hub = createHub(pages({}));
|
|
85
|
-
const { result, warn } = await captured(() => hub.dataForPage("nope", context()));
|
|
86
|
-
assert.deepEqual(result, {});
|
|
87
|
-
assert.match(warn.join("\n"), /no page 'nope'/);
|
|
88
|
-
});
|
|
89
|
-
it("awaits a query that returns a promise", async () => {
|
|
90
|
-
const hub = createHub(pages({
|
|
91
|
-
report: { queries: { head: row(async () => ({ a: "1" })) } },
|
|
92
|
-
}));
|
|
93
|
-
assert.deepEqual(await hub.dataForPage("report", context()), {
|
|
94
|
-
head: { a: "1" },
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
// --- Actions -------------------------------------------------------------
|
|
99
|
-
describe("a reserved name", () => {
|
|
100
|
-
// Names beginning with lb- are Loadbare's: the hub's own query, the CRUD
|
|
101
|
-
// operations. See LB_RESERVED_PREFIX in core/lb-constants.ts.
|
|
102
|
-
it("is refused as a query, at startup", () => {
|
|
103
|
-
assert.throws(() => createHub({
|
|
104
|
-
home: {
|
|
105
|
-
queries: { "lb-navigation": row(async () => ({})) },
|
|
106
|
-
requests: {},
|
|
107
|
-
},
|
|
108
|
-
}), /page 'home' declares 'lb-navigation', but names beginning with 'lb-' are reserved/);
|
|
109
|
-
});
|
|
110
|
-
it("is refused as an action, at startup", () => {
|
|
111
|
-
assert.throws(() => createHub({
|
|
112
|
-
home: {
|
|
113
|
-
queries: {},
|
|
114
|
-
requests: {
|
|
115
|
-
actions: { "lb-delete": { run: () => { }, refresh: [] } },
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
}), /declares 'lb-delete', but names beginning with 'lb-' are reserved/);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
describe("an action", () => {
|
|
122
|
-
it("is refused unless the page declared it", async () => {
|
|
123
|
-
// The rule that keeps this from being an RPC endpoint: the wire cannot
|
|
124
|
-
// reach anything the page has not published.
|
|
125
|
-
const log = [];
|
|
126
|
-
const hub = createHub(pages({
|
|
127
|
-
counter: {
|
|
128
|
-
queries: { count: stub(log, "count", { n: "1" }) },
|
|
129
|
-
requests: {
|
|
130
|
-
actions: {
|
|
131
|
-
bump: { run: () => void log.push("bump"), refresh: ["count"] },
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
}));
|
|
136
|
-
const { result, warn } = await captured(() => hub.runAction("counter", "drop", {}, context()));
|
|
137
|
-
assert.deepEqual(result, {});
|
|
138
|
-
assert.deepEqual(log, [], "nothing ran");
|
|
139
|
-
assert.match(warn.join("\n"), /page 'counter' declares no action 'drop'/);
|
|
140
|
-
});
|
|
141
|
-
it("is refused on a page that does not exist", async () => {
|
|
142
|
-
const hub = createHub(pages({}));
|
|
143
|
-
const { result, warn } = await captured(() => hub.runAction("nope", "bump", {}, context()));
|
|
144
|
-
assert.deepEqual(result, {});
|
|
145
|
-
assert.match(warn.join("\n"), /declares no action 'bump'/);
|
|
146
|
-
});
|
|
147
|
-
it("runs, then runs the refresh set it declared", async () => {
|
|
148
|
-
const log = [];
|
|
149
|
-
const hub = createHub(pages({
|
|
150
|
-
counter: {
|
|
151
|
-
queries: {
|
|
152
|
-
count: stub(log, "count", { n: "1" }),
|
|
153
|
-
unrelated: stub(log, "unrelated", { x: "9" }),
|
|
154
|
-
},
|
|
155
|
-
requests: {
|
|
156
|
-
actions: {
|
|
157
|
-
bump: { run: () => void log.push("bump"), refresh: ["count"] },
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
}));
|
|
162
|
-
const data = await hub.runAction("counter", "bump", {}, context());
|
|
163
|
-
assert.deepEqual(log, ["bump", "count"], "the action runs first");
|
|
164
|
-
assert.deepEqual(data, { count: { n: "1" } }, "only the refresh set ran");
|
|
165
|
-
});
|
|
166
|
-
it("hands the action where the interaction happened", async () => {
|
|
167
|
-
let where;
|
|
168
|
-
const hub = createHub(pages({
|
|
169
|
-
roster: {
|
|
170
|
-
queries: {},
|
|
171
|
-
requests: {
|
|
172
|
-
actions: {
|
|
173
|
-
pick: { run: (_ctx, w) => void (where = w), refresh: [] },
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
}));
|
|
178
|
-
await hub.runAction("roster", "pick", { list: "members", key: "7", cell: "role", value: "lead" }, context());
|
|
179
|
-
assert.deepEqual(where, {
|
|
180
|
-
list: "members",
|
|
181
|
-
key: "7",
|
|
182
|
-
cell: "role",
|
|
183
|
-
value: "lead",
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
it("hands the action and its refresh queries one context", async () => {
|
|
187
|
-
seen.clear();
|
|
188
|
-
const log = [];
|
|
189
|
-
let actionCtx;
|
|
190
|
-
const hub = createHub(pages({
|
|
191
|
-
counter: {
|
|
192
|
-
queries: { count: stub(log, "count", {}) },
|
|
193
|
-
requests: {
|
|
194
|
-
actions: {
|
|
195
|
-
bump: {
|
|
196
|
-
run: (ctx) => void (actionCtx = ctx),
|
|
197
|
-
refresh: ["count"],
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
}));
|
|
203
|
-
const ctx = context();
|
|
204
|
-
await hub.runAction("counter", "bump", {}, ctx);
|
|
205
|
-
assert.equal(actionCtx, ctx);
|
|
206
|
-
assert.equal(seen.get("count"), ctx);
|
|
207
|
-
});
|
|
208
|
-
it("refreshes against whatever the action wrote", async () => {
|
|
209
|
-
// The refresh queries run after the action, against the same context, so
|
|
210
|
-
// they see it. This is the case a store makes concrete.
|
|
211
|
-
const store = { n: 1 };
|
|
212
|
-
const hub = createHub(pages({
|
|
213
|
-
counter: {
|
|
214
|
-
queries: { count: row(() => ({ n: String(store.n) })) },
|
|
215
|
-
requests: {
|
|
216
|
-
actions: {
|
|
217
|
-
bump: { run: () => void (store.n += 1), refresh: ["count"] },
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
}));
|
|
222
|
-
assert.deepEqual(await hub.runAction("counter", "bump", {}, context()), {
|
|
223
|
-
count: { n: "2" },
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
it("lays what it stated over what the refresh produced", async () => {
|
|
227
|
-
// This is how a patch reaches the browser at all. A query answers for its
|
|
228
|
-
// whole set and cannot know why it was re-run; the action knows exactly
|
|
229
|
-
// what it changed and may say only that. The narrower answer wins.
|
|
230
|
-
const hub = createHub(pages({
|
|
231
|
-
roster: {
|
|
232
|
-
queries: { members: list(() => [{ id: "1" }, { id: "2" }]) },
|
|
233
|
-
requests: {
|
|
234
|
-
actions: {
|
|
235
|
-
rename: {
|
|
236
|
-
run: () => ({ members: patch({ rows: [{ id: "2" }] }) }),
|
|
237
|
-
refresh: ["members"],
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
}));
|
|
243
|
-
const data = await hub.runAction("roster", "rename", {}, context());
|
|
244
|
-
assert.deepEqual(data, { members: { rows: [{ id: "2" }] } });
|
|
245
|
-
});
|
|
246
|
-
it("answers with the refresh set alone when it states nothing", async () => {
|
|
247
|
-
const hub = createHub(pages({
|
|
248
|
-
counter: {
|
|
249
|
-
queries: { count: row(() => ({ n: "1" })) },
|
|
250
|
-
requests: {
|
|
251
|
-
actions: { bump: { run: () => { }, refresh: ["count"] } },
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
}));
|
|
255
|
-
assert.deepEqual(await hub.runAction("counter", "bump", {}, context()), {
|
|
256
|
-
count: { n: "1" },
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
it("skips a refresh name the page does not declare, and runs its siblings", async () => {
|
|
260
|
-
const hub = createHub(pages({
|
|
261
|
-
counter: {
|
|
262
|
-
queries: { count: row(() => ({ n: "1" })) },
|
|
263
|
-
requests: {
|
|
264
|
-
actions: {
|
|
265
|
-
bump: { run: () => { }, refresh: ["ghost", "count"] },
|
|
266
|
-
},
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
|
-
}));
|
|
270
|
-
const { result, warn } = await captured(() => hub.runAction("counter", "bump", {}, context()));
|
|
271
|
-
assert.deepEqual(result, { count: { n: "1" } });
|
|
272
|
-
assert.match(warn.join("\n"), /page 'counter' declares no query 'ghost'/);
|
|
273
|
-
});
|
|
274
|
-
it("awaits an action that returns a promise", async () => {
|
|
275
|
-
const action = {
|
|
276
|
-
run: async () => ({ count: { n: "5" } }),
|
|
277
|
-
refresh: [],
|
|
278
|
-
};
|
|
279
|
-
const hub = createHub(pages({
|
|
280
|
-
counter: { queries: {}, requests: { actions: { bump: action } } },
|
|
281
|
-
}));
|
|
282
|
-
assert.deepEqual(await hub.runAction("counter", "bump", {}, context()), {
|
|
283
|
-
count: { n: "5" },
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
// --- Cell change -----------------------------------------------------------
|
|
288
|
-
describe("a cell-change", () => {
|
|
289
|
-
it("is refused unless the query declared it", async () => {
|
|
290
|
-
const hub = createHub(pages({ roster: { queries: { members: list(() => []) } } }));
|
|
291
|
-
const { result, warn } = await captured(() => hub.runCellChange("roster", { list: "members", key: "1", cell: "name", value: "Ada" }, context()));
|
|
292
|
-
assert.deepEqual(result, {});
|
|
293
|
-
assert.match(warn.join("\n"), /page 'roster' declares no cellChange for 'members'/);
|
|
294
|
-
});
|
|
295
|
-
it("hands the operation its key, cell and value, then runs its refresh set", async () => {
|
|
296
|
-
let where;
|
|
297
|
-
const log = [];
|
|
298
|
-
const hub = createHub(pages({
|
|
299
|
-
roster: {
|
|
300
|
-
queries: { members: stub(log, "members", { n: "1" }) },
|
|
301
|
-
requests: {
|
|
302
|
-
crud: {
|
|
303
|
-
members: {
|
|
304
|
-
cellChange: {
|
|
305
|
-
run: (_ctx, w) => void (where = w),
|
|
306
|
-
refresh: ["members"],
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
}));
|
|
313
|
-
const data = await hub.runCellChange("roster", { list: "members", key: "7", cell: "role", value: "lead" }, context());
|
|
314
|
-
assert.deepEqual(where, { key: "7", cell: "role", value: "lead" });
|
|
315
|
-
assert.deepEqual(log, ["members"]);
|
|
316
|
-
assert.deepEqual(data, { members: { n: "1" } });
|
|
317
|
-
});
|
|
318
|
-
it("lays what it stated over what the refresh produced", async () => {
|
|
319
|
-
const hub = createHub(pages({
|
|
320
|
-
roster: {
|
|
321
|
-
queries: { members: list(() => [{ id: "1" }, { id: "2" }]) },
|
|
322
|
-
requests: {
|
|
323
|
-
crud: {
|
|
324
|
-
members: {
|
|
325
|
-
cellChange: {
|
|
326
|
-
run: (_ctx, w) => ({
|
|
327
|
-
members: patch({ rows: [{ id: "2", name: w.value }] }),
|
|
328
|
-
}),
|
|
329
|
-
refresh: ["members"],
|
|
330
|
-
},
|
|
331
|
-
},
|
|
332
|
-
},
|
|
333
|
-
},
|
|
334
|
-
},
|
|
335
|
-
}));
|
|
336
|
-
const data = await hub.runCellChange("roster", { list: "members", key: "2", cell: "name", value: "Grace" }, context());
|
|
337
|
-
assert.deepEqual(data, {
|
|
338
|
-
members: { rows: [{ id: "2", name: "Grace" }] },
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
// --- Row delete ------------------------------------------------------
|
|
343
|
-
describe("a row-delete", () => {
|
|
344
|
-
it("is refused unless the query declared it", async () => {
|
|
345
|
-
const hub = createHub(pages({ roster: { queries: { members: list(() => []) } } }));
|
|
346
|
-
const { result, warn } = await captured(() => hub.runRowDelete("roster", { list: "members", key: "1" }, context()));
|
|
347
|
-
assert.deepEqual(result, {});
|
|
348
|
-
assert.match(warn.join("\n"), /page 'roster' declares no rowDelete for 'members'/);
|
|
349
|
-
});
|
|
350
|
-
it("hands the operation its key, then runs its refresh set", async () => {
|
|
351
|
-
let where;
|
|
352
|
-
const hub = createHub(pages({
|
|
353
|
-
roster: {
|
|
354
|
-
queries: { members: list(() => []) },
|
|
355
|
-
requests: {
|
|
356
|
-
crud: {
|
|
357
|
-
members: {
|
|
358
|
-
rowDelete: {
|
|
359
|
-
run: (_ctx, w) => void (where = w),
|
|
360
|
-
refresh: [],
|
|
361
|
-
},
|
|
362
|
-
},
|
|
363
|
-
},
|
|
364
|
-
},
|
|
365
|
-
},
|
|
366
|
-
}));
|
|
367
|
-
await hub.runRowDelete("roster", { list: "members", key: "7" }, context());
|
|
368
|
-
assert.deepEqual(where, { key: "7" });
|
|
369
|
-
});
|
|
370
|
-
it("answers with the drop it stated", async () => {
|
|
371
|
-
const hub = createHub(pages({
|
|
372
|
-
roster: {
|
|
373
|
-
queries: { members: list(() => [{ id: "1" }, { id: "2" }]) },
|
|
374
|
-
requests: {
|
|
375
|
-
crud: {
|
|
376
|
-
members: {
|
|
377
|
-
rowDelete: {
|
|
378
|
-
run: (_ctx, w) => ({
|
|
379
|
-
members: patch({ drop: [w.key] }),
|
|
380
|
-
}),
|
|
381
|
-
refresh: [],
|
|
382
|
-
},
|
|
383
|
-
},
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
},
|
|
387
|
-
}));
|
|
388
|
-
const data = await hub.runRowDelete("roster", { list: "members", key: "1" }, context());
|
|
389
|
-
assert.deepEqual(data, { members: { drop: ["1"] } });
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
// --- Row insert --------------------------------------------------------
|
|
393
|
-
describe("a row-insert", () => {
|
|
394
|
-
it("is refused unless the query declared it", async () => {
|
|
395
|
-
const hub = createHub(pages({ roster: { queries: { members: list(() => []) } } }));
|
|
396
|
-
const { result, warn } = await captured(() => hub.runRowInsert("roster", { list: "members", values: { name: "Ada" } }, context()));
|
|
397
|
-
assert.deepEqual(result, {});
|
|
398
|
-
assert.match(warn.join("\n"), /page 'roster' declares no rowInsert for 'members'/);
|
|
399
|
-
});
|
|
400
|
-
it("hands the operation the gathered values, then runs its refresh set", async () => {
|
|
401
|
-
let where;
|
|
402
|
-
const hub = createHub(pages({
|
|
403
|
-
roster: {
|
|
404
|
-
queries: { members: list(() => []) },
|
|
405
|
-
requests: {
|
|
406
|
-
crud: {
|
|
407
|
-
members: {
|
|
408
|
-
rowInsert: {
|
|
409
|
-
run: (_ctx, w) => void (where = w),
|
|
410
|
-
refresh: [],
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
|
-
},
|
|
415
|
-
},
|
|
416
|
-
}));
|
|
417
|
-
await hub.runRowInsert("roster", { list: "members", values: { name: "Ada", role: "Analyst" } }, context());
|
|
418
|
-
assert.deepEqual(where, { values: { name: "Ada", role: "Analyst" } });
|
|
419
|
-
});
|
|
420
|
-
it("answers with the row it stated", async () => {
|
|
421
|
-
const hub = createHub(pages({
|
|
422
|
-
roster: {
|
|
423
|
-
queries: { members: list(() => [{ id: "1" }]) },
|
|
424
|
-
requests: {
|
|
425
|
-
crud: {
|
|
426
|
-
members: {
|
|
427
|
-
rowInsert: {
|
|
428
|
-
run: (_ctx, w) => ({
|
|
429
|
-
members: patch({ rows: [{ id: "2", ...w.values }] }),
|
|
430
|
-
}),
|
|
431
|
-
refresh: [],
|
|
432
|
-
},
|
|
433
|
-
},
|
|
434
|
-
},
|
|
435
|
-
},
|
|
436
|
-
},
|
|
437
|
-
}));
|
|
438
|
-
const data = await hub.runRowInsert("roster", { list: "members", values: { name: "Ada" } }, context());
|
|
439
|
-
assert.deepEqual(data, {
|
|
440
|
-
members: { rows: [{ id: "2", name: "Ada" }] },
|
|
441
|
-
});
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
// --- Row update ----------------------------------------------------------
|
|
445
|
-
describe("a row-update", () => {
|
|
446
|
-
it("is refused unless the query declared it", async () => {
|
|
447
|
-
const hub = createHub(pages({ roster: { queries: { members: list(() => []) } } }));
|
|
448
|
-
const { result, warn } = await captured(() => hub.runRowUpdate("roster", { list: "members", key: "1", values: { name: "Ada" } }, context()));
|
|
449
|
-
assert.deepEqual(result, {});
|
|
450
|
-
assert.match(warn.join("\n"), /page 'roster' declares no rowUpdate for 'members'/);
|
|
451
|
-
});
|
|
452
|
-
it("hands the operation the key and the gathered values, then runs its refresh set", async () => {
|
|
453
|
-
let where;
|
|
454
|
-
const hub = createHub(pages({
|
|
455
|
-
roster: {
|
|
456
|
-
queries: { members: list(() => []) },
|
|
457
|
-
requests: {
|
|
458
|
-
crud: {
|
|
459
|
-
members: {
|
|
460
|
-
rowUpdate: {
|
|
461
|
-
run: (_ctx, w) => void (where = w),
|
|
462
|
-
refresh: [],
|
|
463
|
-
},
|
|
464
|
-
},
|
|
465
|
-
},
|
|
466
|
-
},
|
|
467
|
-
},
|
|
468
|
-
}));
|
|
469
|
-
await hub.runRowUpdate("roster", { list: "members", key: "7", values: { name: "Ada", role: "Analyst" } }, context());
|
|
470
|
-
assert.deepEqual(where, {
|
|
471
|
-
key: "7",
|
|
472
|
-
values: { name: "Ada", role: "Analyst" },
|
|
473
|
-
});
|
|
474
|
-
});
|
|
475
|
-
it("answers with the row it stated", async () => {
|
|
476
|
-
const hub = createHub(pages({
|
|
477
|
-
roster: {
|
|
478
|
-
queries: { members: list(() => [{ id: "1", name: "old" }]) },
|
|
479
|
-
requests: {
|
|
480
|
-
crud: {
|
|
481
|
-
members: {
|
|
482
|
-
rowUpdate: {
|
|
483
|
-
run: (_ctx, w) => ({
|
|
484
|
-
members: patch({ rows: [{ id: w.key, ...w.values }] }),
|
|
485
|
-
}),
|
|
486
|
-
refresh: [],
|
|
487
|
-
},
|
|
488
|
-
},
|
|
489
|
-
},
|
|
490
|
-
},
|
|
491
|
-
},
|
|
492
|
-
}));
|
|
493
|
-
const data = await hub.runRowUpdate("roster", { list: "members", key: "1", values: { name: "Ada" } }, context());
|
|
494
|
-
assert.deepEqual(data, {
|
|
495
|
-
members: { rows: [{ id: "1", name: "Ada" }] },
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
// --- The wire vocabulary -------------------------------------------------
|
|
500
|
-
describe("cardinality is declared, not inspected", () => {
|
|
501
|
-
it("marks a list query and a row query apart", () => {
|
|
502
|
-
assert.equal(list(() => []).kind, "list");
|
|
503
|
-
assert.equal(row(() => ({})).kind, "row");
|
|
504
|
-
});
|
|
505
|
-
it("carries a whole set as the array itself, with no wrapper", async () => {
|
|
506
|
-
const set = [{ id: "1" }, { id: "2" }];
|
|
507
|
-
const hub = createHub(pages({ roster: { queries: { members: list(() => set) } } }));
|
|
508
|
-
assert.deepEqual(await hub.dataForPage("roster", context()), {
|
|
509
|
-
members: set,
|
|
510
|
-
});
|
|
511
|
-
});
|
|
512
|
-
it("carries only what changed as an object, and adds nothing", () => {
|
|
513
|
-
assert.deepEqual(patch({ drop: ["1"] }), { drop: ["1"] });
|
|
514
|
-
assert.deepEqual(patch({}), {});
|
|
515
|
-
});
|
|
516
|
-
it("tells a whole set from a patch by Array.isArray, so no column is reserved", () => {
|
|
517
|
-
// `op` used to be a reserved column name, because the hub read it to
|
|
518
|
-
// decide which shape had arrived. Nothing inspects a value now.
|
|
519
|
-
assert.equal(Array.isArray(patch({ rows: [] })), false);
|
|
520
|
-
assert.equal(Array.isArray([{ op: "rows" }]), true);
|
|
521
|
-
});
|
|
522
|
-
it("refuses a list query that answers with one row", async () => {
|
|
523
|
-
const hub = createHub(pages({
|
|
524
|
-
roster: {
|
|
525
|
-
queries: {
|
|
526
|
-
members: list(() => ({})),
|
|
527
|
-
},
|
|
528
|
-
},
|
|
529
|
-
}));
|
|
530
|
-
const { result, warn } = await captured(() => hub.dataForPage("roster", context()));
|
|
531
|
-
assert.deepEqual(result, {});
|
|
532
|
-
assert.match(warn.join("\n"), /query 'members' is declared list but answered with one row/);
|
|
533
|
-
});
|
|
534
|
-
it("refuses a row query that answers with rows", async () => {
|
|
535
|
-
const hub = createHub(pages({
|
|
536
|
-
roster: {
|
|
537
|
-
queries: {
|
|
538
|
-
chosen: row(() => []),
|
|
539
|
-
},
|
|
540
|
-
},
|
|
541
|
-
}));
|
|
542
|
-
const { result, warn } = await captured(() => hub.dataForPage("roster", context()));
|
|
543
|
-
assert.deepEqual(result, {});
|
|
544
|
-
assert.match(warn.join("\n"), /query 'chosen' is declared row but answered with rows/);
|
|
545
|
-
});
|
|
546
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The origin cascade — see build/origins.ts.
|
|
3
|
-
*
|
|
4
|
-
* Definitions, scripts and stylesheets all come from the same ordered list of
|
|
5
|
-
* origins, so they are all tested here: that one origin is scanned the same
|
|
6
|
-
* way whatever it is, that a duplicate tag inside one origin is an error, and
|
|
7
|
-
* that a later origin wins over an earlier one.
|
|
8
|
-
*/
|
|
9
|
-
export {};
|
|
10
|
-
//# sourceMappingURL=origins.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"origins.test.d.ts","sourceRoot":"","sources":["../../tests/origins.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|