forked from rosa/hakurei
Compare commits
8 Commits
525e413e75
...
90df5c13fb
| Author | SHA1 | Date | |
|---|---|---|---|
|
90df5c13fb
|
|||
|
3b5b903f0e
|
|||
|
443db2d03d
|
|||
|
f2d883ee8c
|
|||
|
536e3c97ea
|
|||
|
6a59d7df86
|
|||
|
e198a98daa
|
|||
|
4f0841e1eb
|
@@ -1,2 +1,2 @@
|
|||||||
// Import all test files to register their test suites.
|
// Import all test files to register their test suites.
|
||||||
import "./sample_test.js";
|
import "./index_test.js";
|
||||||
|
|||||||
2
cmd/mbf/internal/pkgserver/ui/index_test.ts
Normal file
2
cmd/mbf/internal/pkgserver/ui/index_test.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { suite, test } from "./jstest/jstest.js";
|
||||||
|
import "./index.js";
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import "../all_tests.js";
|
||||||
|
import { GoTestReporter, GLOBAL_REGISTRAR } from "./jstest.js";
|
||||||
|
GLOBAL_REGISTRAR.run(new GoTestReporter());
|
||||||
@@ -23,6 +23,7 @@ export class TestRegistrar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run(reporter: Reporter) {
|
run(reporter: Reporter) {
|
||||||
|
reporter.register(this.#suites);
|
||||||
for (const suite of this.#suites) {
|
for (const suite of this.#suites) {
|
||||||
for (const c of suite.children) runTests(reporter, [suite.name], c);
|
for (const c of suite.children) runTests(reporter, [suite.name], c);
|
||||||
}
|
}
|
||||||
@@ -157,6 +158,9 @@ function extractExceptionString(e: any): string {
|
|||||||
// Reporting
|
// Reporting
|
||||||
|
|
||||||
export interface Reporter {
|
export interface Reporter {
|
||||||
|
// A notable feature—or flaw, to some—of the DSL is that the tree of tests
|
||||||
|
// is statically known, which might greatly aid in implementing a reporter.
|
||||||
|
register(suites: TestGroup[]): void;
|
||||||
// While we could simply call a function with a tree representing all
|
// While we could simply call a function with a tree representing all
|
||||||
// results, which would indeed greatly simplify implementation of reporters,
|
// results, which would indeed greatly simplify implementation of reporters,
|
||||||
// simply registering a path and allowing the reporter to—either implicitly
|
// simply registering a path and allowing the reporter to—either implicitly
|
||||||
@@ -186,14 +190,20 @@ export interface Reporter {
|
|||||||
// underlying result tree; this makes it extremely convenient in some cases like
|
// underlying result tree; this makes it extremely convenient in some cases like
|
||||||
// testing ourself.
|
// testing ourself.
|
||||||
export class NoOpReporter implements Reporter {
|
export class NoOpReporter implements Reporter {
|
||||||
|
suites: TestGroup[];
|
||||||
results: ({ path: string[] } & TestResult)[];
|
results: ({ path: string[] } & TestResult)[];
|
||||||
finalized: boolean;
|
finalized: boolean;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.suites = [];
|
||||||
this.results = [];
|
this.results = [];
|
||||||
this.finalized = false;
|
this.finalized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register(suites: TestGroup[]) {
|
||||||
|
this.suites = suites;
|
||||||
|
}
|
||||||
|
|
||||||
update(path: string[], result: TestResult) {
|
update(path: string[], result: TestResult) {
|
||||||
this.results.push({ path, ...result });
|
this.results.push({ path, ...result });
|
||||||
}
|
}
|
||||||
@@ -229,6 +239,9 @@ export class StreamReporter implements Reporter {
|
|||||||
return this.#successes.length > 0 && this.#failures.length === 0;
|
return this.#successes.length > 0 && this.#failures.length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We don't need the structure for reporting.
|
||||||
|
register(suites: TestGroup[]) {}
|
||||||
|
|
||||||
update(path: string[], result: TestResult) {
|
update(path: string[], result: TestResult) {
|
||||||
if (path.length === 0) throw new RangeError("path is empty");
|
if (path.length === 0) throw new RangeError("path is empty");
|
||||||
const pathStr = path.join(SEP);
|
const pathStr = path.join(SEP);
|
||||||
@@ -342,6 +355,13 @@ function assertGetElementById(id: string): HTMLElement {
|
|||||||
// A reporter that directly translates a tree of results into a tree of
|
// A reporter that directly translates a tree of results into a tree of
|
||||||
// collapsible elements in the DOM.
|
// collapsible elements in the DOM.
|
||||||
export class DOMReporter implements Reporter {
|
export class DOMReporter implements Reporter {
|
||||||
|
// It is very difficult to implement this using the statically known tree,
|
||||||
|
// because Map doesn't handle array keys properly (to store the path), and
|
||||||
|
// it's unknown of there's any way to implement it without writing one's own
|
||||||
|
// data types. Oh well; using the DOM as a data structure might seem hacky
|
||||||
|
// but it does have its benefits, apart from encouraging a tagless final.
|
||||||
|
register(suites: TestGroup[]) {}
|
||||||
|
|
||||||
update(path: string[], result: TestResult) {
|
update(path: string[], result: TestResult) {
|
||||||
if (path.length === 0) throw new RangeError("path is empty");
|
if (path.length === 0) throw new RangeError("path is empty");
|
||||||
if (result.state === "skip") {
|
if (result.state === "skip") {
|
||||||
@@ -419,3 +439,40 @@ export class DOMReporter implements Reporter {
|
|||||||
|
|
||||||
finalize() {}
|
finalize() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GoNode {
|
||||||
|
name: string;
|
||||||
|
subtests?: GoNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to display results via `go test`, via some glue code from the Go side.
|
||||||
|
// TODO(ophestra): said glue code has to be written.
|
||||||
|
export class GoTestReporter implements Reporter {
|
||||||
|
register(suites: TestGroup[]) {
|
||||||
|
console.log(JSON.stringify(suites.map(GoTestReporter.serialize)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a test tree into the one expected by the Go code.
|
||||||
|
static serialize(node: TestTree): GoNode {
|
||||||
|
return {
|
||||||
|
name: node.name,
|
||||||
|
subtests: "children" in node ? node.children.map(GoTestReporter.serialize) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
update(path: string[], result: TestResult) {
|
||||||
|
let state: number;
|
||||||
|
switch (result.state) {
|
||||||
|
case "success": state = 0; break;
|
||||||
|
case "failure": state = 1; break;
|
||||||
|
case "skip": state = 2; break;
|
||||||
|
}
|
||||||
|
console.log(JSON.stringify({ path, state, logs: result.logs }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unnecessary but convenient on the Go side, so that it doesn't have to
|
||||||
|
// infer this via process exit.
|
||||||
|
finalize() {
|
||||||
|
console.log("null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
import { NoOpReporter, TestRegistrar, context, group, suite, test } from "./jstest/jstest.js";
|
|
||||||
|
|
||||||
suite("dog", [
|
|
||||||
group("tail", [
|
|
||||||
test("wags when happy", (t) => {
|
|
||||||
if (0 / 0 !== Infinity / Infinity) {
|
|
||||||
t.fatal("undefined must not be defined");
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
test("idle when down", (t) => {
|
|
||||||
t.log("test test");
|
|
||||||
t.error("dog whining noises go here");
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
test("likes headpats", (t) => {
|
|
||||||
if (2 !== 2) {
|
|
||||||
t.error("IEEE 754 violated: 2 is NaN");
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
context("near cat", [
|
|
||||||
test("is ecstatic", (t) => {
|
|
||||||
if (("b" + "a" + + "a" + "a").toLowerCase() === "banana") {
|
|
||||||
t.error("🍌🍌🍌");
|
|
||||||
t.error("🍌🍌🍌");
|
|
||||||
t.error("🍌🍌🍌");
|
|
||||||
t.failNow();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
test("playfully bites cats' tails", (t) => {
|
|
||||||
t.log("arf!");
|
|
||||||
throw new Error("nom");
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
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.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"]`);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
Reference in New Issue
Block a user