forked from rosa/hakurei
Compare commits
11 Commits
71fda61076
...
5944b799ac
| Author | SHA1 | Date | |
|---|---|---|---|
|
5944b799ac
|
|||
|
e22650e526
|
|||
|
4e7b6b7d79
|
|||
|
352e77c5d5
|
|||
|
45547ae6a6
|
|||
|
f113030aec
|
|||
|
7c0c8fff8f
|
|||
|
a209ad5237
|
|||
|
12eacd957d
|
|||
|
1d28d95d7b
|
|||
|
0a3301e40b
|
@@ -33,6 +33,18 @@ func serveStaticContent(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFileFS(w, r, content, "ui/static/all_tests.js")
|
||||
case "/static/test_tests.js":
|
||||
http.ServeFileFS(w, r, content, "ui/static/test_tests.js")
|
||||
case "/static/success-closed.svg":
|
||||
http.ServeFileFS(w, r, content, "ui/static/success-closed.svg")
|
||||
case "/static/success-open.svg":
|
||||
http.ServeFileFS(w, r, content, "ui/static/success-open.svg")
|
||||
case "/static/failure-closed.svg":
|
||||
http.ServeFileFS(w, r, content, "ui/static/failure-closed.svg")
|
||||
case "/static/failure-open.svg":
|
||||
http.ServeFileFS(w, r, content, "ui/static/failure-open.svg")
|
||||
case "/static/skip-closed.svg":
|
||||
http.ServeFileFS(w, r, content, "ui/static/skip-closed.svg")
|
||||
case "/static/skip-open.svg":
|
||||
http.ServeFileFS(w, r, content, "ui/static/skip-open.svg")
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
|
||||
|
||||
13
cmd/pkgserver/ui/static/failure-closed.svg
Normal file
13
cmd/pkgserver/ui/static/failure-closed.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- See failure-open.svg for an explanation of the view box dimensions. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" viewBox="-20,-20 160 130">
|
||||
<!-- This triangle should match success-closed.svg, fill and stroke color notwithstanding. -->
|
||||
<polygon points="0,0 100,50 0,100" fill="red" stroke="red" stroke-width="15" stroke-linejoin="round"/>
|
||||
<!--
|
||||
! y-coordinates go before x-coordinates here to highlight the difference
|
||||
! (or, lack thereof) between these numbers and the ones in failure-open.svg;
|
||||
! try a textual diff. Make sure to keep the numbers in sync!
|
||||
-->
|
||||
<line y1="30" x1="10" y2="70" x2="50" stroke="white" stroke-width="16"/>
|
||||
<line y1="30" x1="50" y2="70" x2="10" stroke="white" stroke-width="16"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 788 B |
35
cmd/pkgserver/ui/static/failure-open.svg
Normal file
35
cmd/pkgserver/ui/static/failure-open.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
! This view box is a bit weird: the strokes assume they're working in a view
|
||||
! box that spans from the (0,0) to (100,100), and indeed that is convenient
|
||||
! conceptualizing the strokes, but the stroke itself has a considerable width
|
||||
! that gets clipped by restrictive view box dimensions. Hence, the view is
|
||||
! shifted from (0,0)–(100,100) to (-20,-20)–(120,120), to make room for the
|
||||
! clipped stroke, while leaving behind an illusion of working in a view box
|
||||
! spanning from (0,0) to (100,100).
|
||||
!
|
||||
! However, the resulting SVG is too close to the summary text, and CSS
|
||||
! properties to add padding do not seem to work with `content:` (likely because
|
||||
! they're anonymous replaced elements); thus, the width of the view is
|
||||
! increased considerably to provide padding in the SVG itself, while leaving
|
||||
! the strokes oblivious.
|
||||
!
|
||||
! It gets worse: the summary text isn't vertically aligned with the icon! As
|
||||
! a flexbox cannot be used in a summary to align the marker with the text, the
|
||||
! simplest and most effective solution is to reduce the height of the view box
|
||||
! from 140 to 130, thereby removing some of the bottom padding present.
|
||||
!
|
||||
! All six SVGs use the same view box (and indeed, they refer to this comment)
|
||||
! so that they all appear to be the same size and position relative to each
|
||||
! other on the DOM—indeed, the view box dimensions, alongside the width,
|
||||
! directly control their placement on the DOM.
|
||||
!
|
||||
! TL;DR: CSS is janky, overflow is weird, and SVG is awesome!
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" viewBox="-20,-20 160 130">
|
||||
<!-- This triangle should match success-open.svg, fill and stroke color notwithstanding. -->
|
||||
<polygon points="0,0 100,0 50,100" fill="red" stroke="red" stroke-width="15" stroke-linejoin="round"/>
|
||||
<!-- See the comment in failure-closed.svg before modifying this. -->
|
||||
<line x1="30" y1="10" x2="70" y2="50" stroke="white" stroke-width="16"/>
|
||||
<line x1="30" y1="50" x2="70" y2="10" stroke="white" stroke-width="16"/>
|
||||
</svg>
|
||||
@@ -3,5 +3,5 @@
|
||||
// provides faster iteration, especially for those acclimated to test-driven
|
||||
// development.
|
||||
import "./all_tests.js";
|
||||
import { StreamReporter, run } from "./test.js";
|
||||
run(new StreamReporter({ writeln: console.log }));
|
||||
import { StreamReporter, TESTS } from "./test.js";
|
||||
TESTS.run(new StreamReporter({ writeln: console.log }));
|
||||
|
||||
21
cmd/pkgserver/ui/static/skip-closed.svg
Normal file
21
cmd/pkgserver/ui/static/skip-closed.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- See failure-open.svg for an explanation of the view box dimensions. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" viewBox="-20,-20 160 130">
|
||||
<!-- This triangle should match success-closed.svg, fill and stroke color notwithstanding. -->
|
||||
<polygon points="0,0 100,50 0,100" fill="blue" stroke="blue" stroke-width="15" stroke-linejoin="round"/>
|
||||
<!--
|
||||
! This path is extremely similar to the one in skip-open.svg; before
|
||||
! making minor modifications, diff the two to understand how they should
|
||||
! remain in sync.
|
||||
-->
|
||||
<path
|
||||
d="M 50,50
|
||||
A 23,23 270,1,1 30,30
|
||||
l -10,20
|
||||
m 10,-20
|
||||
l -20,-10"
|
||||
fill="none"
|
||||
stroke="white"
|
||||
stroke-width="12"
|
||||
stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 812 B |
21
cmd/pkgserver/ui/static/skip-open.svg
Normal file
21
cmd/pkgserver/ui/static/skip-open.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- See failure-open.svg for an explanation of the view box dimensions. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" viewBox="-20,-20 160 130">
|
||||
<!-- This triangle should match success-open.svg, fill and stroke color notwithstanding. -->
|
||||
<polygon points="0,0 100,0 50,100" fill="blue" stroke="blue" stroke-width="15" stroke-linejoin="round"/>
|
||||
<!--
|
||||
! This path is extremely similar to the one in skip-closed.svg; before
|
||||
! making minor modifications, diff the two to understand how they should
|
||||
! remain in sync.
|
||||
-->
|
||||
<path
|
||||
d="M 50,50
|
||||
A 23,23 270,1,1 70,30
|
||||
l 10,-20
|
||||
m -10,20
|
||||
l -20,-10"
|
||||
fill="none"
|
||||
stroke="white"
|
||||
stroke-width="12"
|
||||
stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 812 B |
6
cmd/pkgserver/ui/static/success-closed.svg
Normal file
6
cmd/pkgserver/ui/static/success-closed.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- See failure-open.svg for an explanation of the view box dimensions. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" viewBox="-20,-20 160 130">
|
||||
<!-- When updating this triangle, also update the other five SVGs. -->
|
||||
<polygon points="0,0 100,50 0,100" fill="none" stroke="black" stroke-width="15" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 369 B |
6
cmd/pkgserver/ui/static/success-open.svg
Normal file
6
cmd/pkgserver/ui/static/success-open.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- See failure-open.svg for an explanation of the view box dimensions. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" viewBox="-20,-20 160 130">
|
||||
<!-- When updating this triangle, also update the other five SVGs. -->
|
||||
<polygon points="0,0 100,0 50,100" fill="none" stroke="black" stroke-width="15" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 369 B |
@@ -13,8 +13,33 @@ details.test-node {
|
||||
> summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.success > summary::marker {
|
||||
/*
|
||||
* WebKit only supports color and font-size properties in ::marker, and
|
||||
* its ::-webkit-details-marker doesn't seem to work whatsoever; thus,
|
||||
* set a color as a fallback: while it may be confusing for colorblind
|
||||
* individuals, it's better than no indication of a test's state.
|
||||
* See: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::marker#browser_compatibility
|
||||
*/
|
||||
color: black;
|
||||
content: url("/static/success-closed.svg");
|
||||
}
|
||||
&.success[open] > summary::marker {
|
||||
content: url("/static/success-open.svg");
|
||||
}
|
||||
&.failure > summary::marker {
|
||||
color: red;
|
||||
content: url("/static/failure-closed.svg");
|
||||
}
|
||||
&.failure[open] > summary::marker {
|
||||
content: url("/static/failure-open.svg");
|
||||
}
|
||||
&.skip > summary::marker {
|
||||
color: blue;
|
||||
content: url("/static/skip-closed.svg");
|
||||
}
|
||||
&.skip[open] > summary::marker {
|
||||
content: url("/static/skip-open.svg");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +51,10 @@ p.test-desc {
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,32 @@ type TestTree = TestGroup | Test;
|
||||
type TestGroup = { name: string, children: TestTree[] };
|
||||
type Test = { name: string, test: (TestController) => void };
|
||||
|
||||
let TESTS: TestGroup[] = [];
|
||||
export class TestRegistrar {
|
||||
#suites: TestGroup[];
|
||||
|
||||
constructor() {
|
||||
this.#suites = [];
|
||||
}
|
||||
|
||||
suite(name: string, children: TestTree[]) {
|
||||
checkDuplicates(name, children)
|
||||
this.#suites.push({ name, children });
|
||||
}
|
||||
|
||||
run(reporter: Reporter) {
|
||||
reporter.register(this.#suites);
|
||||
for (const suite of this.#suites) {
|
||||
for (const c of suite.children) runTests(reporter, [suite.name], c);
|
||||
}
|
||||
reporter.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
export let TESTS = new TestRegistrar();
|
||||
|
||||
// Register a suite in the global registrar.
|
||||
export function suite(name: string, children: TestTree[]) {
|
||||
checkDuplicates(name, children);
|
||||
TESTS.push({ name, children });
|
||||
TESTS.suite(name, children);
|
||||
}
|
||||
|
||||
export function context(name: string, children: TestTree[]): TestTree {
|
||||
@@ -32,28 +53,44 @@ function checkDuplicates(parent: string, names: { name: string }[]) {
|
||||
}
|
||||
}
|
||||
|
||||
class FailNowSentinel {}
|
||||
export type TestState = "success" | "failure" | "skip";
|
||||
|
||||
class AbortSentinel {}
|
||||
|
||||
export class TestController {
|
||||
#state: TestState;
|
||||
logs: string[];
|
||||
#failed: boolean;
|
||||
|
||||
constructor() {
|
||||
this.#state = "success";
|
||||
this.logs = [];
|
||||
this.#failed = false;
|
||||
}
|
||||
|
||||
getState(): TestState {
|
||||
return this.#state;
|
||||
}
|
||||
|
||||
fail() {
|
||||
this.#failed = true;
|
||||
this.#state = "failure";
|
||||
}
|
||||
|
||||
failed(): boolean {
|
||||
return this.#failed;
|
||||
return this.#state === "failure";
|
||||
}
|
||||
|
||||
failNow(): never {
|
||||
this.fail();
|
||||
throw new FailNowSentinel();
|
||||
throw new AbortSentinel();
|
||||
}
|
||||
|
||||
skip(message?: string): never {
|
||||
if (message != null) this.log(message);
|
||||
if (this.#state !== "failure") this.#state = "skip";
|
||||
throw new AbortSentinel();
|
||||
}
|
||||
|
||||
skipped(): boolean {
|
||||
return this.#state === "skip";
|
||||
}
|
||||
|
||||
log(message: string) {
|
||||
@@ -75,18 +112,10 @@ export class TestController {
|
||||
// Execution
|
||||
|
||||
export interface TestResult {
|
||||
success: boolean;
|
||||
state: TestState;
|
||||
logs: string[];
|
||||
}
|
||||
|
||||
export function run(reporter: Reporter) {
|
||||
reporter.register(TESTS);
|
||||
for (const suite of TESTS) {
|
||||
for (const c of suite.children) runTests(reporter, [suite.name], c);
|
||||
}
|
||||
reporter.finalize();
|
||||
}
|
||||
|
||||
function runTests(reporter: Reporter, parents: string[], node: TestTree) {
|
||||
const path = [...parents, node.name];
|
||||
if ("children" in node) {
|
||||
@@ -97,11 +126,11 @@ function runTests(reporter: Reporter, parents: string[], node: TestTree) {
|
||||
try {
|
||||
node.test(controller);
|
||||
} catch (e) {
|
||||
if (!(e instanceof FailNowSentinel)) {
|
||||
if (!(e instanceof AbortSentinel)) {
|
||||
controller.error(extractExceptionString(e));
|
||||
}
|
||||
}
|
||||
reporter.update(path, { success: !controller.failed(), logs: controller.logs });
|
||||
reporter.update(path, { state: controller.getState(), logs: controller.logs });
|
||||
}
|
||||
|
||||
function extractExceptionString(e: any): string {
|
||||
@@ -124,6 +153,30 @@ export interface Reporter {
|
||||
finalize(): void;
|
||||
}
|
||||
|
||||
export class NoOpReporter implements Reporter {
|
||||
suites: TestGroup[];
|
||||
results: ({ path: string[] } & TestResult)[];
|
||||
finalized: boolean;
|
||||
|
||||
constructor() {
|
||||
this.suites = [];
|
||||
this.results = [];
|
||||
this.finalized = false;
|
||||
}
|
||||
|
||||
register(suites: TestGroup[]) {
|
||||
this.suites = suites;
|
||||
}
|
||||
|
||||
update(path: string[], result: TestResult) {
|
||||
this.results.push({ path, ...result });
|
||||
}
|
||||
|
||||
finalize() {
|
||||
this.finalized = true;
|
||||
}
|
||||
}
|
||||
|
||||
export interface Stream {
|
||||
writeln(s: string): void;
|
||||
}
|
||||
@@ -133,14 +186,16 @@ const SEP = " ❯ ";
|
||||
export class StreamReporter implements Reporter {
|
||||
stream: Stream;
|
||||
verbose: boolean;
|
||||
#successes: ({ path: string[] } & TestResult)[];
|
||||
#failures: ({ path: string[] } & TestResult)[];
|
||||
counts: { successes: number, failures: number };
|
||||
#skips: ({ path: string[] } & TestResult)[];
|
||||
|
||||
constructor(stream: Stream, verbose: boolean = false) {
|
||||
this.stream = stream;
|
||||
this.verbose = verbose;
|
||||
this.#successes = [];
|
||||
this.#failures = [];
|
||||
this.counts = { successes: 0, failures: 0 };
|
||||
this.#skips = [];
|
||||
}
|
||||
|
||||
register(suites: TestGroup[]) {}
|
||||
@@ -148,31 +203,52 @@ export class StreamReporter implements Reporter {
|
||||
update(path: string[], result: TestResult) {
|
||||
if (path.length === 0) throw new RangeError("path is empty");
|
||||
const pathStr = path.join(SEP);
|
||||
if (result.success) {
|
||||
this.counts.successes++;
|
||||
switch (result.state) {
|
||||
case "success":
|
||||
this.#successes.push({ path, ...result });
|
||||
if (this.verbose) this.stream.writeln(`✅️ ${pathStr}`);
|
||||
} else {
|
||||
this.counts.failures++;
|
||||
this.stream.writeln(`⚠️ ${pathStr}`);
|
||||
break;
|
||||
case "failure":
|
||||
this.#failures.push({ path, ...result });
|
||||
this.stream.writeln(`⚠️ ${pathStr}`);
|
||||
break;
|
||||
case "skip":
|
||||
this.#skips.push({ path, ...result });
|
||||
this.stream.writeln(`⏭️ ${pathStr}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
finalize() {
|
||||
if (this.verbose) this.#displaySection("successes", this.#successes, true);
|
||||
this.#displaySection("failures", this.#failures);
|
||||
this.#displaySection("skips", this.#skips);
|
||||
this.stream.writeln("");
|
||||
this.stream.writeln(
|
||||
`${this.#successes.length} succeeded, ${this.#failures.length} failed`
|
||||
+ (this.#skips.length ? `, ${this.#skips.length} skipped` : "")
|
||||
);
|
||||
}
|
||||
|
||||
#displaySection(name: string, data: ({ path: string[] } & TestResult)[], ignoreEmpty: boolean = false) {
|
||||
if (!data.length) return;
|
||||
|
||||
// Transform [{ path: ["a", "b", "c"] }, { path: ["a", "b", "d"] }]
|
||||
// into { "a ❯ b": ["c", "d"] }.
|
||||
let pathMap = new Map<string, ({ name: string } & TestResult)[]>();
|
||||
for (const f of this.#failures) {
|
||||
const key = f.path.slice(0, -1).join(SEP);
|
||||
for (const t of data) {
|
||||
const key = t.path.slice(0, -1).join(SEP);
|
||||
if (!pathMap.has(key)) pathMap.set(key, []);
|
||||
pathMap.get(key).push({ name: f.path.at(-1), ...f });
|
||||
pathMap.get(key).push({ name: t.path.at(-1), ...t });
|
||||
}
|
||||
|
||||
this.stream.writeln("");
|
||||
this.stream.writeln("FAILURES");
|
||||
this.stream.writeln("========");
|
||||
this.stream.writeln(name.toUpperCase());
|
||||
this.stream.writeln("=".repeat(name.length));
|
||||
|
||||
for (const [path, tests] of pathMap) {
|
||||
for (let [path, tests] of pathMap) {
|
||||
if (ignoreEmpty) tests = tests.filter((t) => t.logs.length);
|
||||
if (tests.length === 0) continue;
|
||||
if (tests.length === 1) {
|
||||
this.#writeOutput(tests[0], path ? `${path}${SEP}` : "", false);
|
||||
} else {
|
||||
@@ -180,10 +256,6 @@ export class StreamReporter implements Reporter {
|
||||
for (const t of tests) this.#writeOutput(t, " - ", true);
|
||||
}
|
||||
}
|
||||
|
||||
this.stream.writeln("");
|
||||
const { successes, failures } = this.counts;
|
||||
this.stream.writeln(`${successes} succeeded, ${failures} failed`);
|
||||
}
|
||||
|
||||
#writeOutput(test: { name: string } & TestResult, prefix: string, nested: boolean) {
|
||||
@@ -209,7 +281,10 @@ export class DOMReporter implements Reporter {
|
||||
|
||||
update(path: string[], result: TestResult) {
|
||||
if (path.length === 0) throw new RangeError("path is empty");
|
||||
const counter = document.getElementById(result.success ? "successes" : "failures");
|
||||
if (result.state === "skip") {
|
||||
document.getElementById("skip-counter-text").classList.remove("hidden");
|
||||
}
|
||||
const counter = document.getElementById(`${result.state}-counter`);
|
||||
counter.innerText = (Number(counter.innerText) + 1).toString();
|
||||
let parent = document.getElementById("root");
|
||||
for (const node of path) {
|
||||
@@ -230,9 +305,10 @@ export class DOMReporter implements Reporter {
|
||||
child.appendChild(summary);
|
||||
parent.appendChild(child);
|
||||
}
|
||||
if (!result.success) {
|
||||
child.open = true;
|
||||
child.classList.add("failure");
|
||||
if (result.state === "failure") child.open = true;
|
||||
if (!child.classList.contains("failure") &&
|
||||
(result.state !== "success" || !child.classList.contains("skip"))) {
|
||||
child.classList.add(result.state);
|
||||
}
|
||||
parent = child;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { context, group, suite, test } from "./test.js";
|
||||
import { NoOpReporter, TestRegistrar, context, group, suite, test } from "./test.js";
|
||||
|
||||
suite("dog", [
|
||||
group("tail", [
|
||||
@@ -37,4 +37,49 @@ suite("cat", [
|
||||
test("likes headpats", (t) => {
|
||||
t.log("meow");
|
||||
}),
|
||||
test("owns skipping rope", (t) => {
|
||||
t.skip("this cat is stuck in your machine!");
|
||||
t.log("never logged");
|
||||
}),
|
||||
test("tester tester", (t) => {
|
||||
const r = new TestRegistrar();
|
||||
r.suite("explod", [
|
||||
test("with yarn", (t) => {
|
||||
t.log("YAY");
|
||||
}),
|
||||
]);
|
||||
const reporter = new NoOpReporter();
|
||||
r.run(reporter);
|
||||
if (reporter.suites.length !== 1) {
|
||||
t.fatal(`incorrect number of suites registered got=${reporter.suites.length} want=1`);
|
||||
}
|
||||
const suite = reporter.suites[0];
|
||||
if (suite.name !== "explod") {
|
||||
t.error(`suite name incorrect got='${suite.name}' want='explod'`);
|
||||
}
|
||||
if (suite.children.length !== 1) {
|
||||
t.fatal(`incorrect number of suite children got=${suite.children.length} want=1`);
|
||||
}
|
||||
const test_ = suite.children[0];
|
||||
if (test_.name !== "with yarn") {
|
||||
t.error(`incorrect test name got='${test_.name}' want='with yarn'`);
|
||||
}
|
||||
if ("children" in test_) {
|
||||
t.error(`expected leaf node, got group of ${test_.children.length} children`);
|
||||
}
|
||||
if (!reporter.finalized) t.error(`expected reporter to have been finalized`);
|
||||
if (reporter.results.length !== 1) {
|
||||
t.fatal(`incorrect result count got=${reporter.results.length} want=1`);
|
||||
}
|
||||
const result = reporter.results[0];
|
||||
if (!(result.path.length === 2 &&
|
||||
result.path[0] === "explod" &&
|
||||
result.path[1] === "with yarn")) {
|
||||
t.error(`incorrect result path got=${result.path} want=["explod", "with yarn"]`);
|
||||
}
|
||||
if (result.state !== "success") t.error(`expected test to succeed`);
|
||||
if (!(result.logs.length === 1 && result.logs[0] === "YAY")) {
|
||||
t.error(`incorrect result logs got=${result.logs} want=["YAY"]`);
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
|
||||
<main>
|
||||
<p id="counters">
|
||||
<span id="successes">0</span> succeeded, <span id="failures">0</span> failed.
|
||||
<span id="success-counter">0</span> succeeded, <span id="failure-counter">0</span>
|
||||
failed<span id="skip-counter-text" class="hidden">, <span id="skip-counter">0</span> skipped</span>.
|
||||
</p>
|
||||
|
||||
<div id="root">
|
||||
@@ -20,8 +21,8 @@
|
||||
|
||||
<script type="module">
|
||||
import "./static/all_tests.js";
|
||||
import { DOMReporter, run } from "./static/test.js";
|
||||
run(new DOMReporter());
|
||||
import { DOMReporter, TESTS } from "./static/test.js";
|
||||
TESTS.run(new DOMReporter());
|
||||
</script>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user