Skip to content

Commit 1152f24

Browse files
committed
🌿 Add more exhaustive test cases
1 parent 587341e commit 1152f24

File tree

1 file changed

+69
-34
lines changed

1 file changed

+69
-34
lines changed

is_test.ts

Lines changed: 69 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,17 @@ const examples = {
3535
boolean: [true, false],
3636
array: [[], [0, 1, 2], ["a", "b", "c"], [0, "a", true]],
3737
record: [{}, { a: 0, b: 1, c: 2 }, { a: "a", b: "b", c: "c" }],
38-
function: [function () {}],
38+
function: [function a() {}, () => {}],
3939
null: [null],
4040
undefined: [undefined],
4141
symbol: [Symbol("a"), Symbol("b"), Symbol("c")],
42-
};
42+
date: [new Date(1690248225000), new Date(0)],
43+
promise: [new Promise(() => {})],
44+
} as const;
4345

4446
function stringify(x: unknown): string {
47+
if (x instanceof Date) return `Date(${x.valueOf()})`;
48+
if (x instanceof Promise) return "Promise";
4549
if (typeof x === "function") return x.toString();
4650
if (typeof x === "bigint") return `${x}n`;
4751
if (typeof x === "symbol") return x.toString();
@@ -51,10 +55,18 @@ function stringify(x: unknown): string {
5155
async function testWithExamples<T>(
5256
t: Deno.TestContext,
5357
pred: Predicate<T>,
54-
validExamples: (keyof typeof examples)[],
58+
opts?: {
59+
validExamples?: (keyof typeof examples)[];
60+
excludeExamples?: (keyof typeof examples)[];
61+
},
5562
): Promise<void> {
56-
for (const [name, example] of Object.entries(examples)) {
57-
const expect = validExamples.includes(name as keyof typeof examples);
63+
const { validExamples = [], excludeExamples = [] } = opts ?? {};
64+
const exampleEntries = (Object.entries(examples) as unknown as [
65+
name: keyof typeof examples,
66+
example: unknown[],
67+
][]).filter(([k]) => !excludeExamples.includes(k));
68+
for (const [name, example] of exampleEntries) {
69+
const expect = validExamples.includes(name);
5870
for (const v of example) {
5971
await t.step(
6072
`returns ${expect} on ${stringify(v)}`,
@@ -67,23 +79,23 @@ async function testWithExamples<T>(
6779
}
6880

6981
Deno.test("isString", async (t) => {
70-
await testWithExamples(t, isString, ["string"]);
82+
await testWithExamples(t, isString, { validExamples: ["string"] });
7183
});
7284

7385
Deno.test("isNumber", async (t) => {
74-
await testWithExamples(t, isNumber, ["number"]);
86+
await testWithExamples(t, isNumber, { validExamples: ["number"] });
7587
});
7688

7789
Deno.test("isBigInt", async (t) => {
78-
await testWithExamples(t, isBigInt, ["bigint"]);
90+
await testWithExamples(t, isBigInt, { validExamples: ["bigint"] });
7991
});
8092

8193
Deno.test("isBoolean", async (t) => {
82-
await testWithExamples(t, isBoolean, ["boolean"]);
94+
await testWithExamples(t, isBoolean, { validExamples: ["boolean"] });
8395
});
8496

8597
Deno.test("isArray", async (t) => {
86-
await testWithExamples(t, isArray, ["array"]);
98+
await testWithExamples(t, isArray, { validExamples: ["array"] });
8799
});
88100

89101
Deno.test("isArrayOf<T>", async (t) => {
@@ -103,6 +115,9 @@ Deno.test("isArrayOf<T>", async (t) => {
103115
assertEquals(isArrayOf(isNumber)(["a", "b", "c"]), false);
104116
assertEquals(isArrayOf(isString)([true, false, true]), false);
105117
});
118+
await testWithExamples(t, isArrayOf((_: unknown): _ is unknown => true), {
119+
excludeExamples: ["array"],
120+
});
106121
});
107122

108123
Deno.test("isTupleOf<T>", async (t) => {
@@ -118,12 +133,21 @@ Deno.test("isTupleOf<T>", async (t) => {
118133
await t.step("returns true on T tuple", () => {
119134
const predTup = [isNumber, isString, isBoolean] as const;
120135
assertEquals(isTupleOf(predTup)([0, "a", true]), true);
136+
assertEquals(isTupleOf([])([]), true, "Specify empty predicates");
121137
});
122138
await t.step("returns false on non T tuple", () => {
123139
const predTup = [isNumber, isString, isBoolean] as const;
124140
assertEquals(isTupleOf(predTup)([0, 1, 2]), false);
125141
assertEquals(isTupleOf(predTup)([0, "a"]), false);
126142
assertEquals(isTupleOf(predTup)([0, "a", true, 0]), false);
143+
assertEquals(
144+
isTupleOf([])([0]),
145+
false,
146+
"Specify empty predicates and value has entry",
147+
);
148+
});
149+
await testWithExamples(t, isTupleOf([(_: unknown): _ is unknown => true]), {
150+
excludeExamples: ["array"],
127151
});
128152
});
129153

@@ -154,10 +178,15 @@ Deno.test("isUniformTupleOf<T>", async (t) => {
154178
assertEquals(isUniformTupleOf(4)([0, 1, 2, 3, 4]), false);
155179
assertEquals(isUniformTupleOf(3, is.Number)(["a", "b", "c"]), false);
156180
});
181+
await testWithExamples(t, isUniformTupleOf(4), {
182+
excludeExamples: ["array"],
183+
});
157184
});
158185

159186
Deno.test("isRecord", async (t) => {
160-
await testWithExamples(t, isRecord, ["record"]);
187+
await testWithExamples(t, isRecord, {
188+
validExamples: ["record", "date", "promise"],
189+
});
161190
});
162191

163192
Deno.test("isRecordOf<T>", async (t) => {
@@ -179,6 +208,9 @@ Deno.test("isRecordOf<T>", async (t) => {
179208
assertEquals(isRecordOf(isNumber)({ a: "a" }), false);
180209
assertEquals(isRecordOf(isString)({ a: true }), false);
181210
});
211+
await testWithExamples(t, isRecordOf((_: unknown): _ is unknown => true), {
212+
excludeExamples: ["record", "date", "promise"],
213+
});
182214
});
183215

184216
Deno.test("isObjectOf<T>", async (t) => {
@@ -225,10 +257,15 @@ Deno.test("isObjectOf<T>", async (t) => {
225257
false,
226258
);
227259
});
260+
await testWithExamples(
261+
t,
262+
isObjectOf({ a: (_: unknown): _ is unknown => true }),
263+
{ excludeExamples: ["record"] },
264+
);
228265
});
229266

230267
Deno.test("isFunction", async (t) => {
231-
await testWithExamples(t, isFunction, ["function"]);
268+
await testWithExamples(t, isFunction, { validExamples: ["function"] });
232269
});
233270

234271
Deno.test("isInstanceOf<T>", async (t) => {
@@ -238,19 +275,17 @@ Deno.test("isInstanceOf<T>", async (t) => {
238275
assertEquals(isInstanceOf(Date)(new Date()), true);
239276
assertEquals(isInstanceOf(Promise<string>)(new Promise(() => {})), true);
240277
});
241-
await t.step("returns false on non function", () => {
278+
await t.step("with user-defined class", async (t) => {
242279
class Cls {}
243-
assertEquals(isInstanceOf(Cls)(new Date()), false);
244-
assertEquals(isInstanceOf(Cls)(new Promise(() => {})), false);
245-
assertEquals(isInstanceOf(Cls)(""), false);
246-
assertEquals(isInstanceOf(Cls)(0), false);
247-
assertEquals(isInstanceOf(Cls)(true), false);
248-
assertEquals(isInstanceOf(Cls)(false), false);
249-
assertEquals(isInstanceOf(Cls)([]), false);
250-
assertEquals(isInstanceOf(Cls)({}), false);
251-
assertEquals(isInstanceOf(Cls)(function () {}), false);
252-
assertEquals(isInstanceOf(Cls)(null), false);
253-
assertEquals(isInstanceOf(Cls)(undefined), false);
280+
await testWithExamples(t, isInstanceOf(Cls));
281+
});
282+
await t.step("with Date", async (t) => {
283+
await testWithExamples(t, isInstanceOf(Date), { validExamples: ["date"] });
284+
});
285+
await t.step("with Promise", async (t) => {
286+
await testWithExamples(t, isInstanceOf(Promise), {
287+
validExamples: ["promise"],
288+
});
254289
});
255290
await t.step("returns proper type predicate", () => {
256291
class Cls {}
@@ -272,19 +307,21 @@ Deno.test("isInstanceOf<T>", async (t) => {
272307
});
273308

274309
Deno.test("isNull", async (t) => {
275-
await testWithExamples(t, isNull, ["null"]);
310+
await testWithExamples(t, isNull, { validExamples: ["null"] });
276311
});
277312

278313
Deno.test("isUndefined", async (t) => {
279-
await testWithExamples(t, isUndefined, ["undefined"]);
314+
await testWithExamples(t, isUndefined, { validExamples: ["undefined"] });
280315
});
281316

282317
Deno.test("isNullish", async (t) => {
283-
await testWithExamples(t, isNullish, ["null", "undefined"]);
318+
await testWithExamples(t, isNullish, {
319+
validExamples: ["null", "undefined"],
320+
});
284321
});
285322

286323
Deno.test("isSymbol", async (t) => {
287-
await testWithExamples(t, isSymbol, ["symbol"]);
324+
await testWithExamples(t, isSymbol, { validExamples: ["symbol"] });
288325
});
289326

290327
Deno.test("isOneOf<T>", async (t) => {
@@ -301,13 +338,11 @@ Deno.test("isOneOf<T>", async (t) => {
301338
assertEquals(isOneOf(preds)("a"), true);
302339
assertEquals(isOneOf(preds)(true), true);
303340
});
304-
await t.step("returns false on non of T", () => {
341+
await t.step("returns false on non of T", async (t) => {
305342
const preds = [isNumber, isString, isBoolean];
306-
assertEquals(isOneOf(preds)([]), false);
307-
assertEquals(isOneOf(preds)({}), false);
308-
assertEquals(isOneOf(preds)(function () {}), false);
309-
assertEquals(isOneOf(preds)(null), false);
310-
assertEquals(isOneOf(preds)(undefined), false);
343+
await testWithExamples(t, isOneOf(preds), {
344+
excludeExamples: ["number", "string", "boolean"],
345+
});
311346
});
312347
});
313348

0 commit comments

Comments
 (0)