Skip to content

Commit f9d8ce7

Browse files
committed
chore: start fleshing out testing a little further
1 parent 3899e22 commit f9d8ce7

File tree

7 files changed

+114
-63
lines changed

7 files changed

+114
-63
lines changed

.env.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
NODE_OPTIONS="--experimental-strip-types --disable-warning=ExperimentalWarning"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
node_modules
2+
test/generated.ts

bin/generate-test-fixture.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { writeFile } from "node:fs/promises";
2+
import { resolve } from "node:path";
3+
import { generateClientType } from "../lib/build/index.ts";
4+
import { server } from "../test/fixture.ts";
5+
6+
const fixtureTypes = generateClientType(server);
7+
await writeFile(resolve(import.meta.dirname, "../test/generated.ts"), fixtureTypes, { encoding: "utf8" });

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
],
1414
"scripts": {
1515
"clean": "rm -f lib/*.js lib/*.d.ts lib/**/*.js lib/**/*.d.ts",
16-
"test": "node --experimental-strip-types --test",
16+
"pretest": "node --env-file=.env.test bin/generate-test-fixture.ts",
17+
"test": "node --env-file=.env.test --test",
1718
"posttest": "npm run lint",
1819
"lint": "eslint .",
1920
"prelint": "tsc",

test/fetch.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { test, type TestContext } from "node:test";
2+
import { server, Client } from "./fixture.ts";
3+
import type { RouteMap } from "./generated.ts";
4+
5+
await test("GET - fetch", async (t) => {
6+
let client: Client;
7+
t.before(async () => {
8+
await server.start();
9+
client = new Client({ url: server.info.uri });
10+
});
11+
12+
t.after(async () => {
13+
await server.stop();
14+
});
15+
16+
await t.test("/simple", async (t: TestContext) => {
17+
const res = await client.get("/simple");
18+
t.assert.equal(res.status, 200);
19+
t.assert.equal(res.url, `${server.info.uri}/simple`);
20+
t.assert.deepStrictEqual<RouteMap["GET"]["/simple"]["response"]>(res.data, { success: true });
21+
});
22+
23+
await t.test("/query", async (t: TestContext) => {
24+
const withoutQueryRes = await client.get("/query");
25+
t.assert.equal(withoutQueryRes.status, 200);
26+
t.assert.equal(withoutQueryRes.url, `${server.info.uri}/query`);
27+
t.assert.deepStrictEqual<RouteMap["GET"]["/query"]["response"]>(withoutQueryRes.data, { flag: false });
28+
29+
const query: RouteMap["GET"]["/query"]["query"] = { flag: true };
30+
const withQueryRes = await client.get("/query", { query });
31+
t.assert.equal(withQueryRes.status, 200);
32+
t.assert.equal(withQueryRes.url, `${server.info.uri}/query?flag=true`);
33+
t.assert.deepStrictEqual<RouteMap["GET"]["/query"]["response"]>(withQueryRes.data, { flag: true });
34+
});
35+
});

test/fixture.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Hapi, { type Request } from "@hapi/hapi";
2+
import Joi from "joi";
3+
import { Client as BaseClient } from "../lib/index.ts";
4+
import type { RouteMap } from "./generated.ts";
5+
6+
export const expectType = <T>(_: T): void => void 0;
7+
export type TypeEqual<Target, Value> = (<T>() => T extends Target ? 1 : 2) extends <T>() => T extends Value ? 1 : 2 ? true : false;
8+
9+
export const server = Hapi.server();
10+
server.route([
11+
{
12+
method: "GET",
13+
path: "/simple",
14+
options: {
15+
response: {
16+
status: {
17+
200: Joi.object({ success: Joi.boolean().required() }),
18+
},
19+
},
20+
handler() {
21+
return { success: true };
22+
},
23+
},
24+
},
25+
{
26+
method: "GET",
27+
path: "/query",
28+
options: {
29+
validate: {
30+
query: Joi.object({
31+
flag: Joi.boolean().default(false),
32+
}),
33+
},
34+
response: {
35+
status: {
36+
200: Joi.object({ flag: Joi.boolean().required() }),
37+
},
38+
},
39+
handler(request: Request<{ Query: { flag: boolean } }>) {
40+
return { flag: request.query.flag };
41+
},
42+
},
43+
},
44+
]);
45+
46+
// TODO: test the types in here somehow
47+
48+
export class Client extends BaseClient<RouteMap> {};

test/inject.ts

Lines changed: 20 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,27 @@
1-
import Hapi, { type Request } from "@hapi/hapi";
2-
import Joi from "joi";
31
import { test, type TestContext } from "node:test";
4-
import { Client } from "../lib/index.ts";
2+
import { server, Client } from "./fixture.ts";
3+
import type { RouteMap } from "./generated.ts";
54

6-
const expectType = <T>(_: T): void => void 0;
7-
type TypeEqual<Target, Value> = (<T>() => T extends Target ? 1 : 2) extends <T>() => T extends Value ? 1 : 2 ? true : false;
5+
await test("GET - server.inject", async (t) => {
6+
const client = new Client({ server });
87

9-
await test("server.inject", async (t) => {
10-
await t.test("can send strongly typed requests", async (t: TestContext) => {
11-
const server = Hapi.server();
12-
server.route({
13-
method: "GET",
14-
path: "/test",
15-
options: {
16-
validate: {
17-
query: Joi.object({
18-
success: Joi.boolean().default(true),
19-
}),
20-
},
21-
response: {
22-
status: {
23-
200: Joi.object({ success: Joi.boolean() }),
24-
},
25-
},
26-
handler(req: Request<{ Query: { success: boolean } }>) {
27-
return { success: req.query.success };
28-
},
29-
},
30-
});
31-
32-
type TestRoutes = {
33-
GET: {
34-
"/test": {
35-
query?: {
36-
success?: boolean;
37-
};
38-
response: {
39-
success: boolean;
40-
};
41-
};
42-
};
43-
};
44-
45-
const client = new Client<TestRoutes>({ server });
46-
47-
type availablePaths = Parameters<typeof client.get>[0];
48-
type expectedPaths = "/test";
49-
expectType<TypeEqual<availablePaths, expectedPaths>>(true);
50-
51-
type availableOptions = Parameters<typeof client.get>[1];
52-
type expectedOptions = {
53-
headers?: HeadersInit;
54-
params?: never;
55-
query?: {
56-
success?: boolean;
57-
};
58-
payload?: never;
59-
};
60-
expectType<TypeEqual<availableOptions, expectedOptions>>(true);
61-
62-
const res = await client.get("/test");
8+
await t.test("/simple", async (t: TestContext) => {
9+
const res = await client.get("/simple");
6310
t.assert.equal(res.status, 200);
64-
t.assert.equal(res.url, "/test");
11+
t.assert.equal(res.url, "/simple");
12+
t.assert.deepStrictEqual<RouteMap["GET"]["/simple"]["response"]>(res.data, { success: true });
13+
});
6514

66-
expectType<{ success: boolean }>(res.data);
67-
t.assert.deepStrictEqual(res.data, { success: true });
15+
await t.test("/query", async (t: TestContext) => {
16+
const withoutQueryRes = await client.get("/query");
17+
t.assert.equal(withoutQueryRes.status, 200);
18+
t.assert.equal(withoutQueryRes.url, "/query");
19+
t.assert.deepStrictEqual<RouteMap["GET"]["/query"]["response"]>(withoutQueryRes.data, { flag: false });
20+
21+
const query: RouteMap["GET"]["/query"]["query"] = { flag: true };
22+
const withQueryRes = await client.get("/query", { query });
23+
t.assert.equal(withQueryRes.status, 200);
24+
t.assert.equal(withQueryRes.url, "/query?flag=true");
25+
t.assert.deepStrictEqual<RouteMap["GET"]["/query"]["response"]>(withQueryRes.data, { flag: true });
6826
});
6927
});

0 commit comments

Comments
 (0)