lightview 1.4.1-b → 1.4.5-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 CHANGED
@@ -1,4 +1,4 @@
1
- # lightview v1.4.1b (BETA)
1
+ # lightview v1.4.5b (BETA)
2
2
 
3
3
  Small, simple, powerful web UI and micro front end creation ...
4
4
 
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ server: {
3
+ command: 'http-server'
4
+ }
5
+ }
@@ -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,8 @@ const {observe} = (() => {
30
30
  let CURRENTOBSERVER;
31
31
  const parser = new DOMParser();
32
32
 
33
- const addListener = (node,eventName,callback) => {
34
- node.addEventListener(eventName,callback); // just used to make code footprint smaller
33
+ const addListener = (node, eventName, callback) => {
34
+ node.addEventListener(eventName, callback); // just used to make code footprint smaller
35
35
  }
36
36
  const anchorHandler = async (event) => {
37
37
  event.preventDefault();
@@ -73,6 +73,7 @@ const {observe} = (() => {
73
73
  if (toType === "number") return parseFloat(value + "");
74
74
  if (toType === "boolean") {
75
75
  if (["on", "checked", "selected"].includes(value)) return true;
76
+ if(value==null || value==="") return false;
76
77
  try {
77
78
  const parsed = JSON.parse(value + "");
78
79
  if (typeof (parsed) === "boolean") return parsed;
@@ -93,9 +94,18 @@ const {observe} = (() => {
93
94
  if (isfunction) {
94
95
  const instance = toType === Date ? new Date() : Object.create(toType.prototype);
95
96
  if (instance instanceof Array) {
96
- const parsed = JSON.parse(value.startsWith("[") ? value : `[${value}]`);
97
- if (!Array.isArray(parsed)) throw new TypeError(`Expected an Array for parsed data`)
98
- parsed.forEach((item) => instance.push(item))
97
+ let parsed = tryParse(value.startsWith("[") ? value : `[${value}]`);
98
+ if (!Array.isArray(parsed)) {
99
+ if(value.includes(",")) parsed = value.split(",");
100
+ else {
101
+ parsed = tryParse(`["${value}"]`);
102
+ if(!Array.isArray(parsed) || parsed[0]!==value && parsed.length!==1) parsed = null;
103
+ }
104
+ }
105
+ if (!Array.isArray(parsed)) {
106
+ throw new TypeError(`Expected an Array for parsed data`)
107
+ }
108
+ instance.push(...parsed);
99
109
  } else if (instance instanceof Date) {
100
110
  instance.setTime(Date.parse(value));
101
111
  } else {
@@ -131,7 +141,7 @@ const {observe} = (() => {
131
141
  return [...target];
132
142
  }
133
143
  if (property === "toString") return function toString() {
134
- return JSON.stringify(target);
144
+ return JSON.stringify([...target]);
135
145
  }
136
146
  }
137
147
  let value = target[property];
@@ -218,14 +228,20 @@ const {observe} = (() => {
218
228
  }
219
229
  });
220
230
  }
221
- const createObserver = (domNode) => {
231
+ const createObserver = (domNode, framed) => {
222
232
  const observer = new MutationObserver((mutations) => {
223
233
  mutations.forEach((mutation) => {
224
234
  if (mutation.type === "attributes") {
235
+ if (framed) debugger;
225
236
  const name = mutation.attributeName,
226
- target = mutation.target;
237
+ target = mutation.target,
238
+ value = target.getAttribute(name);
239
+ if (framed && name === "message" && target instanceof IFrameElement) {
240
+ if (value) console.log("message", value);
241
+ target.removeAttribute(name);
242
+ target.dispatchEvent(new CustomEvent("message", {detail: JSON.parse(value)}))
243
+ }
227
244
  if (target.observedAttributes && target.observedAttributes.includes(name)) {
228
- const value = target.getAttribute(name);
229
245
  if (value !== mutation.oldValue) {
230
246
  target.setValue(name, value);
231
247
  if (target.attributeChangedCallback) target.attributeChangedCallback(name, value, mutation.oldValue);
@@ -264,7 +280,7 @@ const {observe} = (() => {
264
280
  nodes.push(node);
265
281
  } else if (node.nodeType === Node.ELEMENT_NODE) {
266
282
  let skip;
267
- if(node.getAttribute("type")==="radio") nodes.push(node);
283
+ if (node.getAttribute("type") === "radio") nodes.push(node);
268
284
  [...node.attributes].forEach((attr) => {
269
285
  if (attr.value.includes("${")) {
270
286
  attr.template ||= attr.value;
@@ -302,7 +318,7 @@ const {observe} = (() => {
302
318
  }
303
319
  const inputTypeToType = (inputType) => {
304
320
  if (!inputType) return "any"
305
- if (["text", "tel", "email", "url", "search", "radio","color","password"].includes(inputType)) return "string";
321
+ if (["text", "tel", "email", "url", "search", "radio", "color", "password"].includes(inputType)) return "string";
306
322
  if (["number", "range"].includes(inputType)) return "number";
307
323
  if (["datetime"].includes(inputType)) return Date;
308
324
  if (["checkbox"].includes(inputType)) return "boolean";
@@ -311,44 +327,45 @@ const {observe} = (() => {
311
327
  const _importAnchors = (node, component) => {
312
328
  [...node.querySelectorAll('a[href][target^="#"]')].forEach((node) => {
313
329
  node.removeEventListener("click", anchorHandler);
314
- addListener(node,"click", anchorHandler);
330
+ addListener(node, "click", anchorHandler);
315
331
  })
316
332
  }
317
- const bindInput = (input, name, component) => {
318
- const inputtype = input.tagName === "SELECT" ? "text" : input.getAttribute("type"),
319
- type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype),
320
- deflt = input.getAttribute("default"),
321
- value = input.getAttribute("value");
322
- let variable = component.vars[name] || {type};
323
- if (type !== variable.type) {
324
- if (variable.type === "any" || variable.type === "unknown") variable.type = type;
325
- else throw new TypeError(`Attempt to bind <input name="${name}" type="${type}"> to variable ${name}:${variable.type}`)
326
- }
327
- component.variables({[name]: type});
328
- let eventname = "change";
329
- if(input.tagName!=="SELECT" && (!inputtype || ["text","number","tel","email","url","search","password"].includes(inputtype))) {
330
- eventname = "input";
331
- }
332
- addListener(input,eventname, (event) => {
333
- event.stopImmediatePropagation();
334
- const target = event.target;
335
- let value = target.value;
336
- if (inputtype === "checkbox") {
337
- value = input.checked
338
- } else if (target.tagName === "SELECT") {
339
- if (target.hasAttribute("multiple")) {
340
- value = [...target.querySelectorAll("option")]
341
- .filter((option) => option.selected || resolveNode(option.attributes.value,component)==value || option.innerText == value)
342
- .map((option) => option.getAttribute("value") || option.innerText);
343
- }
333
+ const bindInput = (input, name, component,value) => {
334
+ const inputtype = input.tagName === "SELECT" || input.tagName === "TEXTAREA" ? "text" : input.getAttribute("type"),
335
+ type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype),
336
+ deflt = input.getAttribute("default");
337
+ value ||= input.getAttribute("value");
338
+ let variable = component.vars[name] || {type};
339
+ if (type !== variable.type) {
340
+ if (variable.type === "any" || variable.type === "unknown") variable.type = type;
341
+ else throw new TypeError(`Attempt to bind <input name="${name}" type="${type}"> to variable ${name}:${variable.type}`)
342
+ }
343
+ component.variables({[name]: type});
344
+ component.setValue(name,value);
345
+ let eventname = "change";
346
+ if (input.tagName !== "SELECT" && (!inputtype || input.tagName === "TEXTAREA" || ["text", "number", "tel", "email", "url", "search", "password"].includes(inputtype))) {
347
+ eventname = "input";
348
+ }
349
+ addListener(input, eventname, (event) => {
350
+ event.stopImmediatePropagation();
351
+ const target = event.target;
352
+ let value = target.value;
353
+ if (inputtype === "checkbox") {
354
+ value = input.checked
355
+ } else if (target.tagName === "SELECT") {
356
+ if (target.hasAttribute("multiple")) {
357
+ value = [...target.querySelectorAll("option")]
358
+ .filter((option) => option.selected || resolveNode(option.attributes.value, component) == value || option.innerText == value)
359
+ .map((option) => option.getAttribute("value") || option.innerText);
344
360
  }
345
- component.varsProxy[name] = coerce(value, type);
346
- })
361
+ }
362
+ component.varsProxy[name] = coerce(value, type);
363
+ })
347
364
  }
348
365
  const tryParse = (value) => {
349
366
  try {
350
367
  return JSON.parse(value);
351
- } catch(e) {
368
+ } catch (e) {
352
369
  return value;
353
370
  }
354
371
  }
@@ -362,7 +379,7 @@ const {observe} = (() => {
362
379
  exported: {value: true, constant: true},
363
380
  imported: {value: true, constant: true}
364
381
  };
365
- const createClass = (domElementNode, {observer, importAnchors}) => {
382
+ const createClass = (domElementNode, {observer, importAnchors, framed}) => {
366
383
  const instances = new Set(),
367
384
  dom = domElementNode.tagName === "TEMPLATE"
368
385
  ? domElementNode.content.cloneNode(true)
@@ -376,7 +393,7 @@ const {observe} = (() => {
376
393
  constructor() {
377
394
  super();
378
395
  instances.add(this);
379
- observer ||= createObserver(this);
396
+ observer ||= createObserver(this, framed);
380
397
  const currentComponent = this,
381
398
  shadow = this.attachShadow({mode: "open"}),
382
399
  eventlisteners = {};
@@ -406,6 +423,7 @@ const {observe} = (() => {
406
423
  };
407
424
  this.defaultAttributes = domElementNode.tagName === "TEMPLATE" ? domElementNode.attributes : dom.attributes;
408
425
  this.varsProxy = createVarsProxy(this.vars, this, CustomElement);
426
+ if (framed || CustomElement.lightviewFramed) this.variables({message: Object}, {exported: true});
409
427
  ["getElementById", "querySelector", "querySelectorAll"]
410
428
  .forEach((fname) => {
411
429
  Object.defineProperty(this, fname, {
@@ -465,51 +483,49 @@ const {observe} = (() => {
465
483
  } else if (node.nodeType === Node.ELEMENT_NODE) {
466
484
  // resolve the value before all else;
467
485
  const attr = node.attributes.value;
468
- let name;
469
- if(attr && attr.template) {
470
- render(!!attr.template,() => {
486
+ if (attr && attr.template) {
487
+ render(!!attr.template, () => {
471
488
  const value = resolveNode(attr, this),
472
- eltype = resolveNode(node.attributes.type,ctx);
473
- if(eltype==="checkbox") {
474
- if(coerce(value,"boolean")===true) {
475
- node.setAttribute("checked","");
489
+ eltype = resolveNode(node.attributes.type, ctx);
490
+ if(node.attributes.value) {
491
+ const template = attr.template;
492
+ if(/\$\{[a-zA-z_]+\}/g.test(template)) {
493
+ const name = template.substring(2,template.length-1);
494
+ if(!name.includes(" ")) bindInput(node,name,this,value);
495
+ }
496
+ }
497
+ if (eltype === "checkbox") {
498
+ if (coerce(value, "boolean") === true) {
499
+ node.setAttribute("checked", "");
476
500
  node.checked = true;
477
501
  } else {
478
502
  node.removeAttribute("checked");
479
503
  node.checked = false;
480
504
  }
481
- const vname = resolveNode(node.attributes.name,ctx);
482
- if(vname) ctx.setValue(vname,node.checked,{coerceTo:"boolean"});
505
+ const vname = resolveNode(node.attributes.name, ctx);
506
+ if (vname) ctx.setValue(vname, node.checked, {coerceTo: "boolean"});
483
507
  }
484
- if(node.tagName==="SELECT") {
508
+ if (node.tagName === "SELECT") {
485
509
  let values = [value];
486
- if(node.hasAttribute("multiple")) values = coerce(value,Array);
510
+ if (node.hasAttribute("multiple")) values = coerce(value, Array);
487
511
  [...node.querySelectorAll("option")].forEach((option) => {
488
- if(option.hasAttribute("value")) {
512
+ if (option.hasAttribute("value")) {
489
513
  if (values.includes(resolveNode(option.attributes.value, ctx))) {
490
514
  option.setAttribute("selected", "");
491
515
  option.selected = true;
492
516
  }
493
- } else if(option.innerText.trim()===value) {
494
- option.setAttribute("selected","");
517
+ } else if (option.innerText.trim() === value) {
518
+ option.setAttribute("selected", "");
495
519
  option.selected = true;
496
520
  }
497
521
  })
498
522
  }
499
523
  });
500
- let name;
501
- for(const vname of this.getVariableNames()) {
502
- if("${" + vname + "}" === attr.template) {
503
- name = vname;
504
- break;
505
- }
506
- }
507
- if(name) bindInput(node,name,ctx);
508
524
  }
509
525
  [...node.attributes].forEach((attr) => {
510
- if(attr.name==="value") return;
526
+ if (attr.name === "value") return;
511
527
  const {name, value} = attr;
512
- if(name==="type") {
528
+ if (name === "type") {
513
529
  if (value === "radio") {
514
530
  const name = resolveNode(node.attributes.name, ctx);
515
531
  for (const vname of this.getVariableNames()) {
@@ -517,7 +533,7 @@ const {observe} = (() => {
517
533
  render(true, () => {
518
534
  const name = resolveNode(node.attributes.name, ctx),
519
535
  varvalue = Function("context", "with(context) { return `${" + name + "}` }")(ctx.varsProxy);
520
- if (varvalue == resolveNode(node.attributes.value,ctx)) {
536
+ if (varvalue == resolveNode(node.attributes.value, ctx)) {
521
537
  node.setAttribute("checked", "");
522
538
  node.checked = true;
523
539
  } else {
@@ -532,17 +548,17 @@ const {observe} = (() => {
532
548
  }
533
549
  }
534
550
 
535
- const [type, ...params] = name.split(":");
551
+ const [type, ...params] = name.split(":");
536
552
  if (type === "") { // name is :something
537
553
  render(!!attr.template, () => {
538
554
  const value = attr.value,
539
- elvalue = resolveNode(node.attributes.value,ctx),
540
- eltype = resolveNode(node.attributes.type,ctx),
541
- elname = resolveNode(node.attributes.name,ctx);
555
+ elvalue = resolveNode(node.attributes.value, ctx),
556
+ eltype = resolveNode(node.attributes.type, ctx),
557
+ elname = resolveNode(node.attributes.name, ctx);
542
558
  if (params[0]) {
543
559
  if (value === "true") node.setAttribute(params[0], "")
544
560
  else node.removeAttribute(params[0]);
545
- } else if (eltype=== "checkbox" || node.tagName === "OPTION") {
561
+ } else if (eltype === "checkbox" || node.tagName === "OPTION") {
546
562
  if (value === "true") node.setAttribute("checked", "")
547
563
  else node.removeAttribute("checked");
548
564
  }
@@ -553,7 +569,7 @@ const {observe} = (() => {
553
569
  const value = resolveNode(attr, this);
554
570
  if (listener) node.removeEventListener(params[0], listener);
555
571
  listener = this[value] || window[value] || Function(value);
556
- addListener(node,params[0], listener);
572
+ addListener(node, params[0], listener);
557
573
  })
558
574
  } else if (type === "l-if") {
559
575
  render(!!attr.template, () => {
@@ -627,7 +643,7 @@ const {observe} = (() => {
627
643
 
628
644
  getVariableNames() {
629
645
  return Object.keys(this.vars).filter((name) => {
630
- return !(name in reserved) && !["self","addEventListener","postEvent"].includes(name)
646
+ return !(name in reserved) && !["self", "addEventListener", "postEvent"].includes(name)
631
647
  })
632
648
  }
633
649
 
@@ -725,13 +741,17 @@ const {observe} = (() => {
725
741
  }
726
742
  }
727
743
  }
728
- const createComponent = (name, node, {observer, importAnchors} = {}) => {
744
+ const createComponent = (name, node, {observer, importAnchors, framed} = {}) => {
729
745
  let ctor = customElements.get(name);
730
746
  if (ctor) {
731
- console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
747
+ if (framed && !ctor.lightviewFramed) {
748
+ ctor.lightviewFramed = true;
749
+ } else {
750
+ console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
751
+ }
732
752
  return ctor;
733
753
  }
734
- ctor = createClass(node, {observer, importAnchors});
754
+ ctor = createClass(node, {observer, importAnchors, framed});
735
755
  customElements.define(name, ctor);
736
756
  return ctor;
737
757
  }
@@ -760,9 +780,9 @@ const {observe} = (() => {
760
780
  }
761
781
  }
762
782
 
763
- const bodyAsComponent = ({as = "x-body", unhide, importAnchors} = {}) => {
783
+ const bodyAsComponent = ({as = "x-body", unhide, importAnchors, framed} = {}) => {
764
784
  const parent = document.body.parentElement;
765
- createComponent(as, document.body, {importAnchors});
785
+ createComponent(as, document.body, {importAnchors, framed});
766
786
  const component = document.createElement(as);
767
787
  parent.replaceChild(component, document.body);
768
788
  Object.defineProperty(document, "body", {
@@ -804,7 +824,8 @@ const {observe} = (() => {
804
824
 
805
825
  const url = new URL(document.currentScript.getAttribute("src"), window.location.href);
806
826
  let domContentLoadedEvent;
807
- addListener(window,"DOMContentLoaded", (event) => domContentLoadedEvent = event);
827
+ if (!domContentLoadedEvent) addListener(window, "DOMContentLoaded", (event) => domContentLoadedEvent = event);
828
+ let OBSERVER;
808
829
  const loader = async (whenFramed) => {
809
830
  if (!!document.querySelector('meta[name="l-importLinks"]')) await importLinks();
810
831
  const importAnchors = !!document.querySelector('meta[name="l-importAnchors"]'),
@@ -812,10 +833,10 @@ const {observe} = (() => {
812
833
  isolated = !!document.querySelector('meta[name="l-isolate"]'),
813
834
  enableFrames = !!document.querySelector('meta[name="l-enableFrames"]');
814
835
  if (whenFramed) {
815
- whenFramed({unhide, importAnchors, isolated, enableFrames});
836
+ whenFramed({unhide, importAnchors, isolated, enableFrames, framed: true});
816
837
  if (!isolated) {
817
838
  postMessage.enabled = true;
818
- addListener(window,"message", ({data}) => {
839
+ addListener(window, "message", ({data}) => {
819
840
  const {type, argsList} = JSON.parse(data);
820
841
  if (type === "framed") {
821
842
  const resize = () => {
@@ -851,7 +872,7 @@ const {observe} = (() => {
851
872
  }
852
873
  if (enableFrames) {
853
874
  postMessage.enabled = true;
854
- addListener(window,"message", (message) => {
875
+ addListener(window, "message", (message) => {
855
876
  const {type, iframeId, argsList, href} = JSON.parse(message.data),
856
877
  iframe = document.getElementById(iframeId);
857
878
  if (iframe) {
@@ -876,7 +897,9 @@ const {observe} = (() => {
876
897
  }
877
898
  if (type === "setAttribute") {
878
899
  const [name, value] = [...argsList];
879
- if (iframe.getAttribute(name) !== value + "") iframe.setAttribute(name, value);
900
+ if (iframe.getAttribute(name) !== value + "") {
901
+ iframe.setAttribute(name, value);
902
+ }
880
903
  return;
881
904
  }
882
905
  if (type === "removeAttribute") {
@@ -886,32 +909,52 @@ const {observe} = (() => {
886
909
  }
887
910
  console.warn("iframe posted a message without providing an id", message);
888
911
  });
889
- const mutationCallback = (mutationsList) => {
890
- const console = document.getElementById("console");
891
- for (const {target, attributeName, oldValue} of mutationsList) {
892
- if (!["height", "width"].includes(attributeName)) {
912
+ if (!OBSERVER) {
913
+ const mutationCallback = (mutationsList) => {
914
+ const console = document.getElementById("console");
915
+ for (const {target, attributeName, oldValue} of mutationsList) {
893
916
  const value = target.getAttribute(attributeName);
894
- if (!value) postMessage({type: "removeAttribute", argsList: [attributeName]}, iframe)
895
- else if (value !== oldValue) postMessage({
896
- type: "setAttribute",
897
- argsList: [attributeName, value]
898
- }, iframe)
917
+ if (!["height", "width", "message"].includes(attributeName)) {
918
+ if (!value) postMessage({type: "removeAttribute", argsList: [attributeName]}, iframe)
919
+ else if (value !== oldValue) {
920
+ postMessage({
921
+ type: "setAttribute",
922
+ argsList: [attributeName, value]
923
+ }, iframe)
924
+ }
925
+ }
926
+ if (attributeName === "message") {
927
+ if (value) {
928
+ target.removeAttribute("message");
929
+ target.dispatchEvent(new CustomEvent("message", {target, detail: JSON.parse(value)}))
930
+ }
931
+ } else {
932
+ target.dispatchEvent(new CustomEvent("attribute.changed", {
933
+ target,
934
+ detail: {attributeName, value, oldValue}
935
+ }))
936
+ }
899
937
  }
900
- }
901
- };
902
- const observer = new MutationObserver(mutationCallback),
903
- iframe = document.getElementById("myframe");
904
- observer.observe(iframe, {attributes: true, attributeOldValue: true});
938
+ };
939
+ const observer = OBSERVER = new MutationObserver(mutationCallback),
940
+ iframe = document.getElementById("myframe");
941
+ observer.observe(iframe, {attributes: true, attributeOldValue: true});
942
+ }
905
943
  }
906
944
  }
907
945
  const whenFramed = (f, {isolated} = {}) => {
908
- addListener(document,"DOMContentLoaded", (event) => loader(f));
946
+ // loads for framed content
947
+ addListener(document, "DOMContentLoaded", (event) => loader(f));
909
948
  }
910
949
  Lightview.whenFramed = whenFramed;
911
950
  //Object.defineProperty(Lightview, "whenFramed", {configurable: true, writable: true, value: whenFramed});
912
- if (window.location === window.parent.location || !(window.parent instanceof Window) || window.parent !== window) { // CodePen mucks with window.parent
913
- addListener(document,"DOMContentLoaded", () => loader())
951
+ if (window.location === window.parent.location || !(window.parent instanceof Window) || window.parent !== window) {
952
+ // loads for unframed content
953
+ // CodePen mucks with window.parent
954
+ addListener(document, "DOMContentLoaded", () => loader())
914
955
  }
915
956
 
916
957
  return {observe}
917
- })();
958
+ })();
959
+
960
+
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "lightview",
3
- "version": "1.4.1b",
3
+ "version": "1.4.5b",
4
4
  "description": "Small, simple, powerful web UI and micro front end creation ... imagine a blend of Svelte, React, Vue, Riot and more.",
5
5
  "main": "lightview.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
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
  }
@@ -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
+ });