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.
- package/benchmarks/naive/Solarite.min.js +4 -0
- package/benchmarks/naive/index.html +14 -0
- package/benchmarks/naive/main.js +339 -0
- package/benchmarks/naive/package-lock.json +13 -0
- package/benchmarks/naive/package.json +23 -0
- package/benchmarks/readme.md +48 -0
- package/build/build.bat +3 -2
- package/build/build.js +1 -1
- package/dist/Solarite-debug.js +1941 -2791
- package/dist/Solarite.js +1697 -2511
- package/dist/Solarite.min.js +3 -3
- package/dist/udomdiff-license.txt +18 -0
- package/docs/index.md +695 -235
- package/docs/js/Playground.js +1 -1
- package/docs/js/codemirror/codemirror6.js +3683 -3206
- package/docs/js/codemirror/themeSolarIce.js +2 -2
- package/docs/js/documentation.js +2 -2
- package/docs/js/ui/CodeEditor.js +183 -45
- package/docs/js/ui/FlexResizer.js +15 -5
- package/docs/js/util/Errors.js +11 -0
- package/docs/media/documentation.css +6 -3
- package/index.html +462 -26
- package/package.json +1 -1
- package/readme.md +11 -1
- package/src/solarite/ExprPath.js +422 -135
- package/src/solarite/Globals.js +53 -0
- package/src/solarite/NodeGroup.js +300 -388
- package/src/solarite/Shell.js +31 -33
- package/src/solarite/Solarite.js +17 -2
- package/src/solarite/Template.js +75 -25
- package/src/solarite/Util.js +131 -7
- package/src/solarite/createSolarite.js +32 -25
- package/src/solarite/getArg.js +12 -12
- package/src/solarite/hash.js +18 -35
- package/src/solarite/r.js +128 -118
- package/src/solarite/watch3.js +98 -0
- package/src/{solarite → unused}/NodeGroupManager.js +86 -224
- package/src/unused/onConnect.js +79 -0
- package/src/{solarite → unused}/watch.js +2 -2
- package/src/{solarite → unused}/watch2.js +4 -4
- package/src/{solarite → util}/MultiValueMap.js +23 -12
- package/src/util/WeakArray.js +33 -0
- package/tests/Solarite.test.js +1108 -166
- package/tests/Testimony.js +274 -67
- package/tests/index.html +6 -35
- package/tests/run.bat +2 -0
- package/tests/NodeGroup.test.js +0 -115
- package/tests/Shell.test.js +0 -75
package/tests/Testimony.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
361
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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
|
-
|
|
546
|
-
|
|
714
|
+
if (!test.children)
|
|
715
|
+
test.children = {};
|
|
547
716
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
717
|
+
// If at leaf
|
|
718
|
+
if (pathSoFar.length === path.length)
|
|
719
|
+
test.children[item] = new Test(name2, desc2, func2);
|
|
551
720
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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> ') {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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
package/tests/NodeGroup.test.js
DELETED
|
@@ -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
|
-
|