solarite 0.1.0 → 0.2.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.
Files changed (48) hide show
  1. package/benchmarks/naive/Solarite.min.js +4 -0
  2. package/benchmarks/naive/index.html +14 -0
  3. package/benchmarks/naive/main.js +339 -0
  4. package/benchmarks/naive/package-lock.json +13 -0
  5. package/benchmarks/naive/package.json +23 -0
  6. package/benchmarks/readme.md +48 -0
  7. package/build/build.bat +3 -2
  8. package/build/build.js +1 -1
  9. package/dist/Solarite-debug.js +1941 -2791
  10. package/dist/Solarite.js +1697 -2511
  11. package/dist/Solarite.min.js +3 -3
  12. package/dist/udomdiff-license.txt +18 -0
  13. package/docs/index.md +695 -235
  14. package/docs/js/Playground.js +1 -1
  15. package/docs/js/codemirror/codemirror6.js +3683 -3206
  16. package/docs/js/codemirror/themeSolarIce.js +2 -2
  17. package/docs/js/documentation.js +2 -2
  18. package/docs/js/ui/CodeEditor.js +183 -45
  19. package/docs/js/ui/FlexResizer.js +15 -5
  20. package/docs/js/util/Errors.js +11 -0
  21. package/docs/media/documentation.css +6 -3
  22. package/index.html +462 -26
  23. package/package.json +1 -1
  24. package/readme.md +11 -1
  25. package/src/solarite/ExprPath.js +422 -135
  26. package/src/solarite/Globals.js +53 -0
  27. package/src/solarite/NodeGroup.js +300 -388
  28. package/src/solarite/Shell.js +31 -33
  29. package/src/solarite/Solarite.js +17 -2
  30. package/src/solarite/Template.js +75 -25
  31. package/src/solarite/Util.js +131 -7
  32. package/src/solarite/createSolarite.js +32 -25
  33. package/src/solarite/getArg.js +12 -12
  34. package/src/solarite/hash.js +18 -35
  35. package/src/solarite/r.js +128 -118
  36. package/src/solarite/watch3.js +98 -0
  37. package/src/{solarite → unused}/NodeGroupManager.js +86 -224
  38. package/src/unused/onConnect.js +79 -0
  39. package/src/{solarite → unused}/watch.js +2 -2
  40. package/src/{solarite → unused}/watch2.js +4 -4
  41. package/src/{solarite → util}/MultiValueMap.js +23 -12
  42. package/src/util/WeakArray.js +33 -0
  43. package/tests/Solarite.test.js +1108 -166
  44. package/tests/Testimony.js +274 -67
  45. package/tests/index.html +6 -35
  46. package/tests/run.bat +2 -0
  47. package/tests/NodeGroup.test.js +0 -115
  48. package/tests/Shell.test.js +0 -75
@@ -3,6 +3,10 @@
3
3
  * Has no external dependencies.
4
4
  *
5
5
  * TODO:
6
+ * Make a Test interface and a Test web component.
7
+ * That way we can render and run the same test object separately.
8
+ *
9
+ *
6
10
  * 4. Integrate with IntelliJ file watcher so we run cmd line tests when files change.
7
11
  * 5. Run tests from @expect doc tags.
8
12
  * 6. Documentation - Web tests, deno tests, intellij integration
@@ -287,7 +291,7 @@ var HtmlRenderer = {
287
291
  }
288
292
  }
289
293
 
290
- // Not used since Deno renders the tests.
294
+ // Not used.
291
295
  var TextRenderer = {
292
296
 
293
297
  render(test) {
@@ -328,16 +332,15 @@ class Test {
328
332
  name;
329
333
  desc;
330
334
  expanded;
331
- enable;
335
+ enabled;
332
336
 
333
337
  /**
334
338
  * @type {boolean|Error|null}
335
- * true: Test passed or all child tests passed
339
+ * true: Test passed or all non-disabled child tests passed
336
340
  * false: One or more child tests failed.
337
341
  * Error: Test failed.
338
342
  * null: Hasn't been run yet. */
339
343
  status = null;
340
-
341
344
  /**
342
345
  * Every test will have either a fn OR children.
343
346
  * @type {?function} */
@@ -357,16 +360,25 @@ class Test {
357
360
  this.expanded = url.searchParams.getAll('x').includes(name);
358
361
 
359
362
  // Enabled if this or a parent is checked
360
- this.enabled = false;
361
- let r = url.searchParams.getAll('r');
362
- let parent = name;
363
- do {
364
- if (r.includes(parent) && !this.getShortName().startsWith('_')) {
363
+ if (this.enabled === undefined && !this.getShortName().startsWith('_')) { // if not otherwise set, set it from url:
364
+ if (url.searchParams.has('allTests'))
365
365
  this.enabled = true;
366
- break;
366
+
367
+ else {
368
+
369
+ let r = url.searchParams.getAll('r');
370
+ let parent = name;
371
+ do {
372
+
373
+ // Enable test if a parent is enabled.
374
+ if (r.includes(parent)) {
375
+ this.enabled = true;
376
+ break;
377
+ }
378
+ parent = parent.split('.').slice(0, -1).join('.')
379
+ } while (parent);
367
380
  }
368
- parent = parent.split('.').slice(0, -1).join('.')
369
- } while (parent);
381
+ }
370
382
  }
371
383
  else // TODO: Get enabled tests from the command line enable arguments.
372
384
  this.enabled = true;
@@ -393,6 +405,8 @@ class Test {
393
405
  if (result !== false)
394
406
  this.status = true;
395
407
  } catch (e) {
408
+ console.log(e)
409
+ Testimony.failedTests.push([this.name, Testimony.shortenError(e, '\n')]);
396
410
  this.status = e;
397
411
  }
398
412
  }
@@ -409,8 +423,8 @@ class Test {
409
423
  if (child.status === false || child.status instanceof Error)
410
424
  this.status = false;
411
425
 
412
- else if (child.status === null && this.status !== false)
413
- this.status = null;
426
+ // else if (child.status === null && this.status !== false)
427
+ // this.status = null;
414
428
 
415
429
  else if (child.status === true)
416
430
  hasPassingChild = true;
@@ -421,18 +435,40 @@ class Test {
421
435
  if (this.status === true && !hasPassingChild)
422
436
  this.status = null;
423
437
  }
438
+
439
+ return this.status;
440
+ }
441
+
442
+ /**
443
+ * Set the enabled status of this test and its children, checking their checkbox.
444
+ * @param tests {string[]} Names of tests.
445
+ * @param enabled {boolean} */
446
+ setEnabled(tests, enabled) {
447
+ if (!tests || tests.includes(this.name))
448
+ this.enabled = enabled;
449
+
450
+ for (let childName in this.children || {}) {
451
+ let child = this.children[childName];
452
+ if (!childName.startsWith('_'))
453
+ child.setEnabled(tests, enabled);
454
+ }
424
455
  }
425
456
 
457
+ /**
458
+ * The top level test returns a depth of 0.
459
+ * @returns {int} */
426
460
  getDepth() {
427
461
  return ((this.name || '').match(/\./g) || []).length;
428
462
  }
429
463
 
464
+ /**
465
+ * Get the name after the last dot.
466
+ * @returns {string} */
430
467
  getShortName() {
431
468
  return /[^.]*$/.exec(this.name)[0];
432
469
  }
433
470
  }
434
471
 
435
-
436
472
  var Testimony = {
437
473
 
438
474
  debugOnAssertFail: false,
@@ -442,11 +478,38 @@ var Testimony = {
442
478
  /** @type {Test} */
443
479
  rootTest: new Test(),
444
480
 
481
+
482
+
483
+
484
+ finished: false,
485
+
486
+ /**
487
+ * A map from the test name to the error.
488
+ * @type {[string, string][]} */
489
+ failedTests: [],
490
+
491
+
492
+
493
+ /**
494
+ *
495
+ * @param tests {?string[]} Test names. E.g. ['Main.one', 'Main.two']. If null, apply to all tests that are not disabled.
496
+ * @param enabled {boolean} */
497
+ setTestsEnabled(tests, enabled) {
498
+ this.rootTest.setEnabled(tests, enabled);
499
+ },
500
+
501
+ /**
502
+ * Run the root test and any of the root tests children.
503
+ * TODO: Separate rendering from running.
504
+ * @param parent {?HTMLElement}
505
+ * @returns {Promise<[string, Error][]>}
506
+ */
445
507
  async run(parent) {
508
+ this.failedTests = []; // resets
446
509
  let renderer = parent ? HtmlRenderer : TextRenderer;
447
510
 
448
511
  if (parent) {
449
- // Expand
512
+ // Expand recursively
450
513
  function doExpand(test, expand) {
451
514
  if (expand) {
452
515
  test.expanded = true;
@@ -469,12 +532,122 @@ var Testimony = {
469
532
  renderer.render(Testimony.rootTest);
470
533
  }
471
534
 
535
+ // Command line
472
536
  else {
473
537
  await Testimony.rootTest.run();
474
538
  console.log(renderer.render(Testimony.rootTest));
475
539
  }
540
+
541
+
542
+ this.finished = true;
543
+ return this.failedTests;
544
+ },
545
+
546
+ /**
547
+ * Requires Deno and a regular Chrome installation.
548
+ * @param page {string}
549
+ * @param webRoot {?string}
550
+ * @param tests {?string[]}
551
+ * @param headless {boolean}
552
+ * @param port {int} Defaults to 8004 to not conflict with commonly used development ports like 8000 or 8080.
553
+ * @returns {Promise<void>} */
554
+ async runPage(page, webRoot=null, tests=null, headless=false, port=8004) {
555
+
556
+ /*
557
+ import puppeteer from 'https://esm.sh/puppeteer@13.0.0';
558
+ import { serve } from 'https://deno.land/std/http/server.ts';
559
+ import { serveFile } from 'https://deno.land/std@0.102.0/http/file_server.ts';
560
+ import { Launcher } from 'https://esm.sh/chrome-launcher@0.15.0';
561
+ */
562
+
563
+ // Dynamically import so we only pull them in if necessary.
564
+ const [
565
+ {default: puppeteer},
566
+ {Launcher},
567
+ {serve},
568
+ {serveFile},
569
+ ] = await Promise.all([
570
+ import('https://deno.land/x/puppeteer@16.2.0/mod.ts'),
571
+ import('https://esm.sh/chrome-launcher@0.15.0'),
572
+ import('https://deno.land/std@0.102.0/http/server.ts'),
573
+ import('https://deno.land/std@0.102.0/http/file_server.ts')
574
+ ]);
575
+
576
+ const startServer = () => {
577
+
578
+ const absWebRoot = Deno.realPathSync(webRoot);
579
+ const server = serve({port: 8004});
580
+ //console.log("HTTP web server running. Access it at: http://localhost:8000/");
581
+
582
+ (async () => {
583
+ for await (const request of server) {
584
+ const url = new URL(request.url, `http://${request.headers.get("host")}`);
585
+ const filepath = `${absWebRoot}${url.pathname}`;
586
+ //console.log(filepath)
587
+ try {
588
+ const content = await serveFile(request, filepath);
589
+ request.respond(content);
590
+ } catch {
591
+ request.respond({status: 404, body: "File not found"});
592
+ }
593
+ }
594
+ })();
595
+
596
+ return server;
597
+ };
598
+
599
+ const stopServer = (server) => {
600
+ server.close();
601
+ };
602
+
603
+ const server = startServer();
604
+
605
+ const installations = await Launcher.getInstallations();
606
+ if (installations.length === 0)
607
+ throw new Error("No Chrome installations found.");
608
+
609
+ const executablePath = installations[0]; // Use the first found installation
610
+
611
+
612
+ const browser = await puppeteer.launch({headless, executablePath});
613
+ const browserPage = await browser.newPage();
614
+ let args = [];
615
+
616
+ if (!tests)
617
+ args.push('allTests');
618
+ else
619
+ for (let test of tests)
620
+ args.push(`&r=${test}`);
621
+
622
+
623
+ const url = `http://localhost:${port}/${page}?${args.join('&')}`;
624
+ //console.log(url)
625
+ await browserPage.goto(url);
626
+
627
+ // Wait for the tests to finish
628
+ await browserPage.waitForFunction(() => window.Testimony?.finished === true);
629
+
630
+ const failedTests = await browserPage.evaluate(() => window.Testimony?.failedTests);
631
+ this.printTestResult(failedTests);
632
+
633
+ await browser.close();
634
+ stopServer(server);
635
+
636
+ Deno.exit(failedTests.length ? 1 : 0);
637
+ },
638
+
639
+ printTestResult(failedTests) {
640
+ if (!failedTests.length)
641
+ console.log(`%cAll tests passed.`, 'color: #0c0');
642
+ else {
643
+ console.log(`These tests failed:`);
644
+ for (const [testName, testError] of failedTests) {
645
+ console.error(`${testName} - %c${testError}`, 'color: red');
646
+ }
647
+ }
476
648
  },
477
649
 
650
+
478
651
  /**
479
652
  * Add a test.
480
653
  *
@@ -530,73 +703,107 @@ var Testimony = {
530
703
  }
531
704
  }
532
705
 
533
- if (globalThis.Deno) {
534
- Deno.test(name2, func2);
535
- }
536
- else {
537
706
 
538
- // Add to rootTest tree.
539
- let path = name.split(/\./g);
540
- let pathSoFar = [];
541
- let test = this.rootTest;
542
- for (let item of path) {
543
- pathSoFar.push(item);
707
+ // Add to rootTest tree.
708
+ let path = name.split(/\./g);
709
+ let pathSoFar = [];
710
+ let test = this.rootTest;
711
+ for (let item of path) {
712
+ pathSoFar.push(item);
544
713
 
545
- if (!test.children)
546
- test.children = {};
714
+ if (!test.children)
715
+ test.children = {};
547
716
 
548
- // If at leaf
549
- if (pathSoFar.length === path.length)
550
- test.children[item] = new Test(name2, desc2, func2);
717
+ // If at leaf
718
+ if (pathSoFar.length === path.length)
719
+ test.children[item] = new Test(name2, desc2, func2);
551
720
 
552
- // Create test if it doesn't exist.
553
- else {
554
- test.children[item] = test.children[item] || new Test(pathSoFar.join('.'));
555
- test = test.children[item];
556
- }
721
+ // Create test if it doesn't exist.
722
+ else {
723
+ test.children[item] = test.children[item] || new Test(pathSoFar.join('.'));
724
+ test = test.children[item];
557
725
  }
558
726
  }
559
727
  },
560
728
 
729
+ /**
730
+ * @param test {?Test} */
731
+ getAllTestNames(test=null) {
732
+ test = test || this.rootTest;
733
+ let result = [];
734
+ if (test.name.length)
735
+ result.push(test.name);
736
+ for (let name in test.children)
737
+ result.push(...this.getAllTestNames(test.children[name]));
738
+ return result;
739
+ },
740
+
561
741
  // Internal functions:
562
742
 
743
+ /**
744
+ * @param error {Error}
745
+ * @param br {string}
746
+ * @returns {string} */
563
747
  shortenError(error, br='<br>&nbsp;&nbsp;') {
564
748
  // slice(0, -3) to remove the 3 stacktrace lines inside Testimony.js that calls runtests.
565
749
  let errorStack = error.stack.split(/\n/g).slice(0, -3).join('\r\n');
566
750
 
567
751
  errorStack = errorStack.replace(/\r?\n/g, br);
568
752
  return errorStack.replace(new RegExp(window.location.origin, 'g'), ''); // Remove server name to shorten error stack.
569
- },
570
-
571
- /**
572
- * TODO: This doesn't wor0 because there's no DOMRect.
573
- * I should conslut the jsdom docs and mabye try the "Executing scripts" section
574
- * to run the tests inside the jsdom document?
575
- *
576
- * Used only when running from the command line.
577
- * Define document object to allow us to run all modules from the command line. */
578
- async enableJsDom() {
579
- if (!globalThis.document) {
580
- await (async () => {
581
- let { default: jsdom} = await import('https://dev.jspm.io/jsdom');
582
- let dom = new jsdom.JSDOM(`<!DOCTYPE html>`, {
583
- pretendToBeVisual: true,
584
- resources: 'usable'
585
- });
586
- let window = dom.window;
587
-
588
- for (let name in window)
589
- globalThis[name] = window[name];
590
-
591
- /*let module =*/ /*import('https://deno.land/std@0.73.0/testing/asserts.ts');*/
592
-
593
- // Sleep is required for JSDom to resolve its promises before tests begin.
594
- await new Promise(resolve => setTimeout(resolve, 10));
595
- })()
596
- }
597
- },
753
+ }
598
754
  }
599
- await Testimony.enableJsDom();
755
+ window.Testimony = Testimony; // used by command line test runner.
600
756
 
601
757
  export default Testimony;
602
- export {assert, Testimony, TextRenderer, HtmlRenderer};
758
+ export {assert, Testimony, TextRenderer, HtmlRenderer};
759
+
760
+
761
+ // If Testimony.js is run directly from the command line
762
+ if (import.meta.main) {
763
+ let pages = null;
764
+ let imports = null;
765
+ let tests = null;
766
+ let webroot = null;
767
+ let headless = false;
768
+ for (let arg of Deno.args) {
769
+
770
+ if (arg.startsWith('--page=')) {
771
+ if (!pages)
772
+ pages = [];
773
+ pages.push(arg.slice('--page='.length));
774
+ }
775
+
776
+ else if (arg.startsWith('--import=')) {
777
+ if (!imports)
778
+ imports = [];
779
+ imports.push(arg.slice('--import='.length));
780
+ }
781
+
782
+ else if (arg.startsWith('--webroot='))
783
+ webroot = arg.slice('--webroot='.length);
784
+
785
+
786
+ else if (arg == '--headless')
787
+ headless = true;
788
+
789
+ else if (arg.startsWith('--')) {
790
+ console.error(`Unsupported arg ${arg}`);
791
+ Deno.exit(1);
792
+ }
793
+
794
+ // Capture test names to run.
795
+ else {
796
+ if (!tests)
797
+ tests = [];
798
+ tests.push(arg);
799
+ }
800
+ }
801
+
802
+ if (webroot && !pages)
803
+ pages = ['index.html'];
804
+
805
+ if (pages) {
806
+ for (let page of pages)
807
+ Testimony.runPage(page, webroot, tests, headless);
808
+ }
809
+ }
package/tests/index.html CHANGED
@@ -3,7 +3,6 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <title>Solarite Tests ✨</title>
6
- <!-- 💫💥🎇✨☀ -->
7
6
  <link rel="icon" href="data:,">
8
7
  <style>
9
8
  body { font: 14px Consolas; margin: 0; padding: 10px; box-sizing: border-box; min-height: 100vh }
@@ -11,7 +10,8 @@
11
10
  td { vertical-align: top; padding: 0; white-space: normal }
12
11
  td:first-child { white-space: nowrap }
13
12
  label { user-select: none }
14
- button { border-radius: 10px; border: 2px solid grey; padding: 2px 15px; outline: 0; cursor: pointer }
13
+ button { border: 1px solid #1b1e22; padding: 2px 15px; outline: 0; cursor: pointer }
14
+ a, a:visited { color: #58f }
15
15
  @media (prefers-color-scheme: dark) {
16
16
  body { background: #000; color: #ddd }
17
17
  button { background: #333; color: white }
@@ -22,49 +22,20 @@
22
22
  <body>
23
23
  <script type="module">
24
24
  import {Testimony} from './Testimony.js';
25
- import './Shell.test.js';
26
- import './NodeGroup.test.js';
25
+
27
26
  import './Solarite.test.js';
28
27
  import './Benchmark.test.js';
29
28
 
30
- // For testing:
31
- window.getHtml = (item, includeComments=false) => {
32
- if (!item)
33
- return item;
34
-
35
- if (item.fragment)
36
- item = item.fragment; // Shell
37
- if (item instanceof DocumentFragment)
38
- item = [...item.childNodes]
39
-
40
- else if (item.getNodes)
41
- item = item.getNodes()
42
-
43
- let result;
44
- if (Array.isArray(item)) {
45
- if (!includeComments)
46
- item = item.filter(n => n.nodeType !==8)
47
-
48
- result = item.map(n => n.nodeType === 8 ? `<!--${n.textContent}-->` : n.outerHTML || n.textContent).join('|')
49
- }
50
-
51
-
52
- else
53
- result = item.outerHTML || item.textContent
54
29
 
55
- if (!includeComments)
56
- result = result.replace(/(<|\x3C)!--(.*?)-->/g, '')
30
+ Testimony.debugOnAssertFail = true;
31
+ Testimony.throwOnError = true;
57
32
 
58
- // Remove whitespace between tags, so we can write simpler tests.
59
- return result.replace(/^\s+</g, '<').replace(/>\s+</g, '><').replace(/>\s+$/g, '>');
60
- }
61
33
 
62
- Testimony.throwOnError = false;
63
- Testimony.debugOnAssertFail = true;
64
34
  Testimony.run(document.getElementById('tests'));
65
35
  </script>
66
36
  <form>
67
37
  <button>Run Tests</button>
38
+ <a href=".">Reset</a>
68
39
  <div id="tests"></div>
69
40
  <br>
70
41
  <button>Run Tests</button>
package/tests/run.bat ADDED
@@ -0,0 +1,2 @@
1
+ @echo off
2
+ deno run -A Testimony.js --page=tests/index.html --webroot=../ --headless
@@ -1,115 +0,0 @@
1
- /**
2
- * These tests make it easier to debug individual functions of NodeGroup.
3
- */
4
- import NodeGroupManager from "../src/solarite/NodeGroupManager.js";
5
- import {r} from "../src/solarite/Solarite.js";
6
- import NodeGroup from "../src/solarite/NodeGroup.js";
7
- import Shell from "../src/solarite/Shell.js";
8
- import Template from "../src/solarite/Template.js";
9
- import Testimony, {assert} from "./Testimony.js";
10
-
11
-
12
-
13
-
14
- Testimony.test('NodeGroup.empty', () => {
15
- let ngm = new NodeGroupManager(document.body);
16
-
17
- let ng = new NodeGroup(new Template([``], []), ngm)
18
- assert.eq(getHtml(ng), '')
19
-
20
- ng.applyExprs([])
21
- assert.eq(getHtml(ng), '')
22
- });
23
-
24
-
25
- Testimony.test('NodeGroup.oneExpr', () => {
26
- let ngm = new NodeGroupManager(document.body);
27
-
28
- let ng = new NodeGroup(new Template([``, ``], ['1']), ngm)
29
- assert.eq(getHtml(ng), '1')
30
-
31
- ng.applyExprs([2])
32
- assert.eq(getHtml(ng), '2')
33
-
34
- ng.applyExprs([[3, 4, 5]])
35
- assert.eq(getHtml(ng), '3|4|5')
36
- });
37
-
38
- Testimony.test('NodeGroup.emptyAdjacent', () => {
39
- let ngm = new NodeGroupManager(document.body);
40
- let ng = new NodeGroup(new Template([``, ``, ``], ['1', '2']), ngm)
41
- ng.verify();
42
-
43
- assert.eq(getHtml(ng), '1|2')
44
-
45
- ng.applyExprs([3, 4])
46
- assert.eq(getHtml(ng), '3|4')
47
-
48
- ng.applyExprs([[1, 2, 3], [4, 5, 6]])
49
- assert.eq(getHtml(ng), '1|2|3|4|5|6')
50
- });
51
-
52
-
53
- Testimony.test('NodeGroup.paragraph', () => {
54
- let ngm = new NodeGroupManager(document.body);
55
- let ng = new NodeGroup(new Template(['<p>', '</p>'], ['1']), ngm)
56
- assert.eq(getHtml(ng), '<p>1</p>')
57
-
58
- ng.applyExprs([2])
59
- assert.eq(getHtml(ng), '<p>2</p>')
60
-
61
- ng.applyExprs([[3, 4, 5]])
62
- assert.eq(getHtml(ng), '<p>345</p>')
63
- });
64
-
65
- Testimony.test('NodeGroup.node', () => {
66
- let ngm = new NodeGroupManager(document.body);
67
- let a = r('<p>a</p>')
68
- let b = r('<p>b</p>')
69
- let ng = new NodeGroup(new Template(['<div>', '</div>'], [a]), ngm)
70
-
71
- assert.eq(getHtml(ng), '<div><p>a</p></div>')
72
-
73
- ng.applyExprs([b]);
74
- assert.eq(getHtml(ng), '<div><p>b</p></div>')
75
-
76
- ng.applyExprs([1]);
77
- assert.eq(getHtml(ng), '<div>1</div>')
78
-
79
- ng.applyExprs([a]);
80
- assert.eq(getHtml(ng), '<div><p>a</p></div>')
81
- });
82
-
83
-
84
- /**
85
- * This fails because
86
- */
87
- Testimony.test('NodeGroup._nodeSwap', () => {
88
- let ngm = new NodeGroupManager(document.body);
89
- let a = r('<p>a</p>')
90
- let b = r('<p>b</p>')
91
- let ng = new NodeGroup(new Template(['<div>', '', '</div>'], [a, b]), ngm)
92
- document.body.append(ng.startNode)
93
-
94
- assert.eq(getHtml(ng), '<div><p>a</p><p>b</p></div>')
95
-
96
-
97
- ng.applyExprs([b, a]);
98
- assert.eq(getHtml(ng), '<div><p>b</p><p>a</p></div>')
99
-
100
- });
101
-
102
-
103
- Testimony.test('NodeGroup.arrayReverse', () => {
104
- let ngm = new NodeGroupManager(document.body);
105
- let list = [r('a'), r('b')];
106
-
107
- let ng = new NodeGroup(new Template(['<div>', '</div>'], [list]), ngm)
108
- ng.verify();
109
- assert(getHtml(ng), '<div>ab</div')
110
-
111
- list.reverse();
112
- ng.applyExprs([list])
113
- assert(getHtml(ng), '<div>ba</div')
114
- });
115
-