lightview 1.4.2-b → 1.4.7-b
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 +2 -2
- package/codepen-no-tabs-embed.css +2 -0
- package/counter.html +1 -0
- package/jest-puppeteer.config.js +5 -0
- package/jest.config.json +12 -0
- package/lightview.js +151 -109
- package/message.html +13 -0
- package/nested.html +11 -0
- package/package.json +8 -4
- package/test/basic.html +79 -0
- package/test/basic.test.mjs +248 -0
- package/top.html +10 -0
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# lightview v1.4.
|
|
1
|
+
# lightview v1.4.7b (BETA)
|
|
2
2
|
|
|
3
3
|
Small, simple, powerful web UI and micro front end creation ...
|
|
4
4
|
|
|
5
|
-
Great ideas from Svelte, React, Vue and Riot combined into one small tool: <
|
|
5
|
+
Great ideas from Svelte, React, Vue and Riot combined into one small tool: < 7K (minified/gzipped).
|
|
6
6
|
|
|
7
7
|
See the docs and examples at [https://lightview.dev](https://lightview.dev).
|
|
8
8
|
|
package/counter.html
CHANGED
package/jest.config.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"moduleFileExtensions": ["mjs", "js", "jsx", "ts", "tsx", "json", "node"],
|
|
3
|
+
"testMatch": [
|
|
4
|
+
"**/?(*.)test.?js"
|
|
5
|
+
],
|
|
6
|
+
"verbose": true,
|
|
7
|
+
"transform": {},
|
|
8
|
+
"testEnvironment": "jest-environment-node",
|
|
9
|
+
"globalSetup": "jest-environment-puppeteer/setup",
|
|
10
|
+
"globalTeardown": "jest-environment-puppeteer/teardown",
|
|
11
|
+
"testEnvironment": "jest-environment-puppeteer"
|
|
12
|
+
}
|
package/lightview.js
CHANGED
|
@@ -30,8 +30,24 @@ const {observe} = (() => {
|
|
|
30
30
|
let CURRENTOBSERVER;
|
|
31
31
|
const parser = new DOMParser();
|
|
32
32
|
|
|
33
|
-
const
|
|
34
|
-
|
|
33
|
+
const templateSanitizer = (string) => {
|
|
34
|
+
return string.replace(/function\s+/g,"")
|
|
35
|
+
.replace(/function\(/g,"")
|
|
36
|
+
.replace(/=\s*>/g,"")
|
|
37
|
+
.replace(/(while|do|for|alert)\s*\(/g,"")
|
|
38
|
+
.replace(/console\.[a-zA-Z$]+\s*\(/g,"");
|
|
39
|
+
}
|
|
40
|
+
Lightview.sanitizeTemplate = templateSanitizer;
|
|
41
|
+
|
|
42
|
+
const escaper = document.createElement('textarea');
|
|
43
|
+
function escapeHTML(html) {
|
|
44
|
+
escaper.textContent = html;
|
|
45
|
+
return escaper.innerHTML;
|
|
46
|
+
}
|
|
47
|
+
Lightview.escapeHTML = escapeHTML;
|
|
48
|
+
|
|
49
|
+
const addListener = (node, eventName, callback) => {
|
|
50
|
+
node.addEventListener(eventName, callback); // just used to make code footprint smaller
|
|
35
51
|
}
|
|
36
52
|
const anchorHandler = async (event) => {
|
|
37
53
|
event.preventDefault();
|
|
@@ -73,6 +89,7 @@ const {observe} = (() => {
|
|
|
73
89
|
if (toType === "number") return parseFloat(value + "");
|
|
74
90
|
if (toType === "boolean") {
|
|
75
91
|
if (["on", "checked", "selected"].includes(value)) return true;
|
|
92
|
+
if(value==null || value==="") return false;
|
|
76
93
|
try {
|
|
77
94
|
const parsed = JSON.parse(value + "");
|
|
78
95
|
if (typeof (parsed) === "boolean") return parsed;
|
|
@@ -93,9 +110,18 @@ const {observe} = (() => {
|
|
|
93
110
|
if (isfunction) {
|
|
94
111
|
const instance = toType === Date ? new Date() : Object.create(toType.prototype);
|
|
95
112
|
if (instance instanceof Array) {
|
|
96
|
-
|
|
97
|
-
if (!Array.isArray(parsed))
|
|
98
|
-
|
|
113
|
+
let parsed = tryParse(value.startsWith("[") ? value : `[${value}]`);
|
|
114
|
+
if (!Array.isArray(parsed)) {
|
|
115
|
+
if(value.includes(",")) parsed = value.split(",");
|
|
116
|
+
else {
|
|
117
|
+
parsed = tryParse(`["${value}"]`);
|
|
118
|
+
if(!Array.isArray(parsed) || parsed[0]!==value && parsed.length!==1) parsed = null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!Array.isArray(parsed)) {
|
|
122
|
+
throw new TypeError(`Expected an Array for parsed data`)
|
|
123
|
+
}
|
|
124
|
+
instance.push(...parsed);
|
|
99
125
|
} else if (instance instanceof Date) {
|
|
100
126
|
instance.setTime(Date.parse(value));
|
|
101
127
|
} else {
|
|
@@ -131,7 +157,7 @@ const {observe} = (() => {
|
|
|
131
157
|
return [...target];
|
|
132
158
|
}
|
|
133
159
|
if (property === "toString") return function toString() {
|
|
134
|
-
return JSON.stringify(target);
|
|
160
|
+
return JSON.stringify([...target]);
|
|
135
161
|
}
|
|
136
162
|
}
|
|
137
163
|
let value = target[property];
|
|
@@ -218,18 +244,18 @@ const {observe} = (() => {
|
|
|
218
244
|
}
|
|
219
245
|
});
|
|
220
246
|
}
|
|
221
|
-
const createObserver = (domNode,framed) => {
|
|
247
|
+
const createObserver = (domNode, framed) => {
|
|
222
248
|
const observer = new MutationObserver((mutations) => {
|
|
223
249
|
mutations.forEach((mutation) => {
|
|
224
250
|
if (mutation.type === "attributes") {
|
|
225
|
-
if(framed) debugger;
|
|
251
|
+
//if (framed) debugger;
|
|
226
252
|
const name = mutation.attributeName,
|
|
227
253
|
target = mutation.target,
|
|
228
254
|
value = target.getAttribute(name);
|
|
229
|
-
if(framed && name==="message" && target instanceof IFrameElement) {
|
|
230
|
-
if(value) console.log("message",value);
|
|
255
|
+
if (framed && name === "message" && target instanceof IFrameElement) {
|
|
256
|
+
if (value) console.log("message", value);
|
|
231
257
|
target.removeAttribute(name);
|
|
232
|
-
target.dispatchEvent(
|
|
258
|
+
target.dispatchEvent(new CustomEvent("message", {detail: JSON.parse(value)}))
|
|
233
259
|
}
|
|
234
260
|
if (target.observedAttributes && target.observedAttributes.includes(name)) {
|
|
235
261
|
if (value !== mutation.oldValue) {
|
|
@@ -270,7 +296,7 @@ const {observe} = (() => {
|
|
|
270
296
|
nodes.push(node);
|
|
271
297
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
272
298
|
let skip;
|
|
273
|
-
if(node.getAttribute("type")==="radio") nodes.push(node);
|
|
299
|
+
if (node.getAttribute("type") === "radio") nodes.push(node);
|
|
274
300
|
[...node.attributes].forEach((attr) => {
|
|
275
301
|
if (attr.value.includes("${")) {
|
|
276
302
|
attr.template ||= attr.value;
|
|
@@ -286,12 +312,14 @@ const {observe} = (() => {
|
|
|
286
312
|
}
|
|
287
313
|
return nodes;
|
|
288
314
|
}
|
|
289
|
-
const resolveNode = (node, component) => {
|
|
315
|
+
const resolveNode = (node, component,safe) => {
|
|
290
316
|
if (node?.template) {
|
|
291
317
|
try {
|
|
292
|
-
|
|
293
|
-
|
|
318
|
+
let value = Function("context", "with(context) { return `" + Lightview.sanitizeTemplate(node?.template) + "` }")(component.varsProxy);
|
|
319
|
+
value = node.nodeType===Node.TEXT_NODE || !safe ? value : Lightview.escapeHTML(value);
|
|
320
|
+
node.nodeValue = value=="null" || value=="undefined" ? "" : value;
|
|
294
321
|
} catch (e) {
|
|
322
|
+
console.warn(e);
|
|
295
323
|
if (!e.message.includes("defined")) throw e; // actually looking for undefined or not defined
|
|
296
324
|
}
|
|
297
325
|
}
|
|
@@ -308,34 +336,35 @@ const {observe} = (() => {
|
|
|
308
336
|
}
|
|
309
337
|
const inputTypeToType = (inputType) => {
|
|
310
338
|
if (!inputType) return "any"
|
|
311
|
-
if (["text", "tel", "email", "url", "search", "radio","color","password"].includes(inputType)) return "string";
|
|
339
|
+
if (["text", "tel", "email", "url", "search", "radio", "color", "password"].includes(inputType)) return "string";
|
|
312
340
|
if (["number", "range"].includes(inputType)) return "number";
|
|
313
341
|
if (["datetime"].includes(inputType)) return Date;
|
|
314
342
|
if (["checkbox"].includes(inputType)) return "boolean";
|
|
315
343
|
return "any";
|
|
316
344
|
}
|
|
317
|
-
const
|
|
318
|
-
[...node.querySelectorAll('a[href][target^="#"]')].forEach((node) => {
|
|
345
|
+
const importAnchors = (node, component) => {
|
|
346
|
+
[...node.querySelectorAll('a[href$=".html"][target^="#"]')].forEach((node) => {
|
|
319
347
|
node.removeEventListener("click", anchorHandler);
|
|
320
|
-
addListener(node,"click", anchorHandler);
|
|
348
|
+
addListener(node, "click", anchorHandler);
|
|
321
349
|
})
|
|
322
350
|
}
|
|
323
|
-
const bindInput = (input, name, component) => {
|
|
324
|
-
const inputtype = input.tagName === "SELECT" ? "text" : input.getAttribute("type"),
|
|
351
|
+
const bindInput = (input, name, component,value) => {
|
|
352
|
+
const inputtype = input.tagName === "SELECT" || input.tagName === "TEXTAREA" ? "text" : input.getAttribute("type"),
|
|
325
353
|
type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype),
|
|
326
|
-
deflt = input.getAttribute("default")
|
|
327
|
-
|
|
354
|
+
deflt = input.getAttribute("default");
|
|
355
|
+
value ||= input.getAttribute("value");
|
|
328
356
|
let variable = component.vars[name] || {type};
|
|
329
357
|
if (type !== variable.type) {
|
|
330
358
|
if (variable.type === "any" || variable.type === "unknown") variable.type = type;
|
|
331
359
|
else throw new TypeError(`Attempt to bind <input name="${name}" type="${type}"> to variable ${name}:${variable.type}`)
|
|
332
360
|
}
|
|
333
361
|
component.variables({[name]: type});
|
|
362
|
+
component.setValue(name,value);
|
|
334
363
|
let eventname = "change";
|
|
335
|
-
if(input.tagName!=="SELECT" && (!inputtype || ["text","number","tel","email","url","search","password"].includes(inputtype))) {
|
|
364
|
+
if (input.tagName !== "SELECT" && (!inputtype || input.tagName === "TEXTAREA" || ["text", "number", "tel", "email", "url", "search", "password"].includes(inputtype))) {
|
|
336
365
|
eventname = "input";
|
|
337
366
|
}
|
|
338
|
-
addListener(input,eventname, (event) => {
|
|
367
|
+
addListener(input, eventname, (event) => {
|
|
339
368
|
event.stopImmediatePropagation();
|
|
340
369
|
const target = event.target;
|
|
341
370
|
let value = target.value;
|
|
@@ -344,7 +373,7 @@ const {observe} = (() => {
|
|
|
344
373
|
} else if (target.tagName === "SELECT") {
|
|
345
374
|
if (target.hasAttribute("multiple")) {
|
|
346
375
|
value = [...target.querySelectorAll("option")]
|
|
347
|
-
.filter((option) => option.selected || resolveNode(option.attributes.value,component)==value || option.innerText == value)
|
|
376
|
+
.filter((option) => option.selected || resolveNode(option.attributes.value, component) == value || option.innerText == value)
|
|
348
377
|
.map((option) => option.getAttribute("value") || option.innerText);
|
|
349
378
|
}
|
|
350
379
|
}
|
|
@@ -354,21 +383,23 @@ const {observe} = (() => {
|
|
|
354
383
|
const tryParse = (value) => {
|
|
355
384
|
try {
|
|
356
385
|
return JSON.parse(value);
|
|
357
|
-
} catch(e) {
|
|
386
|
+
} catch (e) {
|
|
358
387
|
return value;
|
|
359
388
|
}
|
|
360
389
|
}
|
|
361
390
|
let reserved = {
|
|
391
|
+
any: {value: "any",constant: true},
|
|
362
392
|
boolean: {value: "boolean", constant: true},
|
|
363
393
|
string: {value: "string", constant: true},
|
|
364
394
|
number: {value: "number", constant: true},
|
|
395
|
+
object: {value: "object", constant: true},
|
|
365
396
|
observed: {value: true, constant: true},
|
|
366
397
|
reactive: {value: true, constant: true},
|
|
367
398
|
shared: {value: true, constant: true},
|
|
368
399
|
exported: {value: true, constant: true},
|
|
369
400
|
imported: {value: true, constant: true}
|
|
370
401
|
};
|
|
371
|
-
const createClass = (domElementNode, {observer,
|
|
402
|
+
const createClass = (domElementNode, {observer, framed}) => {
|
|
372
403
|
const instances = new Set(),
|
|
373
404
|
dom = domElementNode.tagName === "TEMPLATE"
|
|
374
405
|
? domElementNode.content.cloneNode(true)
|
|
@@ -382,7 +413,7 @@ const {observe} = (() => {
|
|
|
382
413
|
constructor() {
|
|
383
414
|
super();
|
|
384
415
|
instances.add(this);
|
|
385
|
-
observer ||= createObserver(this,framed);
|
|
416
|
+
observer ||= createObserver(this, framed);
|
|
386
417
|
const currentComponent = this,
|
|
387
418
|
shadow = this.attachShadow({mode: "open"}),
|
|
388
419
|
eventlisteners = {};
|
|
@@ -412,7 +443,7 @@ const {observe} = (() => {
|
|
|
412
443
|
};
|
|
413
444
|
this.defaultAttributes = domElementNode.tagName === "TEMPLATE" ? domElementNode.attributes : dom.attributes;
|
|
414
445
|
this.varsProxy = createVarsProxy(this.vars, this, CustomElement);
|
|
415
|
-
if(framed || CustomElement.lightviewFramed) this.variables({message:Object},{exported:true});
|
|
446
|
+
if (framed || CustomElement.lightviewFramed) this.variables({message: Object}, {exported: true});
|
|
416
447
|
["getElementById", "querySelector", "querySelectorAll"]
|
|
417
448
|
.forEach((fname) => {
|
|
418
449
|
Object.defineProperty(this, fname, {
|
|
@@ -422,7 +453,7 @@ const {observe} = (() => {
|
|
|
422
453
|
})
|
|
423
454
|
});
|
|
424
455
|
[...dom.childNodes].forEach((child) => shadow.appendChild(child.cloneNode(true)));
|
|
425
|
-
|
|
456
|
+
importAnchors(shadow, this);
|
|
426
457
|
}
|
|
427
458
|
|
|
428
459
|
get siblings() {
|
|
@@ -472,51 +503,49 @@ const {observe} = (() => {
|
|
|
472
503
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
473
504
|
// resolve the value before all else;
|
|
474
505
|
const attr = node.attributes.value;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
render(!!attr.template,() => {
|
|
506
|
+
if (attr && attr.template) {
|
|
507
|
+
render(!!attr.template, () => {
|
|
478
508
|
const value = resolveNode(attr, this),
|
|
479
|
-
eltype = resolveNode(node.attributes.type,ctx);
|
|
480
|
-
if(
|
|
481
|
-
|
|
482
|
-
|
|
509
|
+
eltype = resolveNode(node.attributes.type, ctx);
|
|
510
|
+
if(node.attributes.value) {
|
|
511
|
+
const template = attr.template;
|
|
512
|
+
if(/\$\{[a-zA-z_]+\}/g.test(template)) {
|
|
513
|
+
const name = template.substring(2,template.length-1);
|
|
514
|
+
bindInput(node,name,this,value);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (eltype === "checkbox") {
|
|
518
|
+
if (coerce(value, "boolean") === true) {
|
|
519
|
+
node.setAttribute("checked", "");
|
|
483
520
|
node.checked = true;
|
|
484
521
|
} else {
|
|
485
522
|
node.removeAttribute("checked");
|
|
486
523
|
node.checked = false;
|
|
487
524
|
}
|
|
488
|
-
const vname = resolveNode(node.attributes.name,ctx);
|
|
489
|
-
if(vname) ctx.setValue(vname,node.checked,{coerceTo:"boolean"});
|
|
525
|
+
const vname = resolveNode(node.attributes.name, ctx);
|
|
526
|
+
if (vname) ctx.setValue(vname, node.checked, {coerceTo: "boolean"});
|
|
490
527
|
}
|
|
491
|
-
if(node.tagName==="SELECT") {
|
|
528
|
+
if (node.tagName === "SELECT") {
|
|
492
529
|
let values = [value];
|
|
493
|
-
if(node.hasAttribute("multiple")) values = coerce(value,Array);
|
|
530
|
+
if (node.hasAttribute("multiple")) values = coerce(value, Array);
|
|
494
531
|
[...node.querySelectorAll("option")].forEach((option) => {
|
|
495
|
-
if(option.hasAttribute("value")) {
|
|
532
|
+
if (option.hasAttribute("value")) {
|
|
496
533
|
if (values.includes(resolveNode(option.attributes.value, ctx))) {
|
|
497
534
|
option.setAttribute("selected", "");
|
|
498
535
|
option.selected = true;
|
|
499
536
|
}
|
|
500
|
-
} else if(option.innerText.trim()===value) {
|
|
501
|
-
option.setAttribute("selected","");
|
|
537
|
+
} else if (option.innerText.trim() === value) {
|
|
538
|
+
option.setAttribute("selected", "");
|
|
502
539
|
option.selected = true;
|
|
503
540
|
}
|
|
504
541
|
})
|
|
505
542
|
}
|
|
506
543
|
});
|
|
507
|
-
let name;
|
|
508
|
-
for(const vname of this.getVariableNames()) {
|
|
509
|
-
if("${" + vname + "}" === attr.template) {
|
|
510
|
-
name = vname;
|
|
511
|
-
break;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
if(name) bindInput(node,name,ctx);
|
|
515
544
|
}
|
|
516
545
|
[...node.attributes].forEach((attr) => {
|
|
517
|
-
if(attr.name==="value") return;
|
|
546
|
+
if (attr.name === "value" && attr.template) return;
|
|
518
547
|
const {name, value} = attr;
|
|
519
|
-
if(name==="type") {
|
|
548
|
+
if (name === "type") {
|
|
520
549
|
if (value === "radio") {
|
|
521
550
|
const name = resolveNode(node.attributes.name, ctx);
|
|
522
551
|
for (const vname of this.getVariableNames()) {
|
|
@@ -524,7 +553,7 @@ const {observe} = (() => {
|
|
|
524
553
|
render(true, () => {
|
|
525
554
|
const name = resolveNode(node.attributes.name, ctx),
|
|
526
555
|
varvalue = Function("context", "with(context) { return `${" + name + "}` }")(ctx.varsProxy);
|
|
527
|
-
if (varvalue == resolveNode(node.attributes.value,ctx)) {
|
|
556
|
+
if (varvalue == resolveNode(node.attributes.value, ctx)) {
|
|
528
557
|
node.setAttribute("checked", "");
|
|
529
558
|
node.checked = true;
|
|
530
559
|
} else {
|
|
@@ -543,13 +572,13 @@ const {observe} = (() => {
|
|
|
543
572
|
if (type === "") { // name is :something
|
|
544
573
|
render(!!attr.template, () => {
|
|
545
574
|
const value = attr.value,
|
|
546
|
-
elvalue = resolveNode(node.attributes.value,ctx),
|
|
547
|
-
eltype = resolveNode(node.attributes.type,ctx),
|
|
548
|
-
elname = resolveNode(node.attributes.name,ctx);
|
|
575
|
+
elvalue = resolveNode(node.attributes.value, ctx),
|
|
576
|
+
eltype = resolveNode(node.attributes.type, ctx),
|
|
577
|
+
elname = resolveNode(node.attributes.name, ctx);
|
|
549
578
|
if (params[0]) {
|
|
550
579
|
if (value === "true") node.setAttribute(params[0], "")
|
|
551
580
|
else node.removeAttribute(params[0]);
|
|
552
|
-
} else if (eltype=== "checkbox" || node.tagName === "OPTION") {
|
|
581
|
+
} else if (eltype === "checkbox" || node.tagName === "OPTION") {
|
|
553
582
|
if (value === "true") node.setAttribute("checked", "")
|
|
554
583
|
else node.removeAttribute("checked");
|
|
555
584
|
}
|
|
@@ -560,7 +589,7 @@ const {observe} = (() => {
|
|
|
560
589
|
const value = resolveNode(attr, this);
|
|
561
590
|
if (listener) node.removeEventListener(params[0], listener);
|
|
562
591
|
listener = this[value] || window[value] || Function(value);
|
|
563
|
-
addListener(node,params[0], listener);
|
|
592
|
+
addListener(node, params[0], listener);
|
|
564
593
|
})
|
|
565
594
|
} else if (type === "l-if") {
|
|
566
595
|
render(!!attr.template, () => {
|
|
@@ -575,11 +604,13 @@ const {observe} = (() => {
|
|
|
575
604
|
coerced = coerce(value, what === "each" ? Array : "object"),
|
|
576
605
|
target = what === "each" ? coerced : Object[what](coerced),
|
|
577
606
|
html = target.reduce((html, item, i, target) => {
|
|
578
|
-
return html += Function("context", "with(context) { return `" + node.template + "` }")(
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
607
|
+
return html += Function("vars","context", "with(vars) { with(context) { return `" + node.template + "` }}")(
|
|
608
|
+
ctx.varsProxy,
|
|
609
|
+
{
|
|
610
|
+
[vname]: item,
|
|
611
|
+
[index]: i,
|
|
612
|
+
[array]: target
|
|
613
|
+
})
|
|
583
614
|
}, ""),
|
|
584
615
|
parsed = parser.parseFromString(html, "text/html");
|
|
585
616
|
if (!window.lightviewDebug) {
|
|
@@ -611,46 +642,46 @@ const {observe} = (() => {
|
|
|
611
642
|
//Object.defineProperty(this, "adoptedCallback", {configurable: true, writable: true, value});
|
|
612
643
|
}
|
|
613
644
|
|
|
614
|
-
connected(
|
|
615
|
-
this.connectedCallback =
|
|
645
|
+
connected(callback) {
|
|
646
|
+
this.connectedCallback = callback;
|
|
616
647
|
//Object.defineProperty(this, "connectedCallback", {configurable: true, writable: true, value});
|
|
617
648
|
}
|
|
618
649
|
|
|
619
|
-
attributeChanged(
|
|
620
|
-
this.attributeChangedCallback =
|
|
650
|
+
attributeChanged(callback) {
|
|
651
|
+
this.attributeChangedCallback = callback;
|
|
621
652
|
//Object.defineProperty(this, "attributeChangedCallback", {configurable: true, writable: true, value});
|
|
622
653
|
}
|
|
623
654
|
|
|
624
|
-
disconnected(
|
|
655
|
+
disconnected(callback) {
|
|
625
656
|
Object.defineProperty(this, "disconnectedCallback", {
|
|
626
657
|
configurable: true,
|
|
627
658
|
writable: true,
|
|
628
659
|
value: () => {
|
|
629
660
|
value();
|
|
630
|
-
super.disconnectedCallback(
|
|
661
|
+
super.disconnectedCallback(callback);
|
|
631
662
|
}
|
|
632
663
|
});
|
|
633
664
|
}
|
|
634
665
|
|
|
635
666
|
getVariableNames() {
|
|
636
667
|
return Object.keys(this.vars).filter((name) => {
|
|
637
|
-
return !(name in reserved) && !["self","addEventListener","postEvent"].includes(name)
|
|
668
|
+
return !(name in reserved) && !["self", "addEventListener", "postEvent"].includes(name)
|
|
638
669
|
})
|
|
639
670
|
}
|
|
640
671
|
|
|
641
|
-
setValue(
|
|
672
|
+
setValue(variableName, value, {coerceTo = typeof (value)} = {}) {
|
|
642
673
|
if (!this.isConnected) {
|
|
643
674
|
instances.delete(this);
|
|
644
675
|
return false;
|
|
645
676
|
}
|
|
646
|
-
let {type} = this.vars[
|
|
677
|
+
let {type} = this.vars[variableName] || {};
|
|
647
678
|
if (type) {
|
|
648
679
|
value = coerce(value, type);
|
|
649
|
-
if (this.varsProxy[
|
|
650
|
-
const variable = this.vars[
|
|
680
|
+
if (this.varsProxy[variableName] !== value) {
|
|
681
|
+
const variable = this.vars[variableName];
|
|
651
682
|
if (variable.shared) {
|
|
652
683
|
const event = new VariableEvent({
|
|
653
|
-
variableName:
|
|
684
|
+
variableName: variableName,
|
|
654
685
|
value: value,
|
|
655
686
|
oldValue: variable.value
|
|
656
687
|
});
|
|
@@ -658,12 +689,12 @@ const {observe} = (() => {
|
|
|
658
689
|
this.vars.postEvent.value("change", event);
|
|
659
690
|
if (event.defaultPrevented) variable.value = value;
|
|
660
691
|
} else {
|
|
661
|
-
this.varsProxy[
|
|
692
|
+
this.varsProxy[variableName] = value;
|
|
662
693
|
}
|
|
663
694
|
}
|
|
664
695
|
return true;
|
|
665
696
|
}
|
|
666
|
-
this.vars[
|
|
697
|
+
this.vars[variableName] = {name, type: coerceTo, value: coerce(value, coerceTo)};
|
|
667
698
|
return false;
|
|
668
699
|
}
|
|
669
700
|
|
|
@@ -732,48 +763,57 @@ const {observe} = (() => {
|
|
|
732
763
|
}
|
|
733
764
|
}
|
|
734
765
|
}
|
|
735
|
-
const createComponent = (name, node, {observer
|
|
766
|
+
const createComponent = (name, node, {framed,observer} = {}) => {
|
|
736
767
|
let ctor = customElements.get(name);
|
|
737
768
|
if (ctor) {
|
|
738
|
-
if(framed && !ctor.lightviewFramed) {
|
|
769
|
+
if (framed && !ctor.lightviewFramed) {
|
|
739
770
|
ctor.lightviewFramed = true;
|
|
740
771
|
} else {
|
|
741
772
|
console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
|
|
742
773
|
}
|
|
743
774
|
return ctor;
|
|
744
775
|
}
|
|
745
|
-
ctor = createClass(node, {observer,
|
|
776
|
+
ctor = createClass(node, {observer, framed});
|
|
746
777
|
customElements.define(name, ctor);
|
|
778
|
+
Lightview.customElements.set(name,ctor);
|
|
747
779
|
return ctor;
|
|
748
780
|
}
|
|
781
|
+
Lightview.customElements = new Map();
|
|
749
782
|
Lightview.createComponent = createComponent;
|
|
750
783
|
//Object.defineProperty(Lightview, "createComponent", {writable: true, configurable: true, value: createComponent})
|
|
751
784
|
const importLink = async (link, observer) => {
|
|
752
785
|
const url = (new URL(link.getAttribute("href"), window.location.href)),
|
|
753
786
|
as = link.getAttribute("as") || getNameFromPath(url.pathname);
|
|
754
|
-
if (url.hostname !== window.location.hostname) {
|
|
755
|
-
throw new URIError(`importLink:HTML imports must be from same domain: ${url.hostname}!=${location.hostname}
|
|
787
|
+
if (url.hostname !== window.location.hostname && !link.getAttribute("crossorigin")) {
|
|
788
|
+
throw new URIError(`importLink:HTML imports must be from same domain: ${url.hostname}!=${location.hostname} unless 'crossorigin' attribute is set.`)
|
|
756
789
|
}
|
|
757
790
|
if (!customElements.get(as)) {
|
|
758
791
|
const html = await (await fetch(url.href)).text(),
|
|
759
792
|
dom = parser.parseFromString(html, "text/html"),
|
|
760
|
-
|
|
761
|
-
|
|
793
|
+
unhide = !!dom.head.querySelector('meta[name="l-unhide"]'),
|
|
794
|
+
links = dom.head.querySelectorAll('link[href$=".html"][rel=module]');
|
|
795
|
+
for(const childlink of links) {
|
|
796
|
+
const href = childlink.getAttribute("href"),
|
|
797
|
+
childurl = new URL(href,url.href);
|
|
798
|
+
childlink.setAttribute("href",childurl.href);
|
|
799
|
+
if(link.hasAttribute("crossorigin")) childlink.setAttribute("crossorigin",link.getAttribute("crossorigin"))
|
|
800
|
+
await importLink(childlink,observer);
|
|
801
|
+
}
|
|
762
802
|
if (unhide) dom.body.removeAttribute("hidden");
|
|
763
|
-
createComponent(as, dom.body, {observer
|
|
803
|
+
createComponent(as, dom.body, {observer});
|
|
764
804
|
}
|
|
765
805
|
return {as};
|
|
766
806
|
}
|
|
767
807
|
const importLinks = async () => {
|
|
768
808
|
const observer = createObserver(document.body);
|
|
769
|
-
for (const link of [...document.querySelectorAll(
|
|
770
|
-
await importLink(link);
|
|
809
|
+
for (const link of [...document.querySelectorAll(`link[href$=".html"][rel=module]`)]) {
|
|
810
|
+
await importLink(link,observer);
|
|
771
811
|
}
|
|
772
812
|
}
|
|
773
813
|
|
|
774
|
-
const bodyAsComponent = ({as = "x-body", unhide,
|
|
814
|
+
const bodyAsComponent = ({as = "x-body", unhide, framed} = {}) => {
|
|
775
815
|
const parent = document.body.parentElement;
|
|
776
|
-
createComponent(as, document.body, {
|
|
816
|
+
createComponent(as, document.body, {framed});
|
|
777
817
|
const component = document.createElement(as);
|
|
778
818
|
parent.replaceChild(component, document.body);
|
|
779
819
|
Object.defineProperty(document, "body", {
|
|
@@ -815,19 +855,18 @@ const {observe} = (() => {
|
|
|
815
855
|
|
|
816
856
|
const url = new URL(document.currentScript.getAttribute("src"), window.location.href);
|
|
817
857
|
let domContentLoadedEvent;
|
|
818
|
-
if(!domContentLoadedEvent) addListener(window,"DOMContentLoaded", (event) => domContentLoadedEvent = event);
|
|
858
|
+
if (!domContentLoadedEvent) addListener(window, "DOMContentLoaded", (event) => domContentLoadedEvent = event);
|
|
819
859
|
let OBSERVER;
|
|
820
860
|
const loader = async (whenFramed) => {
|
|
821
|
-
|
|
822
|
-
const
|
|
823
|
-
unhide = !!document.querySelector('meta[name="l-unhide"]'),
|
|
861
|
+
await importLinks();
|
|
862
|
+
const unhide = !!document.querySelector('meta[name="l-unhide"]'),
|
|
824
863
|
isolated = !!document.querySelector('meta[name="l-isolate"]'),
|
|
825
864
|
enableFrames = !!document.querySelector('meta[name="l-enableFrames"]');
|
|
826
865
|
if (whenFramed) {
|
|
827
|
-
whenFramed({unhide,
|
|
866
|
+
whenFramed({unhide, isolated, enableFrames, framed: true});
|
|
828
867
|
if (!isolated) {
|
|
829
868
|
postMessage.enabled = true;
|
|
830
|
-
addListener(window,"message", ({data}) => {
|
|
869
|
+
addListener(window, "message", ({data}) => {
|
|
831
870
|
const {type, argsList} = JSON.parse(data);
|
|
832
871
|
if (type === "framed") {
|
|
833
872
|
const resize = () => {
|
|
@@ -859,11 +898,11 @@ const {observe} = (() => {
|
|
|
859
898
|
postMessage({type: "DOMContentLoaded"})
|
|
860
899
|
}
|
|
861
900
|
} else if (url.searchParams.has("as")) {
|
|
862
|
-
bodyAsComponent({as: url.searchParams.get("as"), unhide
|
|
901
|
+
bodyAsComponent({as: url.searchParams.get("as"), unhide});
|
|
863
902
|
}
|
|
864
903
|
if (enableFrames) {
|
|
865
904
|
postMessage.enabled = true;
|
|
866
|
-
addListener(window,"message", (message) => {
|
|
905
|
+
addListener(window, "message", (message) => {
|
|
867
906
|
const {type, iframeId, argsList, href} = JSON.parse(message.data),
|
|
868
907
|
iframe = document.getElementById(iframeId);
|
|
869
908
|
if (iframe) {
|
|
@@ -900,7 +939,7 @@ const {observe} = (() => {
|
|
|
900
939
|
}
|
|
901
940
|
console.warn("iframe posted a message without providing an id", message);
|
|
902
941
|
});
|
|
903
|
-
if(!OBSERVER) {
|
|
942
|
+
if (!OBSERVER) {
|
|
904
943
|
const mutationCallback = (mutationsList) => {
|
|
905
944
|
const console = document.getElementById("console");
|
|
906
945
|
for (const {target, attributeName, oldValue} of mutationsList) {
|
|
@@ -914,13 +953,16 @@ const {observe} = (() => {
|
|
|
914
953
|
}, iframe)
|
|
915
954
|
}
|
|
916
955
|
}
|
|
917
|
-
if(attributeName==="message") {
|
|
918
|
-
if(value) {
|
|
956
|
+
if (attributeName === "message") {
|
|
957
|
+
if (value) {
|
|
919
958
|
target.removeAttribute("message");
|
|
920
|
-
target.dispatchEvent(new CustomEvent("message",{target,detail:JSON.parse(value)}))
|
|
959
|
+
target.dispatchEvent(new CustomEvent("message", {target, detail: JSON.parse(value)}))
|
|
921
960
|
}
|
|
922
961
|
} else {
|
|
923
|
-
target.dispatchEvent(new CustomEvent("attribute.changed",{
|
|
962
|
+
target.dispatchEvent(new CustomEvent("attribute.changed", {
|
|
963
|
+
target,
|
|
964
|
+
detail: {attributeName, value, oldValue}
|
|
965
|
+
}))
|
|
924
966
|
}
|
|
925
967
|
}
|
|
926
968
|
};
|
|
@@ -930,17 +972,17 @@ const {observe} = (() => {
|
|
|
930
972
|
}
|
|
931
973
|
}
|
|
932
974
|
}
|
|
933
|
-
const whenFramed = (
|
|
975
|
+
const whenFramed = (callback, {isolated} = {}) => {
|
|
934
976
|
// loads for framed content
|
|
935
|
-
addListener(document,"DOMContentLoaded", (event) => loader(
|
|
977
|
+
addListener(document, "DOMContentLoaded", (event) => loader(callback));
|
|
936
978
|
}
|
|
937
979
|
Lightview.whenFramed = whenFramed;
|
|
938
980
|
//Object.defineProperty(Lightview, "whenFramed", {configurable: true, writable: true, value: whenFramed});
|
|
939
981
|
if (window.location === window.parent.location || !(window.parent instanceof Window) || window.parent !== window) {
|
|
940
982
|
// loads for unframed content
|
|
941
983
|
// CodePen mucks with window.parent
|
|
942
|
-
addListener(document,"DOMContentLoaded", () => loader())
|
|
984
|
+
addListener(document, "DOMContentLoaded", () => loader())
|
|
943
985
|
}
|
|
944
986
|
|
|
945
987
|
return {observe}
|
|
946
|
-
})();
|
|
988
|
+
})();
|
package/message.html
ADDED
package/nested.html
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<head>
|
|
3
|
+
<title>Nested</title>
|
|
4
|
+
<link href="./message.html" rel="module">
|
|
5
|
+
<script src="./lightview.js?as=x-body"></script>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<l-message value="Hello One"></l-message>
|
|
9
|
+
<l-message value="Hello Two"></l-message>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lightview",
|
|
3
|
-
"version": "1.4.
|
|
4
|
-
"description": "Small, simple, powerful web UI and micro front end creation ...
|
|
3
|
+
"version": "1.4.7b",
|
|
4
|
+
"description": "Small, simple, powerful web UI and micro front end creation ... Great ideas from Svelte, React, Vue and Riot combined.",
|
|
5
5
|
"main": "lightview.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
7
|
+
"test": "set NODE_OPTIONS=--experimental-vm-modules && jest ./test"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -28,5 +28,9 @@
|
|
|
28
28
|
"bugs": {
|
|
29
29
|
"url": "https://github.com/anywhichway/lightview/issues"
|
|
30
30
|
},
|
|
31
|
-
"homepage": "https://github.com/anywhichway/lightview#readme"
|
|
31
|
+
"homepage": "https://github.com/anywhichway/lightview#readme",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"jest": "^27.5.1",
|
|
34
|
+
"jest-puppeteer": "^6.1.0"
|
|
35
|
+
}
|
|
32
36
|
}
|
package/test/basic.html
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Basic</title>
|
|
6
|
+
<template id="x-test" name="joe" open="true" count=1 children='["mary"]' l-on:click="bump">
|
|
7
|
+
<span id="name">${name}</span>
|
|
8
|
+
<span id="open">${open}</span>
|
|
9
|
+
<span id="count">${count}</span>
|
|
10
|
+
<span id="children">${children}</span>
|
|
11
|
+
<span id="color">${color}</span>
|
|
12
|
+
<span id="checked">${checked}</span>
|
|
13
|
+
<span id="age">${age}</span>
|
|
14
|
+
<span id="hamburger">${hamburger}</span>
|
|
15
|
+
|
|
16
|
+
<input id="iuntyped" value="${iuntyped}">
|
|
17
|
+
<input id="itext" type="text" value="${itext}">
|
|
18
|
+
<input id="itel" type="tel" value="${itel}">
|
|
19
|
+
<input id="iemail" type="email" value="${iemail}">
|
|
20
|
+
<input id="iurl" type="url" value="${iurl}">
|
|
21
|
+
<input id="isearch" type="search" value="${isearch}">
|
|
22
|
+
<input id="iradio" type="radio" value="${iradio}">
|
|
23
|
+
<input id="icolor" type="color" value="${icolor}">
|
|
24
|
+
<input id="ipassword" type="password" value="${ipassword}">
|
|
25
|
+
|
|
26
|
+
<input id="inumber" type="number" value="${inumber}">
|
|
27
|
+
<input id="irange" type="range" value="${irange}">
|
|
28
|
+
|
|
29
|
+
<input id="idatetime" type="datetime" value="${idatetime}">
|
|
30
|
+
|
|
31
|
+
<input id="icheckbox" type="checkbox" value="${icheckbox}">
|
|
32
|
+
|
|
33
|
+
<script type="lightview/module">
|
|
34
|
+
debugger;
|
|
35
|
+
self.variables({name:string,open:boolean,count:number,children:Array},{imported,reactive});
|
|
36
|
+
self.variables({color:string,checked:boolean,age:number,hamburger:Array},{exported,reactive});
|
|
37
|
+
self.variables({counter:number},{reactive});
|
|
38
|
+
self.variables({myshare:number},{shared});
|
|
39
|
+
|
|
40
|
+
color = "green";
|
|
41
|
+
checked = true;
|
|
42
|
+
age = 27;
|
|
43
|
+
hamburger = ["lettuce"];
|
|
44
|
+
counter = 0;
|
|
45
|
+
myshare = 1;
|
|
46
|
+
|
|
47
|
+
iuntyped = "test";
|
|
48
|
+
itext = "test";
|
|
49
|
+
itel = "test";
|
|
50
|
+
iemail = "test";
|
|
51
|
+
iurl = "test";
|
|
52
|
+
isearch = "test";
|
|
53
|
+
iradio = "test";
|
|
54
|
+
icolor = "test";
|
|
55
|
+
ipassword = "test";
|
|
56
|
+
|
|
57
|
+
inumber = 1;
|
|
58
|
+
irange = 1;
|
|
59
|
+
|
|
60
|
+
idatetime = new Date();
|
|
61
|
+
|
|
62
|
+
icheckbox = true;
|
|
63
|
+
|
|
64
|
+
self.bump = () => {
|
|
65
|
+
counter++;
|
|
66
|
+
};
|
|
67
|
+
</script>
|
|
68
|
+
</template>
|
|
69
|
+
<script src="../lightview.js"></script>
|
|
70
|
+
<script>
|
|
71
|
+
Lightview.createComponent("x-test",document.getElementById("x-test"));
|
|
72
|
+
</script>
|
|
73
|
+
</head>
|
|
74
|
+
<body>
|
|
75
|
+
<p><x-test id="test"></x-test></p>
|
|
76
|
+
|
|
77
|
+
<p><x-test id="test1"></x-test></p>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import 'expect-puppeteer';
|
|
2
|
+
|
|
3
|
+
describe('Google', () => {
|
|
4
|
+
beforeAll(async () => {
|
|
5
|
+
await page.goto('https://google.com');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
test('should be titled "Google"', async () => {
|
|
9
|
+
await expect(page.title()).resolves.toMatch('Google');
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('Lightview', () => {
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
await page.goto('http://localhost:8080/test/basic.html');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('should be titled "Basic"', async () => {
|
|
19
|
+
await expect(page.title()).resolves.toMatch('Basic');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('boolean - open should be imported', async () => {
|
|
23
|
+
const result = await page.evaluate(() => {
|
|
24
|
+
const el = document.getElementById("test");
|
|
25
|
+
return JSON.parse(el.getValue("open"));
|
|
26
|
+
});
|
|
27
|
+
expect(result).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('number - count should be imported', async () => {
|
|
31
|
+
const result = await page.evaluate(() => {
|
|
32
|
+
const el = document.getElementById("test");
|
|
33
|
+
return JSON.parse(el.getValue("count"));
|
|
34
|
+
});
|
|
35
|
+
expect(result).toBe(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('string - name should be imported', async () => {
|
|
39
|
+
const result = await page.evaluate(() => {
|
|
40
|
+
const el = document.getElementById("test");
|
|
41
|
+
return el.getValue("name");
|
|
42
|
+
});
|
|
43
|
+
expect(result).toBe("joe");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('object - children should be imported', async () => {
|
|
47
|
+
const result = await page.evaluate(() => {
|
|
48
|
+
const el = document.getElementById("test");
|
|
49
|
+
return el.getValue("children").toJSON();
|
|
50
|
+
});
|
|
51
|
+
expect(Array.isArray(result)).toBe(true);
|
|
52
|
+
expect(result[0]).toBe("mary");
|
|
53
|
+
expect(result.length).toBe(1);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('boolean - checked should be exported', async () => {
|
|
57
|
+
const result = await page.evaluate(() => {
|
|
58
|
+
const el = document.getElementById("test");
|
|
59
|
+
return JSON.parse(el.getAttribute("checked"));
|
|
60
|
+
});
|
|
61
|
+
expect(result).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('number - age should be exported', async () => {
|
|
65
|
+
const result = await page.evaluate(() => {
|
|
66
|
+
const el = document.getElementById("test");
|
|
67
|
+
return JSON.parse(el.getAttribute("age"));
|
|
68
|
+
});
|
|
69
|
+
expect(result).toBe(27);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('string - color should be exported', async () => {
|
|
73
|
+
const result = await page.evaluate(() => {
|
|
74
|
+
const el = document.getElementById("test");
|
|
75
|
+
return el.getAttribute("color");
|
|
76
|
+
});
|
|
77
|
+
expect(result).toBe("green");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('object - hamburger should be exported', async () => {
|
|
81
|
+
const result = await page.evaluate(() => {
|
|
82
|
+
const el = document.getElementById("test");
|
|
83
|
+
return JSON.parse(el.getAttribute("hamburger"));
|
|
84
|
+
});
|
|
85
|
+
expect(Array.isArray(result)).toBe(true);
|
|
86
|
+
expect(result[0]).toBe("lettuce");
|
|
87
|
+
expect(result.length).toBe(1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('boolean - open should be rendered', async () => {
|
|
91
|
+
const result = await page.evaluate(() => {
|
|
92
|
+
const el = document.getElementById("test"),
|
|
93
|
+
result = el.getElementById("open");
|
|
94
|
+
return JSON.parse(result.innerText);
|
|
95
|
+
});
|
|
96
|
+
expect(result).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('number - count should be rendered', async () => {
|
|
100
|
+
const result = await page.evaluate(() => {
|
|
101
|
+
const el = document.getElementById("test"),
|
|
102
|
+
result = el.getElementById("count");
|
|
103
|
+
return JSON.parse(result.innerText);
|
|
104
|
+
});
|
|
105
|
+
expect(result).toBe(1);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('string - name should be rendered', async () => {
|
|
109
|
+
const result = await page.evaluate(() => {
|
|
110
|
+
const el = document.getElementById("test"),
|
|
111
|
+
result = el.getElementById("name");
|
|
112
|
+
return result.innerText;
|
|
113
|
+
});
|
|
114
|
+
expect(result).toBe("joe");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('object - children should be rendered', async () => {
|
|
118
|
+
const result = await page.evaluate(() => {
|
|
119
|
+
const el = document.getElementById("test"),
|
|
120
|
+
result = el.getElementById("children");
|
|
121
|
+
return JSON.parse(result.innerText);
|
|
122
|
+
});
|
|
123
|
+
expect(Array.isArray(result)).toBe(true);
|
|
124
|
+
expect(result[0]).toBe("mary");
|
|
125
|
+
expect(result.length).toBe(1);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('boolean - checked should be rendered', async () => {
|
|
129
|
+
const result = await page.evaluate(() => {
|
|
130
|
+
const el = document.getElementById("test"),
|
|
131
|
+
result = el.getElementById("checked");
|
|
132
|
+
return JSON.parse(result.innerText);
|
|
133
|
+
});
|
|
134
|
+
expect(result).toBe(true);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('number - age should be rendered', async () => {
|
|
138
|
+
const result = await page.evaluate(() => {
|
|
139
|
+
const el = document.getElementById("test"),
|
|
140
|
+
result = el.getElementById("age");
|
|
141
|
+
return JSON.parse(result.innerText);
|
|
142
|
+
});
|
|
143
|
+
expect(result).toBe(27);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('string - color should be rendered', async () => {
|
|
147
|
+
const result = await page.evaluate(() => {
|
|
148
|
+
const el = document.getElementById("test"),
|
|
149
|
+
result = el.getElementById("color");
|
|
150
|
+
return result.innerText;
|
|
151
|
+
});
|
|
152
|
+
expect(result).toBe("green");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('object - hamburger should be rendered', async () => {
|
|
156
|
+
const result = await page.evaluate(() => {
|
|
157
|
+
const el = document.getElementById("test"),
|
|
158
|
+
result = el.getElementById("hamburger");
|
|
159
|
+
return JSON.parse(result.innerText);
|
|
160
|
+
});
|
|
161
|
+
expect(Array.isArray(result)).toBe(true);
|
|
162
|
+
expect(result[0]).toBe("lettuce");
|
|
163
|
+
expect(result.length).toBe(1);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('shared - myshare should be same', async () => {
|
|
167
|
+
const result = await page.evaluate(async () => {
|
|
168
|
+
const el0 = document.getElementById("test"),
|
|
169
|
+
el1 = document.getElementById("test1")
|
|
170
|
+
return [el0.getValue("myshare"),el1.getValue("myshare")];
|
|
171
|
+
});
|
|
172
|
+
expect(Array.isArray(result)).toBe(true);
|
|
173
|
+
expect(result[0]).toBe(result[1]);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('untyped input - iuntyped should be "test"', async () => {
|
|
177
|
+
const result = await page.evaluate(async () => {
|
|
178
|
+
const el = document.getElementById("test"),
|
|
179
|
+
result = el.getElementById("iuntyped")
|
|
180
|
+
return result.getAttribute("value");
|
|
181
|
+
});
|
|
182
|
+
expect(result).toBe("test");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// "tel", "email", "url", "search", "radio", "color", "password"
|
|
186
|
+
["text","tel","email", "url", "search", "radio", "color", "password"].forEach((type) => {
|
|
187
|
+
const f = Function(`return async () => {
|
|
188
|
+
const result = await page.evaluate(async () => {
|
|
189
|
+
const el = document.getElementById("test"),
|
|
190
|
+
result = el.getElementById("i${type}");
|
|
191
|
+
return {value:result.getAttribute("value"),variable:el.vars["i${type}"]};
|
|
192
|
+
});
|
|
193
|
+
const {value,variable} = result;
|
|
194
|
+
expect(value).toBe("test");
|
|
195
|
+
expect(variable.name).toBe("i${type}");
|
|
196
|
+
expect(variable.type).toBe("string");
|
|
197
|
+
expect(variable.value).toBe(value);
|
|
198
|
+
}`)();
|
|
199
|
+
test(`${type} input - i${type} should be "test"`,f);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('number input - inumber should be 1', async () => {
|
|
203
|
+
const result = await page.evaluate(async () => {
|
|
204
|
+
const el = document.getElementById("test"),
|
|
205
|
+
result = el.getElementById("inumber")
|
|
206
|
+
return JSON.parse(result.getAttribute("value"));
|
|
207
|
+
});
|
|
208
|
+
expect(result).toBe(1);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('range input - irange should be 1', async () => {
|
|
212
|
+
const result = await page.evaluate(async () => {
|
|
213
|
+
const el = document.getElementById("test"),
|
|
214
|
+
result = el.getElementById("irange")
|
|
215
|
+
return JSON.parse(result.getAttribute("value"));
|
|
216
|
+
});
|
|
217
|
+
expect(result).toBe(1);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('datetime input - idatetime should be current date', async () => {
|
|
221
|
+
const result = await page.evaluate(async () => {
|
|
222
|
+
const el = document.getElementById("test"),
|
|
223
|
+
result = el.getElementById("idatetime")
|
|
224
|
+
return result.getAttribute("value");
|
|
225
|
+
});
|
|
226
|
+
const dt = new Date(result);
|
|
227
|
+
expect(dt).toBeInstanceOf(Date);
|
|
228
|
+
expect(dt.toString()).toBe(result);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('checkbox input - icheckbox should be true', async () => {
|
|
232
|
+
const result = await page.evaluate(async () => {
|
|
233
|
+
const el = document.getElementById("test"),
|
|
234
|
+
result = el.getElementById("icheckbox")
|
|
235
|
+
return JSON.parse(result.getAttribute("value"));
|
|
236
|
+
});
|
|
237
|
+
expect(result).toBe(true);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('on:<handler> - count should be bumped', async () => {
|
|
241
|
+
await page.click("#test",{waitUntil:"load"});
|
|
242
|
+
const result = await page.evaluate(async () => {
|
|
243
|
+
const el = document.getElementById("test");
|
|
244
|
+
return JSON.parse(el.getValue("counter"));
|
|
245
|
+
});
|
|
246
|
+
expect(result).toBe(1);
|
|
247
|
+
});
|
|
248
|
+
});
|