-
-
Notifications
You must be signed in to change notification settings - Fork 38
/
_test_util.ts
120 lines (116 loc) · 3.51 KB
/
_test_util.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright 2019-2020 Yusuke Sakurai. All rights reserved. MIT license.
import { Router } from "./router.ts";
import {
AssertionError,
assertThrowsAsync,
} from "./vendor/https/deno.land/std/testing/asserts.ts";
import { RoutingError } from "./error.ts";
import { createRecorder } from "./testing.ts";
import { encode } from "./_util.ts";
import { green, red } from "./vendor/https/deno.land/std/fmt/colors.ts";
import { STATUS_TEXT } from "./vendor/https/deno.land/std/http/http_status.ts";
type PromiseOrVal<T> = T | Promise<T>;
export type SetupFunc = () => PromiseOrVal<TearDownFunc | void>;
export type TearDownFunc = () => PromiseOrVal<void>;
export interface GroupBody {
setupAll(func: SetupFunc): void;
setupEach(func: SetupFunc): void;
test: typeof Deno.test;
}
export type GroupHead = Omit<Deno.TestDefinition, "fn">;
type TestFunc = () => PromiseOrVal<void>;
export function group(
desc: string | GroupHead,
body: (p: GroupBody) => void,
): void {
let opts: GroupHead;
if (typeof desc !== "string") {
opts = { ...desc };
} else {
opts = { name: desc };
}
let setupAllFuncs: (SetupFunc)[] = [];
let setupEachFuncs: (SetupFunc)[] = [];
function setupAll(f: SetupFunc): void {
setupAllFuncs.push(f);
}
function setupEach(f: SetupFunc): void {
setupEachFuncs.push(f);
}
const tests: Deno.TestDefinition[] = [];
function wrap(funcs: SetupFunc[], fn: TestFunc): TestFunc {
return async () => {
const tearDowns: (TearDownFunc)[] = [];
for (const setupFunc of funcs) {
if (!setupFunc) continue;
const tearDown = await setupFunc?.();
if (!tearDown) continue;
tearDowns.push(tearDown);
}
try {
await fn();
} finally {
for (let i = tearDowns.length - 1; i >= 0; i--) {
const tearDown = tearDowns[i];
await tearDown?.();
}
}
};
}
function test(f: TestFunc): void;
function test(s: string, f: TestFunc): void;
function test(d: Deno.TestDefinition): void;
function test(
arg1: (TestFunc | string | Deno.TestDefinition),
arg2?: TestFunc,
) {
let fn: TestFunc;
let name: string;
if (typeof arg1 === "function") {
fn = arg1;
name = fn.name ?? "";
} else if (typeof arg1 === "string") {
if (arg2 == null) throw new Error("invalid arg");
name = arg1;
fn = arg2;
} else {
name = arg1.name;
fn = arg1.fn;
}
tests.push({ ...opts, name, fn: wrap(setupEachFuncs, fn) });
}
body({ test, setupAll, setupEach });
Deno.test({
...opts,
fn: wrap(setupAllFuncs, async () => {
await Deno.writeAll(Deno.stdout, encode("\n"));
for (const { fn, name } of tests) {
await Deno.writeAll(Deno.stdout, encode(` ${name} ... `));
try {
await fn();
await Deno.writeAll(Deno.stdout, encode(green("ok") + "\n"));
} catch (e) {
if (e instanceof AssertionError) {
await Deno.writeAll(Deno.stdout, encode(red("FAILED") + "\n"));
} else {
await Deno.writeAll(Deno.stdout, encode(red("ERROR") + "\n"));
}
throw e;
}
}
}),
});
}
export function makeGet(router: Router, method = "GET") {
return async function get(url: string) {
const rec = createRecorder({ method, url });
await router.handleRoute("", rec);
return rec.response();
};
}
export async function assertRoutingError(
f: () => Promise<any>,
status: number,
) {
await assertThrowsAsync(f, RoutingError, STATUS_TEXT.get(status));
}