@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,253 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Expansion — see docs/reference/custom-elements.md and docs/testing.md, tier 1.
|
|
3
|
-
*
|
|
4
|
-
* Definitions are written inline here because `expand` takes them as a map
|
|
5
|
-
* and a two-line definition beside its assertion reads better than a file
|
|
6
|
-
* reference. The cases that genuinely need files — the checks `loadDefinitions`
|
|
7
|
-
* runs, and directory ordering — use tests/fixtures/definitions.
|
|
8
|
-
*/
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
import { describe, it } from "node:test";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
import { JSDOM } from "jsdom";
|
|
13
|
-
import { expand, loadDefinitions } from "../build/expand";
|
|
14
|
-
const FIXTURES = fileURLToPath(new URL("./fixtures/definitions", import.meta.url));
|
|
15
|
-
/**
|
|
16
|
-
* Read expansion's output as a tree.
|
|
17
|
-
*
|
|
18
|
-
* Used where the claim is about the document rather than about the text —
|
|
19
|
-
* which elements exist, which attributes an element has. Matching a
|
|
20
|
-
* serialization would test the serializer.
|
|
21
|
-
*/
|
|
22
|
-
function parseHtml(html) {
|
|
23
|
-
const holder = new JSDOM("").window.document.createElement("div");
|
|
24
|
-
holder.innerHTML = html;
|
|
25
|
-
return holder;
|
|
26
|
-
}
|
|
27
|
-
/** Definitions are keyed by the uppercased tag they define. */
|
|
28
|
-
function defs(entries) {
|
|
29
|
-
const map = {};
|
|
30
|
-
for (const [tag, source] of Object.entries(entries)) {
|
|
31
|
-
map[tag.toUpperCase()] = source;
|
|
32
|
-
}
|
|
33
|
-
return map;
|
|
34
|
-
}
|
|
35
|
-
// --- Rejections ----------------------------------------------------------
|
|
36
|
-
//
|
|
37
|
-
// Expansion runs before any request exists, so every one of these is a build
|
|
38
|
-
// that must not produce output. Each asserts on the message as well as the
|
|
39
|
-
// throw: an author reads the message, and it is the whole of the diagnosis.
|
|
40
|
-
describe("expansion rejects", () => {
|
|
41
|
-
it("a parameter the definition never declared", () => {
|
|
42
|
-
assert.throws(() => expand(`<lb-thing exp-label="Name"></lb-thing>`, defs({ "lb-thing": `<div></div>` })), /given exp-label, but its definition has no \{\{label\}\}/);
|
|
43
|
-
});
|
|
44
|
-
it("a placeholder no tag could supply", async () => {
|
|
45
|
-
await assert.rejects(() => loadDefinitions([`${FIXTURES}/camel`]), /declares \{\{inputClass\}\}.*Write \{\{input-class\}\}/s);
|
|
46
|
-
});
|
|
47
|
-
it("a cycle in the definition graph", async () => {
|
|
48
|
-
await assert.rejects(() => loadDefinitions([`${FIXTURES}/cycle`]), /cycle in the definition graph/);
|
|
49
|
-
});
|
|
50
|
-
it("two destinations with one name", () => {
|
|
51
|
-
assert.throws(() => expand(`<lb-thing></lb-thing>`, defs({
|
|
52
|
-
"lb-thing": `<table><thead lb-template="head"></thead><tfoot lb-template="head"></tfoot></table>`,
|
|
53
|
-
})), /declares two lb-template="head" destinations/);
|
|
54
|
-
});
|
|
55
|
-
it("two templates for one destination", () => {
|
|
56
|
-
assert.throws(() => expand(`<lb-thing>` +
|
|
57
|
-
`<template lb-template="head"><tr></tr></template>` +
|
|
58
|
-
`<template lb-template="head"><tr></tr></template>` +
|
|
59
|
-
`</lb-thing>`, defs({
|
|
60
|
-
"lb-thing": `<table><thead lb-template="head"></thead></table>`,
|
|
61
|
-
})), /was given two templates for 'head'/);
|
|
62
|
-
});
|
|
63
|
-
it("a template naming a destination the definition does not have", () => {
|
|
64
|
-
assert.throws(() => expand(`<lb-thing><template lb-template="foot"><tr></tr></template></lb-thing>`, defs({
|
|
65
|
-
"lb-thing": `<table><thead lb-template="head"></thead></table>`,
|
|
66
|
-
})), /has no destination by that name/);
|
|
67
|
-
});
|
|
68
|
-
it("content written into a definition with no slot", () => {
|
|
69
|
-
assert.throws(() => expand(`<lb-thing><p>hello</p></lb-thing>`, defs({ "lb-thing": `<div></div>` })), /content was written inside <lb-thing>, whose definition has no lb-slot/);
|
|
70
|
-
});
|
|
71
|
-
it("a definition declaring more than one slot", () => {
|
|
72
|
-
assert.throws(() => expand(`<lb-thing></lb-thing>`, defs({
|
|
73
|
-
"lb-thing": `<div lb-slot></div><span lb-slot></span>`,
|
|
74
|
-
})), /declares more than one lb-slot/);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
// --- Substitution --------------------------------------------------------
|
|
78
|
-
describe("substitution", () => {
|
|
79
|
-
it("fills an attribute and a text node", () => {
|
|
80
|
-
const html = expand(`<lb-thing exp-label="Name" exp-name="who"></lb-thing>`, defs({
|
|
81
|
-
"lb-thing": `<label>{{label}}<input name="{{name}}" /></label>`,
|
|
82
|
-
}));
|
|
83
|
-
assert.match(html, /<label>Name<input name="who"><\/label>/);
|
|
84
|
-
});
|
|
85
|
-
it("drops an attribute whose placeholder was not supplied", () => {
|
|
86
|
-
// Presence propagates: an unsupplied placeholder must not emit
|
|
87
|
-
// readonly="", which HTML reads as readonly.
|
|
88
|
-
const html = expand(`<lb-thing></lb-thing>`, defs({ "lb-thing": `<input readonly="{{readonly}}" />` }));
|
|
89
|
-
assert.match(html, /<input>/);
|
|
90
|
-
assert.doesNotMatch(html, /readonly/);
|
|
91
|
-
});
|
|
92
|
-
it("keeps the whitespace around a text placeholder", () => {
|
|
93
|
-
// "{{label}} <input>" has to keep separating the label from the control.
|
|
94
|
-
const html = expand(`<lb-thing exp-label="Name"></lb-thing>`, defs({ "lb-thing": `<label>{{label}} <input /></label>` }));
|
|
95
|
-
assert.match(html, /<label>Name <input><\/label>/);
|
|
96
|
-
});
|
|
97
|
-
it("fills an unsupplied text placeholder with nothing", () => {
|
|
98
|
-
const html = expand(`<lb-thing></lb-thing>`, defs({ "lb-thing": `<label>{{label}}</label>` }));
|
|
99
|
-
assert.match(html, /<label><\/label>/);
|
|
100
|
-
});
|
|
101
|
-
it("falls back to a default the definition wrote", () => {
|
|
102
|
-
const html = expand(`<lb-thing></lb-thing>`, defs({ "lb-thing": `<button>{{text|OK}}</button>` }));
|
|
103
|
-
assert.match(html, /<button>OK<\/button>/);
|
|
104
|
-
});
|
|
105
|
-
it("lets a supplied value win over a default", () => {
|
|
106
|
-
const html = expand(`<lb-thing exp-text="Go"></lb-thing>`, defs({ "lb-thing": `<button>{{text|OK}}</button>` }));
|
|
107
|
-
assert.match(html, /<button>Go<\/button>/);
|
|
108
|
-
});
|
|
109
|
-
it("trims whitespace around a name and its default", () => {
|
|
110
|
-
// A spaced form that shipped as literal braces would be a silent failure
|
|
111
|
-
// found in the browser, so the space is not part of either side.
|
|
112
|
-
const html = expand(`<lb-thing></lb-thing>`, defs({
|
|
113
|
-
"lb-thing": `<button title="{{ tip | Press me }}">{{ text | OK }}</button>`,
|
|
114
|
-
}));
|
|
115
|
-
assert.match(html, /<button title="Press me">OK<\/button>/);
|
|
116
|
-
});
|
|
117
|
-
it("emits an attribute whose default is empty", () => {
|
|
118
|
-
// The one case a default distinguishes from no default: presence.
|
|
119
|
-
// readonly="{{readonly|}}" is a boolean attribute on unless a tag turns
|
|
120
|
-
// it off — which, since a supplied value is a string, a tag cannot do;
|
|
121
|
-
// the default is for a definition that wants the attribute there.
|
|
122
|
-
const html = expand(`<lb-thing></lb-thing>`, defs({ "lb-thing": `<input readonly="{{readonly|}}" />` }));
|
|
123
|
-
assert.match(html, /<input readonly="">/);
|
|
124
|
-
});
|
|
125
|
-
it("reaches placeholders inside template content", () => {
|
|
126
|
-
// lb-picker ships a row template of its own, so {{key}} written inside
|
|
127
|
-
// one has to be a placeholder rather than literal text that ships.
|
|
128
|
-
const html = expand(`<lb-thing exp-key="id" exp-cell="name"></lb-thing>`, defs({
|
|
129
|
-
"lb-thing": `<select><template lb-key="{{key}}"><option lb-cell="{{cell}}"></option></template></select>`,
|
|
130
|
-
}));
|
|
131
|
-
assert.match(html, /<template lb-key="id">/);
|
|
132
|
-
assert.match(html, /<option lb-cell="name">/);
|
|
133
|
-
});
|
|
134
|
-
it("leaves a partial placeholder alone", () => {
|
|
135
|
-
// A placeholder is an entire attribute value or an entire text node.
|
|
136
|
-
const html = expand(`<lb-thing></lb-thing>`, defs({ "lb-thing": `<label title="a {{label}} b"></label>` }));
|
|
137
|
-
assert.match(html, /title="a \{\{label\}\} b"/);
|
|
138
|
-
});
|
|
139
|
-
it("refuses a parameter for a partial placeholder", () => {
|
|
140
|
-
// The consequence of the rule above, and the reason it is safe: a name
|
|
141
|
-
// that is not a placeholder is a name the definition never declared, so
|
|
142
|
-
// supplying it is the same error as supplying a name that is not there
|
|
143
|
-
// at all. The author is told rather than shipped literal braces.
|
|
144
|
-
assert.throws(() => expand(`<lb-thing exp-label="Name"></lb-thing>`, defs({ "lb-thing": `<label title="a {{label}} b"></label>` })), /has no \{\{label\}\}/);
|
|
145
|
-
});
|
|
146
|
-
it("keeps the exp- attributes on the expanded tag", () => {
|
|
147
|
-
// What ships shows what was asked for beside what it produced.
|
|
148
|
-
const html = expand(`<lb-thing exp-label="Name"></lb-thing>`, defs({ "lb-thing": `<label>{{label}}</label>` }));
|
|
149
|
-
assert.match(html, /<lb-thing exp-label="Name">/);
|
|
150
|
-
});
|
|
151
|
-
it("cannot inject markup, by construction", () => {
|
|
152
|
-
// Substitution goes through setAttribute and node values, never through a
|
|
153
|
-
// string. This test exists to fail if anyone reaches for concatenation.
|
|
154
|
-
//
|
|
155
|
-
// The assertion reparses rather than matching the serialization, because
|
|
156
|
-
// the serialization is not the claim. An expanded tag keeps the exp-
|
|
157
|
-
// attributes it was given, and `<` in an attribute value is legal HTML
|
|
158
|
-
// that no serializer has to escape — so the output does contain the
|
|
159
|
-
// characters. What must not exist is an element.
|
|
160
|
-
const html = expand(`<lb-thing exp-label="<img src=x onerror=alert(1)>" exp-name="" onclick="alert(1)"></lb-thing>`, defs({
|
|
161
|
-
"lb-thing": `<label>{{label}}<input name="{{name}}" /></label>`,
|
|
162
|
-
}));
|
|
163
|
-
const tree = parseHtml(html);
|
|
164
|
-
assert.equal(tree.querySelectorAll("img").length, 0);
|
|
165
|
-
assert.equal(tree.querySelector("label").textContent, "<img src=x onerror=alert(1)>");
|
|
166
|
-
// The second value closes an attribute and opens an event handler, if it
|
|
167
|
-
// is ever concatenated. Through setAttribute it is one attribute's value.
|
|
168
|
-
const input = tree.querySelector("input");
|
|
169
|
-
assert.deepEqual([...input.attributes].map((a) => a.name), ["name"]);
|
|
170
|
-
assert.equal(input.getAttribute("name"), `" onclick="alert(1)`);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
// --- Slots and destinations ---------------------------------------------
|
|
174
|
-
describe("placement", () => {
|
|
175
|
-
it("puts authored content in the slot and removes the marker", () => {
|
|
176
|
-
const html = expand(`<lb-list><p>hello</p></lb-list>`, defs({ "lb-list": `<div lb-slot></div>` }));
|
|
177
|
-
assert.match(html, /<div><p>hello<\/p><\/div>/);
|
|
178
|
-
assert.doesNotMatch(html, /lb-slot/);
|
|
179
|
-
});
|
|
180
|
-
it("unwraps a named template into its destination as real markup", () => {
|
|
181
|
-
// The wrapper existed only to survive the parser. What ships is a real
|
|
182
|
-
// <tr> in a real <thead>, visible in view-source like everything else.
|
|
183
|
-
const html = expand(`<lb-table>` +
|
|
184
|
-
`<template lb-template="head"><tr><th>Name</th></tr></template>` +
|
|
185
|
-
`<template lb-key="id"><tr><td lb-cell="name"></td></tr></template>` +
|
|
186
|
-
`</lb-table>`, defs({
|
|
187
|
-
"lb-table": `<table><thead lb-template="head"></thead><tbody lb-slot></tbody></table>`,
|
|
188
|
-
}));
|
|
189
|
-
assert.match(html, /<thead><tr><th>Name<\/th><\/tr><\/thead>/);
|
|
190
|
-
assert.match(html, /<tbody><template lb-key="id">/);
|
|
191
|
-
assert.doesNotMatch(html, /lb-template/);
|
|
192
|
-
});
|
|
193
|
-
it("does not count whitespace as content", () => {
|
|
194
|
-
// An author who writes only named templates still leaves the newlines
|
|
195
|
-
// between them, and those are the file's formatting.
|
|
196
|
-
const html = expand(`<lb-thing>\n \n</lb-thing>`, defs({ "lb-thing": `<div></div>` }));
|
|
197
|
-
assert.match(html, /<lb-thing><div><\/div><\/lb-thing>/);
|
|
198
|
-
});
|
|
199
|
-
it("expands a widget used inside a definition", () => {
|
|
200
|
-
const html = expand(`<lb-outer></lb-outer>`, defs({
|
|
201
|
-
"lb-outer": `<div><lb-inner exp-label="deep"></lb-inner></div>`,
|
|
202
|
-
"lb-inner": `<span>{{label}}</span>`,
|
|
203
|
-
}));
|
|
204
|
-
assert.match(html, /<span>deep<\/span>/);
|
|
205
|
-
});
|
|
206
|
-
it("expands a widget written inside a page's row template", () => {
|
|
207
|
-
// Template content is markup in a file like any other, and it is inert,
|
|
208
|
-
// so a row template ships already expanded.
|
|
209
|
-
const html = expand(`<lb-list><template lb-key="id"><lb-inner exp-label="row"></lb-inner></template></lb-list>`, defs({
|
|
210
|
-
"lb-list": `<div lb-slot></div>`,
|
|
211
|
-
"lb-inner": `<span>{{label}}</span>`,
|
|
212
|
-
}));
|
|
213
|
-
assert.match(html, /<span>row<\/span>/);
|
|
214
|
-
});
|
|
215
|
-
it("expands a tag outside the lb- namespace when it has a definition", () => {
|
|
216
|
-
// An application's widget can use its own prefix — see docs/tutorials/000-getting-started.md.
|
|
217
|
-
// The definition must still apply; discovering it is not enough.
|
|
218
|
-
const html = expand(`<app-nav exp-label="Site"></app-nav>`, defs({ "app-nav": `<nav aria-label="{{label}}" lb-slot></nav>` }));
|
|
219
|
-
assert.match(html, /<nav aria-label="Site"><\/nav>/);
|
|
220
|
-
});
|
|
221
|
-
it("leaves a tag outside the lb- namespace alone when it has no definition", () => {
|
|
222
|
-
// A plain custom element, registered by script alone — see build/elements.ts.
|
|
223
|
-
const html = expand(`<app-box>hi</app-box>`, defs({}));
|
|
224
|
-
assert.match(html, /<app-box>hi<\/app-box>/);
|
|
225
|
-
});
|
|
226
|
-
it("leaves a tag with no definition alone, whatever its name", () => {
|
|
227
|
-
// No name is special, the hub's own tag included. A tag that has neither
|
|
228
|
-
// a definition nor a script is refused once, in build/elements.ts.
|
|
229
|
-
const html = expand(`<lb-nope></lb-nope><lb-hub></lb-hub>`, defs({}));
|
|
230
|
-
assert.match(html, /<lb-nope><\/lb-nope><lb-hub><\/lb-hub>/);
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
// --- Loading -------------------------------------------------------------
|
|
234
|
-
describe("loadDefinitions", () => {
|
|
235
|
-
it("keys a definition by the tag its filename names", async () => {
|
|
236
|
-
const loaded = await loadDefinitions([`${FIXTURES}/base`]);
|
|
237
|
-
assert.deepEqual(Object.keys(loaded), ["LB-THING"]);
|
|
238
|
-
});
|
|
239
|
-
it("lets a later directory override a built-in", async () => {
|
|
240
|
-
const loaded = await loadDefinitions([
|
|
241
|
-
`${FIXTURES}/base`,
|
|
242
|
-
`${FIXTURES}/override`,
|
|
243
|
-
]);
|
|
244
|
-
assert.match(loaded["LB-THING"], /application/);
|
|
245
|
-
});
|
|
246
|
-
it("skips a directory that is not there", async () => {
|
|
247
|
-
const loaded = await loadDefinitions([
|
|
248
|
-
`${FIXTURES}/nothing-here`,
|
|
249
|
-
`${FIXTURES}/base`,
|
|
250
|
-
]);
|
|
251
|
-
assert.deepEqual(Object.keys(loaded), ["LB-THING"]);
|
|
252
|
-
});
|
|
253
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../../../../../tests/fixtures/elements/collision/imports.ts"],"names":[],"mappings":";AAAA,wBAAgC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default ["@acme/widget"];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"acme-widget.browser.d.ts","sourceRoot":"","sources":["../../../../../../tests/fixtures/elements/collision/widgets/acme-widget.browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app-box.browser.d.ts","sourceRoot":"","sources":["../../../../../../tests/fixtures/elements/local/widgets/app-box.browser.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../../../../../tests/fixtures/elements/manifest/imports.ts"],"names":[],"mappings":";AAAA,wBAAgC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default ["@acme/widget"];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../../../../../tests/fixtures/elements/manifest-bad-entry/imports.ts"],"names":[],"mappings":";AAAA,wBAAoC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default ["@acme/widget", 42];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../../../../../tests/fixtures/elements/manifest-not-array/imports.ts"],"names":[],"mappings":";;;AAAA,wBAAiD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default { "acme-widget": "@acme/widget" };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"acme-widget.browser.d.ts","sourceRoot":"","sources":["../../../../../tests/fixtures/elements/pkg/acme-widget.browser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app-box.d.ts","sourceRoot":"","sources":["../../../../../../tests/fixtures/elements/unmarked/widgets/app-box.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The console is an interface — see docs/testing.md.
|
|
3
|
-
*
|
|
4
|
-
* Loadbare App reports what it survives through `console.warn` and `console.error`:
|
|
5
|
-
* a page that does not exist, a query with no scope, a list widget with no
|
|
6
|
-
* template. The behavior under test in those cases is that Loadbare App reported and
|
|
7
|
-
* carried on, so the report is as much the assertion as the return value is.
|
|
8
|
-
*
|
|
9
|
-
* This is the seam until those calls go through one `report()` in `core/`.
|
|
10
|
-
*/
|
|
11
|
-
type Line = string;
|
|
12
|
-
export interface Captured<T> {
|
|
13
|
-
result: T;
|
|
14
|
-
warn: Line[];
|
|
15
|
-
error: Line[];
|
|
16
|
-
}
|
|
17
|
-
/** Run something with the console collected rather than printed. */
|
|
18
|
-
export declare function captured<T>(run: () => T | Promise<T>): Promise<Captured<T>>;
|
|
19
|
-
export {};
|
|
20
|
-
//# sourceMappingURL=console.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../tests/helpers/console.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,KAAK,IAAI,GAAG,MAAM,CAAC;AAEnB,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,MAAM,EAAE,CAAC,CAAC;IACV,IAAI,EAAE,IAAI,EAAE,CAAC;IACb,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,oEAAoE;AACpE,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,GAAG,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAgBtB"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The console is an interface — see docs/testing.md.
|
|
3
|
-
*
|
|
4
|
-
* Loadbare App reports what it survives through `console.warn` and `console.error`:
|
|
5
|
-
* a page that does not exist, a query with no scope, a list widget with no
|
|
6
|
-
* template. The behavior under test in those cases is that Loadbare App reported and
|
|
7
|
-
* carried on, so the report is as much the assertion as the return value is.
|
|
8
|
-
*
|
|
9
|
-
* This is the seam until those calls go through one `report()` in `core/`.
|
|
10
|
-
*/
|
|
11
|
-
/** Run something with the console collected rather than printed. */
|
|
12
|
-
export async function captured(run) {
|
|
13
|
-
const warn = [];
|
|
14
|
-
const error = [];
|
|
15
|
-
const realWarn = console.warn;
|
|
16
|
-
const realError = console.error;
|
|
17
|
-
// Arguments are joined the way the console would show them, so a test can
|
|
18
|
-
// match one string against the message an author would read.
|
|
19
|
-
console.warn = (...args) => void warn.push(args.join(" "));
|
|
20
|
-
console.error = (...args) => void error.push(args.join(" "));
|
|
21
|
-
try {
|
|
22
|
-
return { result: await run(), warn, error };
|
|
23
|
-
}
|
|
24
|
-
finally {
|
|
25
|
-
console.warn = realWarn;
|
|
26
|
-
console.error = realError;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A document for tier 3 — see docs/testing.md.
|
|
3
|
-
*
|
|
4
|
-
* The landing code touches nothing global: it is handed a root and calls
|
|
5
|
-
* `matches`, `querySelectorAll`, `cloneNode` and `setAttribute` on it. So a
|
|
6
|
-
* test needs a parsed tree and nothing else — no globals installed, no
|
|
7
|
-
* custom elements registered, no window on the loose between test files.
|
|
8
|
-
*
|
|
9
|
-
* Markup is parsed as the body of a document rather than into a detached
|
|
10
|
-
* `<div>`, because the parser's content models are half of what this code
|
|
11
|
-
* exists to work around: a `<tr>` assigned to a div's innerHTML is discarded,
|
|
12
|
-
* and a test that lost its rows would be testing the parser.
|
|
13
|
-
*/
|
|
14
|
-
/** Parse markup and hand back the body holding it. */
|
|
15
|
-
export declare function body(html: string): HTMLElement;
|
|
16
|
-
/** Parse markup and hand back its first element. */
|
|
17
|
-
export declare function one(html: string): Element;
|
|
18
|
-
//# sourceMappingURL=dom.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../../tests/helpers/dom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,sDAAsD;AACtD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAE9C;AAED,oDAAoD;AACpD,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzC"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A document for tier 3 — see docs/testing.md.
|
|
3
|
-
*
|
|
4
|
-
* The landing code touches nothing global: it is handed a root and calls
|
|
5
|
-
* `matches`, `querySelectorAll`, `cloneNode` and `setAttribute` on it. So a
|
|
6
|
-
* test needs a parsed tree and nothing else — no globals installed, no
|
|
7
|
-
* custom elements registered, no window on the loose between test files.
|
|
8
|
-
*
|
|
9
|
-
* Markup is parsed as the body of a document rather than into a detached
|
|
10
|
-
* `<div>`, because the parser's content models are half of what this code
|
|
11
|
-
* exists to work around: a `<tr>` assigned to a div's innerHTML is discarded,
|
|
12
|
-
* and a test that lost its rows would be testing the parser.
|
|
13
|
-
*/
|
|
14
|
-
import { JSDOM } from "jsdom";
|
|
15
|
-
/** Parse markup and hand back the body holding it. */
|
|
16
|
-
export function body(html) {
|
|
17
|
-
return new JSDOM(`<!doctype html><body>${html}</body>`).window.document.body;
|
|
18
|
-
}
|
|
19
|
-
/** Parse markup and hand back its first element. */
|
|
20
|
-
export function one(html) {
|
|
21
|
-
return body(html).firstElementChild;
|
|
22
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A window for tier 4 — see docs/testing.md.
|
|
3
|
-
*
|
|
4
|
-
* Tier 3 needed no globals: the landing code is handed a root and calls
|
|
5
|
-
* methods on it. The hub is the opposite. It extends `HTMLElement`, it
|
|
6
|
-
* registers itself with `customElements` when its module loads, and it
|
|
7
|
-
* reaches `document`, `history` and `location` on its own. So the window has
|
|
8
|
-
* to exist before the module does, which is why the hub is imported
|
|
9
|
-
* dynamically, after `installWindow()` has run:
|
|
10
|
-
*
|
|
11
|
-
* installWindow();
|
|
12
|
-
* await import("../hub/lb-hub.browser");
|
|
13
|
-
*
|
|
14
|
-
* One window per test file, since registration is global and permanent.
|
|
15
|
-
*
|
|
16
|
-
* Two things the hub reaches for do not exist in jsdom 25 and are supplied
|
|
17
|
-
* here. `fetch` is absent, and a scripted one is what a test wants in any
|
|
18
|
-
* case. `HTMLDialogElement.showModal` is absent, so it is recorded rather
|
|
19
|
-
* than performed. Everything else — `history.pushState`, `popstate`, event
|
|
20
|
-
* delegation, form controls — is jsdom's own.
|
|
21
|
-
*/
|
|
22
|
-
import type { HubData, HubRequest } from "../../core/lb-types";
|
|
23
|
-
/** One round trip the hub asked for. */
|
|
24
|
-
export interface Sent {
|
|
25
|
-
page: string;
|
|
26
|
-
request: HubRequest | Record<string, never>;
|
|
27
|
-
}
|
|
28
|
-
export interface Server {
|
|
29
|
-
/** Every round trip, in order. */
|
|
30
|
-
sent: Sent[];
|
|
31
|
-
/** What the next round trip answers with. Replace it per test. */
|
|
32
|
-
answer: HubData;
|
|
33
|
-
/** Set to fail the next round trip instead of answering it. */
|
|
34
|
-
fail?: string;
|
|
35
|
-
}
|
|
36
|
-
/** The scripted server every test in a file shares. */
|
|
37
|
-
export declare const server: Server;
|
|
38
|
-
export declare function installWindow(): void;
|
|
39
|
-
/**
|
|
40
|
-
* Put a built document in place and hand back the hub in it.
|
|
41
|
-
*
|
|
42
|
-
* `pages` are the page hosts the builder would have written, keyed by stub —
|
|
43
|
-
* see build/assemble.ts. `chrome` is what goes inside `<lb-hub>` beside
|
|
44
|
-
* `<main>`. `path` is where the browser is when the hub connects, and it is
|
|
45
|
-
* reset for every mount so no test inherits where the last one navigated to.
|
|
46
|
-
*
|
|
47
|
-
* Connecting the hub is what runs `connectedCallback`, and that navigates,
|
|
48
|
-
* so the caller awaits the first round trip before asserting.
|
|
49
|
-
*/
|
|
50
|
-
export declare function mountHub(chrome: string, pages?: Record<string, string>, path?: string): Promise<Element>;
|
|
51
|
-
/**
|
|
52
|
-
* Let the hub's pending round trip finish.
|
|
53
|
-
*
|
|
54
|
-
* `navigate` is async and nothing hands a test its promise, so a test waits
|
|
55
|
-
* on the microtask queue the way the page does. Two turns: one for the fetch
|
|
56
|
-
* and one for reading its body.
|
|
57
|
-
*/
|
|
58
|
-
export declare function settle(): Promise<void>;
|
|
59
|
-
/**
|
|
60
|
-
* Step back and let the hub answer.
|
|
61
|
-
*
|
|
62
|
-
* `history.back()` is queued rather than synchronous, so a test waits for
|
|
63
|
-
* the `popstate` the hub is listening for and then for the round trip it
|
|
64
|
-
* starts.
|
|
65
|
-
*/
|
|
66
|
-
export declare function back(): Promise<void>;
|
|
67
|
-
/** Fire the event a person's click fires. */
|
|
68
|
-
export declare function click(el: Element): Promise<void>;
|
|
69
|
-
/** Fire the event a form's submit fires. */
|
|
70
|
-
export declare function submit(form: Element): Promise<void>;
|
|
71
|
-
/** Dispatch a request the way a widget does. */
|
|
72
|
-
export declare function send(el: Element, detail: HubRequest | undefined): Promise<void>;
|
|
73
|
-
//# sourceMappingURL=hub.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../../../tests/helpers/hub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAyB/D,wCAAwC;AACxC,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,MAAM;IACrB,kCAAkC;IAClC,IAAI,EAAE,IAAI,EAAE,CAAC;IACb,kEAAkE;IAClE,MAAM,EAAE,OAAO,CAAC;IAChB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,uDAAuD;AACvD,eAAO,MAAM,MAAM,EAAE,MAAiC,CAAC;AAEvD,wBAAgB,aAAa,IAAI,IAAI,CAgDpC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAiB,EAC7C,IAAI,SAAM,GACT,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAG5C;AAED;;;;;;GAMG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAO1C;AAED,6CAA6C;AAC7C,wBAAsB,KAAK,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAKtD;AAED,4CAA4C;AAC5C,wBAAsB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAGzD;AAED,gDAAgD;AAChD,wBAAsB,IAAI,CACxB,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,UAAU,GAAG,SAAS,GAC7B,OAAO,CAAC,IAAI,CAAC,CAGf"}
|