forked from rosa/hakurei
Compare commits
8 Commits
90df5c13fb
...
525e413e75
| Author | SHA1 | Date | |
|---|---|---|---|
|
525e413e75
|
|||
|
2c32cc7812
|
|||
|
48bf09c2c8
|
|||
|
a743439ccb
|
|||
|
7f39e764ea
|
|||
|
1ac9e120d1
|
|||
|
d512e7900d
|
|||
|
3a5a15b36f
|
@@ -1,2 +1,2 @@
|
||||
// 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) {
|
||||
reporter.register(this.#suites);
|
||||
for (const suite of this.#suites) {
|
||||
for (const c of suite.children) runTests(reporter, [suite.name], c);
|
||||
}
|
||||
@@ -157,6 +158,9 @@ function extractExceptionString(e: any): string {
|
||||
// Reporting
|
||||
|
||||
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
|
||||
// results, which would indeed greatly simplify implementation of reporters,
|
||||
// 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
|
||||
// testing ourself.
|
||||
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 });
|
||||
}
|
||||
@@ -229,6 +239,9 @@ export class StreamReporter implements Reporter {
|
||||
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) {
|
||||
if (path.length === 0) throw new RangeError("path is empty");
|
||||
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
|
||||
// collapsible elements in the DOM.
|
||||
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) {
|
||||
if (path.length === 0) throw new RangeError("path is empty");
|
||||
if (result.state === "skip") {
|
||||
@@ -419,3 +439,40 @@ export class DOMReporter implements Reporter {
|
||||
|
||||
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