BREAKING(testing, assert): move std/testing/asserts to std/assert (#3445)

This commit is contained in:
Asher Gomez 2023-07-13 17:04:30 +10:00 committed by GitHub
parent 2e647b0bea
commit 239e85ad6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
391 changed files with 3327 additions and 3020 deletions

View File

@ -94,9 +94,6 @@ jobs:
- name: Check Import paths in Docs
run: "deno task lint:doc-imports"
- name: Check non-test assertions
run: deno task lint:check-assertions
- name: Spell-check
uses: crate-ci/typos@master
with:

View File

@ -106,13 +106,6 @@ _For maintainers_:
To release a new version a tag in the form of `x.y.z` should be added.
### Internal Assertions
All internal non-test code, that is files that do not have `test` or `bench` in
the name, must use the assertion functions within `_utils/asserts.ts` and not
`testing/asserts.ts`. This is to create a separation of concerns between
internal and testing assertions.
### Types
Deno is moving away from non-native IO functions and interfaces in favor of the

View File

@ -1,40 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import {
createGraph,
Module,
} from "https://deno.land/x/deno_graph@0.37.1/mod.ts";
import { walk } from "../fs/walk.ts";
const ROOT = new URL("../", import.meta.url);
const EXTS = [".mjs", ".ts"];
const SKIP = [/(test|bench)/];
const BAD_IMPORT = new URL("../testing/asserts.ts", import.meta.url);
async function getFilePaths(): Promise<string[]> {
const paths: string[] = [];
for await (const { path } of walk(ROOT, { exts: EXTS, skip: SKIP })) {
paths.push("file://" + path);
}
return paths;
}
function hasBadImports({ dependencies }: Module): boolean {
return Object.values(dependencies!)
.some(({ code }) => code?.specifier?.includes(BAD_IMPORT.href));
}
async function getFilePathsWithBadImports(): Promise<string[]> {
const paths = await getFilePaths();
const { modules } = await createGraph(paths);
return modules.filter(hasBadImports)
.map(({ specifier }: Module) => specifier);
}
const paths = await getFilePathsWithBadImports();
if (paths.length > 0) {
console.error(
"Non-test code must use `_util/assert.ts` for assertions. Please fix:",
);
paths.forEach((path) => console.error("- " + path));
Deno.exit(1);
}

View File

@ -1,25 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/**
* All internal non-test code, that is files that do not have `test` or `bench` in the name, must use the assertion functions within `_utils/asserts.ts` and not `testing/asserts.ts`. This is to create a separation of concerns between internal and testing assertions.
*/
export class DenoStdInternalError extends Error {
constructor(message: string) {
super(message);
this.name = "DenoStdInternalError";
}
}
/** Make an assertion, if not `true`, then throw. */
export function assert(expr: unknown, msg = ""): asserts expr {
if (!expr) {
throw new DenoStdInternalError(msg);
}
}
/** Use this to assert unreachable code. */
export function unreachable(): never {
throw new DenoStdInternalError("unreachable");
}

View File

@ -1,43 +0,0 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, DenoStdInternalError, unreachable } from "./asserts.ts";
import { assertThrows } from "../testing/asserts.ts";
Deno.test({
name: "assert valid scenario",
fn() {
assert(true);
},
});
Deno.test({
name: "assert invalid scenario, no message",
fn() {
assertThrows(() => {
assert(false);
}, DenoStdInternalError);
},
});
Deno.test({
name: "assert invalid scenario, with message",
fn() {
assertThrows(
() => {
assert(false, "Oops! Should be true");
},
DenoStdInternalError,
"Oops! Should be true",
);
},
});
Deno.test("assert unreachable", function () {
let didThrow = false;
try {
unreachable();
} catch (e) {
assert(e instanceof DenoStdInternalError);
assert(e.message === "unreachable");
didThrow = true;
}
assert(didThrow);
});

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { diff, diffstr, DiffType } from "./_diff.ts";
import { assertEquals } from "../testing/asserts.ts";
import { diff, diffstr, DiffType } from "./diff.ts";
import { assertEquals } from "../assert/mod.ts";
Deno.test({
name: "empty",

View File

@ -42,7 +42,7 @@ export { type TarInfo, type TarMeta, type TarOptions };
import { MultiReader } from "../io/multi_reader.ts";
import { Buffer } from "../io/buffer.ts";
import { assert } from "../_util/asserts.ts";
import { assert } from "../assert/assert.ts";
import { recordSize } from "./_common.ts";
const ustar = "ustar\u000000";

View File

@ -9,7 +9,7 @@
* **to run this test**
* deno run --allow-read archive/tar_test.ts
*/
import { assert, assertEquals } from "../testing/asserts.ts";
import { assert, assertEquals } from "../assert/mod.ts";
import { resolve } from "../path/mod.ts";
import { Tar } from "./tar.ts";
import { Untar } from "./untar.ts";

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals, assertExists } from "../testing/asserts.ts";
import { assert, assertEquals, assertExists } from "../assert/mod.ts";
import { resolve } from "../path/mod.ts";
import { Tar, type TarMeta } from "./tar.ts";
import { TarEntry, type TarHeader, Untar } from "./untar.ts";

2
assert/_constants.ts Normal file
View File

@ -0,0 +1,2 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
export const CAN_NOT_DISPLAY = "[Cannot display]";

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { green, red, stripColor } from "../fmt/colors.ts";
import { assertEquals, assertThrows } from "./asserts.ts";
import { assertEquals, assertThrows } from "../assert/mod.ts";
import { format } from "./_format.ts";
Deno.test("assert diff formatting (strings)", () => {

9
assert/assert.ts Normal file
View File

@ -0,0 +1,9 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/** Make an assertion, error will be thrown if `expr` does not have truthy value. */
export function assert(expr: unknown, msg = ""): asserts expr {
if (!expr) {
throw new AssertionError(msg);
}
}

View File

@ -0,0 +1,41 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` and `expected` are almost equal numbers through
* a given tolerance. It can be used to take into account IEEE-754 double-precision
* floating-point representation limitations.
* If the values are not almost equal then throw.
*
* @example
* ```ts
* import { assertAlmostEquals, assertThrows } from "https://deno.land/std@$STD_VERSION/assert/mod.ts";
*
* assertAlmostEquals(0.1, 0.2);
*
* // Using a custom tolerance value
* assertAlmostEquals(0.1 + 0.2, 0.3, 1e-16);
* assertThrows(() => assertAlmostEquals(0.1 + 0.2, 0.3, 1e-17));
* ```
*/
export function assertAlmostEquals(
actual: number,
expected: number,
tolerance = 1e-7,
msg?: string,
) {
if (Object.is(actual, expected)) {
return;
}
const delta = Math.abs(expected - actual);
if (delta <= tolerance) {
return;
}
const msgSuffix = msg ? `: ${msg}` : ".";
const f = (n: number) => Number.isInteger(n) ? n : n.toExponential();
throw new AssertionError(
`Expected actual: "${f(actual)}" to be close to "${f(expected)}": \
delta "${f(delta)}" is greater than "${f(tolerance)}"${msgSuffix}`,
);
}

View File

@ -0,0 +1,43 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertAlmostEquals, AssertionError, assertThrows } from "./mod.ts";
Deno.test("assert almost equals number", () => {
//Default precision
assertAlmostEquals(-0, +0);
assertAlmostEquals(Math.PI, Math.PI);
assertAlmostEquals(0.1 + 0.2, 0.3);
assertAlmostEquals(NaN, NaN);
assertAlmostEquals(Number.NaN, Number.NaN);
assertThrows(() => assertAlmostEquals(1, 2));
assertThrows(() => assertAlmostEquals(1, 1.1));
//Higher precision
assertAlmostEquals(0.1 + 0.2, 0.3, 1e-16);
assertThrows(
() => assertAlmostEquals(0.1 + 0.2, 0.3, 1e-17),
AssertionError,
`Expected actual: "${
(
0.1 + 0.2
).toExponential()
}" to be close to "${(0.3).toExponential()}"`,
);
//Special cases
assertAlmostEquals(Infinity, Infinity);
assertThrows(
() => assertAlmostEquals(0, Infinity),
AssertionError,
'Expected actual: "0" to be close to "Infinity"',
);
assertThrows(
() => assertAlmostEquals(-Infinity, +Infinity),
AssertionError,
'Expected actual: "-Infinity" to be close to "Infinity"',
);
assertThrows(
() => assertAlmostEquals(Infinity, NaN),
AssertionError,
'Expected actual: "Infinity" to be close to "NaN"',
);
});

View File

@ -0,0 +1,46 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { equal } from "./equal.ts";
import { format } from "./_format.ts";
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` includes the `expected` values.
* If not then an error will be thrown.
*
* Type parameter can be specified to ensure values under comparison have the same type.
*
* @example
* ```ts
* import { assertArrayIncludes } from "https://deno.land/std@$STD_VERSION/assert/assert_array_includes.ts";
*
* assertArrayIncludes<number>([1, 2], [2])
* ```
*/
export function assertArrayIncludes<T>(
actual: ArrayLike<T>,
expected: ArrayLike<T>,
msg?: string,
) {
const missing: unknown[] = [];
for (let i = 0; i < expected.length; i++) {
let found = false;
for (let j = 0; j < actual.length; j++) {
if (equal(expected[i], actual[j])) {
found = true;
break;
}
}
if (!found) {
missing.push(expected[i]);
}
}
if (missing.length === 0) {
return;
}
const msgSuffix = msg ? `: ${msg}` : ".";
msg = `Expected actual: "${format(actual)}" to include: "${
format(expected)
}"${msgSuffix}\nmissing: ${format(missing)}`;
throw new AssertionError(msg);
}

View File

@ -0,0 +1,34 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertArrayIncludes, AssertionError, assertThrows } from "./mod.ts";
Deno.test("ArrayContains", function () {
const fixture = ["deno", "iz", "luv"];
const fixtureObject = [{ deno: "luv" }, { deno: "Js" }];
assertArrayIncludes(fixture, ["deno"]);
assertArrayIncludes(fixtureObject, [{ deno: "luv" }]);
assertArrayIncludes(
Uint8Array.from([1, 2, 3, 4]),
Uint8Array.from([1, 2, 3]),
);
assertThrows(
() => assertArrayIncludes(fixtureObject, [{ deno: "node" }]),
AssertionError,
`Expected actual: "[
{
deno: "luv",
},
{
deno: "Js",
},
]" to include: "[
{
deno: "node",
},
]".
missing: [
{
deno: "node",
},
]`,
);
});

46
assert/assert_equals.ts Normal file
View File

@ -0,0 +1,46 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { equal } from "./equal.ts";
import { format } from "./_format.ts";
import { AssertionError } from "./assertion_error.ts";
import { red } from "../fmt/colors.ts";
import { buildMessage, diff, diffstr } from "../_util/diff.ts";
import { CAN_NOT_DISPLAY } from "./_constants.ts";
/**
* Make an assertion that `actual` and `expected` are equal, deeply. If not
* deeply equal, then throw.
*
* Type parameter can be specified to ensure values under comparison have the same type.
*
* @example
* ```ts
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* Deno.test("example", function (): void {
* assertEquals("world", "world");
* assertEquals({ hello: "world" }, { hello: "world" });
* });
* ```
*/
export function assertEquals<T>(actual: T, expected: T, msg?: string) {
if (equal(actual, expected)) {
return;
}
const msgSuffix = msg ? `: ${msg}` : ".";
let message = `Values are not equal${msgSuffix}`;
const actualString = format(actual);
const expectedString = format(expected);
try {
const stringDiff = (typeof actual === "string") &&
(typeof expected === "string");
const diffResult = stringDiff
? diffstr(actual as string, expected as string)
: diff(actualString.split("\n"), expectedString.split("\n"));
const diffMsg = buildMessage(diffResult, { stringDiff }).join("\n");
message = `${message}\n${diffMsg}`;
} catch {
message = `${message}\n${red(CAN_NOT_DISPLAY)} + \n\n`;
}
throw new AssertionError(message);
}

View File

@ -0,0 +1,212 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, AssertionError, assertThrows } from "./mod.ts";
import { bold, gray, green, red, stripColor, yellow } from "../fmt/colors.ts";
const createHeader = (): string[] => [
"",
"",
` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${
green(
bold("Expected"),
)
}`,
"",
"",
];
const added: (s: string) => string = (s: string): string =>
green(bold(stripColor(s)));
const removed: (s: string) => string = (s: string): string =>
red(bold(stripColor(s)));
Deno.test({
name: "pass case",
fn() {
assertEquals({ a: 10 }, { a: 10 });
assertEquals(true, true);
assertEquals(10, 10);
assertEquals("abc", "abc");
assertEquals({ a: 10, b: { c: "1" } }, { a: 10, b: { c: "1" } });
assertEquals(new Date("invalid"), new Date("invalid"));
},
});
Deno.test({
name: "failed with number",
fn() {
assertThrows(
() => assertEquals(1, 2),
AssertionError,
[
"Values are not equal.",
...createHeader(),
removed(`- ${yellow("1")}`),
added(`+ ${yellow("2")}`),
"",
].join("\n"),
);
},
});
Deno.test({
name: "failed with number vs string",
fn() {
assertThrows(
() => assertEquals<unknown>(1, "1"),
AssertionError,
[
"Values are not equal.",
...createHeader(),
removed(`- ${yellow("1")}`),
added(`+ "1"`),
].join("\n"),
);
},
});
Deno.test({
name: "failed with array",
fn() {
assertThrows(
() => assertEquals([1, "2", 3], ["1", "2", 3]),
AssertionError,
`
[
- 1,
+ "1",
"2",
3,
]`,
);
},
});
Deno.test({
name: "failed with object",
fn() {
assertThrows(
() => assertEquals({ a: 1, b: "2", c: 3 }, { a: 1, b: 2, c: [3] }),
AssertionError,
`
{
a: 1,
+ b: 2,
+ c: [
+ 3,
+ ],
- b: "2",
- c: 3,
}`,
);
},
});
Deno.test({
name: "failed with date",
fn() {
assertThrows(
() =>
assertEquals(
new Date(2019, 0, 3, 4, 20, 1, 10),
new Date(2019, 0, 3, 4, 20, 1, 20),
),
AssertionError,
[
"Values are not equal.",
...createHeader(),
removed(`- ${new Date(2019, 0, 3, 4, 20, 1, 10).toISOString()}`),
added(`+ ${new Date(2019, 0, 3, 4, 20, 1, 20).toISOString()}`),
"",
].join("\n"),
);
assertThrows(
() =>
assertEquals(new Date("invalid"), new Date(2019, 0, 3, 4, 20, 1, 20)),
AssertionError,
[
"Values are not equal.",
...createHeader(),
removed(`- ${new Date("invalid")}`),
added(`+ ${new Date(2019, 0, 3, 4, 20, 1, 20).toISOString()}`),
"",
].join("\n"),
);
},
});
Deno.test({
name: "failed with custom msg",
fn() {
assertThrows(
() => assertEquals(1, 2, "CUSTOM MESSAGE"),
AssertionError,
[
"Values are not equal: CUSTOM MESSAGE",
...createHeader(),
removed(`- ${yellow("1")}`),
added(`+ ${yellow("2")}`),
"",
].join("\n"),
);
},
});
Deno.test(
"assertEquals compares objects structurally if one object's constructor is undefined and the other is Object",
() => {
const a = Object.create(null);
a.prop = "test";
const b = {
prop: "test",
};
assertEquals(a, b);
assertEquals(b, a);
},
);
Deno.test("assertEquals diff for differently ordered objects", () => {
assertThrows(
() => {
assertEquals(
{
aaaaaaaaaaaaaaaaaaaaaaaa: 0,
bbbbbbbbbbbbbbbbbbbbbbbb: 0,
ccccccccccccccccccccccc: 0,
},
{
ccccccccccccccccccccccc: 1,
aaaaaaaaaaaaaaaaaaaaaaaa: 0,
bbbbbbbbbbbbbbbbbbbbbbbb: 0,
},
);
},
AssertionError,
`
{
aaaaaaaaaaaaaaaaaaaaaaaa: 0,
bbbbbbbbbbbbbbbbbbbbbbbb: 0,
- ccccccccccccccccccccccc: 0,
+ ccccccccccccccccccccccc: 1,
}`,
);
});
Deno.test("assertEquals same Set with object keys", () => {
const data = [
{
id: "_1p7ZED73OF98VbT1SzSkjn",
type: { id: "_ETGENUS" },
name: "Thuja",
friendlyId: "g-thuja",
},
{
id: "_567qzghxZmeQ9pw3q09bd3",
type: { id: "_ETGENUS" },
name: "Pinus",
friendlyId: "g-pinus",
},
];
assertEquals(data, data);
assertEquals(new Set(data), new Set(data));
});

18
assert/assert_exists.ts Normal file
View File

@ -0,0 +1,18 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that actual is not null or undefined.
* If not then throw.
*/
export function assertExists<T>(
actual: T,
msg?: string,
): asserts actual is NonNullable<T> {
if (actual === undefined || actual === null) {
const msgSuffix = msg ? `: ${msg}` : ".";
msg =
`Expected actual: "${actual}" to not be null or undefined${msgSuffix}`;
throw new AssertionError(msg);
}
}

View File

@ -0,0 +1,35 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals, assertExists, AssertionError } from "./mod.ts";
Deno.test("AssertExists", function () {
assertExists("Denosaurus");
assertExists(false);
assertExists(0);
assertExists("");
assertExists(-0);
assertExists(0);
assertExists(NaN);
const value = new URLSearchParams({ value: "test" }).get("value");
assertExists(value);
assertEquals(value.length, 4);
let didThrow;
try {
assertExists(undefined);
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
didThrow = false;
try {
assertExists(null);
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
});

10
assert/assert_false.ts Normal file
View File

@ -0,0 +1,10 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/** Make an assertion, error will be thrown if `expr` have truthy value. */
type Falsy = false | 0 | 0n | "" | null | undefined;
export function assertFalse(expr: unknown, msg = ""): asserts expr is Falsy {
if (expr) {
throw new AssertionError(msg);
}
}

View File

@ -0,0 +1,18 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertFalse, assertThrows } from "./mod.ts";
Deno.test("Assert False with falsy values", () => {
assertFalse(false);
assertFalse(0);
assertFalse("");
assertFalse(null);
assertFalse(undefined);
});
Deno.test("Assert False with truthy values", () => {
assertThrows(() => assertFalse(true));
assertThrows(() => assertFalse(1));
assertThrows(() => assertFalse("a"));
assertThrows(() => assertFalse({}));
assertThrows(() => assertFalse([]));
});

View File

@ -0,0 +1,47 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
// deno-lint-ignore no-explicit-any
type AnyConstructor = new (...args: any[]) => any;
type GetConstructorType<T extends AnyConstructor> = T extends // deno-lint-ignore no-explicit-any
new (...args: any) => infer C ? C
: never;
/**
* Make an assertion that `obj` is an instance of `type`.
* If not then throw.
*/
export function assertInstanceOf<T extends AnyConstructor>(
actual: unknown,
expectedType: T,
msg = "",
): asserts actual is GetConstructorType<T> {
if (actual instanceof expectedType) return;
const msgSuffix = msg ? `: ${msg}` : ".";
const expectedTypeStr = expectedType.name;
let actualTypeStr = "";
if (actual === null) {
actualTypeStr = "null";
} else if (actual === undefined) {
actualTypeStr = "undefined";
} else if (typeof actual === "object") {
actualTypeStr = actual.constructor?.name ?? "Object";
} else {
actualTypeStr = typeof actual;
}
if (expectedTypeStr == actualTypeStr) {
msg =
`Expected object to be an instance of "${expectedTypeStr}"${msgSuffix}`;
} else if (actualTypeStr == "function") {
msg =
`Expected object to be an instance of "${expectedTypeStr}" but was not an instanced object${msgSuffix}`;
} else {
msg =
`Expected object to be an instance of "${expectedTypeStr}" but was "${actualTypeStr}"${msgSuffix}`;
}
throw new AssertionError(msg);
}

View File

@ -0,0 +1,116 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertInstanceOf, AssertionError, assertThrows } from "./mod.ts";
Deno.test({
name: "assertInstanceOf",
fn() {
class TestClass1 {}
class TestClass2 {}
class TestClass3 {}
// Regular types
assertInstanceOf(new Date(), Date);
assertInstanceOf(new Number(), Number);
assertInstanceOf(Promise.resolve(), Promise);
assertInstanceOf(new TestClass1(), TestClass1);
// Throwing cases
assertThrows(
() => assertInstanceOf(new Date(), RegExp),
AssertionError,
`Expected object to be an instance of "RegExp" but was "Date".`,
);
assertThrows(
() => assertInstanceOf(5, Date),
AssertionError,
`Expected object to be an instance of "Date" but was "number".`,
);
assertThrows(
() => assertInstanceOf(new TestClass1(), TestClass2),
AssertionError,
`Expected object to be an instance of "TestClass2" but was "TestClass1".`,
);
// Custom message
assertThrows(
() => assertInstanceOf(new Date(), RegExp, "Custom message"),
AssertionError,
"Custom message",
);
// Edge cases
assertThrows(
() => assertInstanceOf(5, Number),
AssertionError,
`Expected object to be an instance of "Number" but was "number".`,
);
let TestClassWithSameName: new () => unknown;
{
class TestClass3 {}
TestClassWithSameName = TestClass3;
}
assertThrows(
() => assertInstanceOf(new TestClassWithSameName(), TestClass3),
AssertionError,
`Expected object to be an instance of "TestClass3".`,
);
assertThrows(
() => assertInstanceOf(TestClass1, TestClass1),
AssertionError,
`Expected object to be an instance of "TestClass1" but was not an instanced object.`,
);
assertThrows(
() => assertInstanceOf(() => {}, TestClass1),
AssertionError,
`Expected object to be an instance of "TestClass1" but was not an instanced object.`,
);
assertThrows(
() => assertInstanceOf(null, TestClass1),
AssertionError,
`Expected object to be an instance of "TestClass1" but was "null".`,
);
assertThrows(
() => assertInstanceOf(undefined, TestClass1),
AssertionError,
`Expected object to be an instance of "TestClass1" but was "undefined".`,
);
assertThrows(
() => assertInstanceOf({}, TestClass1),
AssertionError,
`Expected object to be an instance of "TestClass1" but was "Object".`,
);
assertThrows(
() => assertInstanceOf(Object.create(null), TestClass1),
AssertionError,
`Expected object to be an instance of "TestClass1" but was "Object".`,
);
// Test TypeScript types functionality, wrapped in a function that never runs
// deno-lint-ignore no-unused-vars
function typeScriptTests() {
class ClassWithProperty {
property = "prop1";
}
const testInstance = new ClassWithProperty() as unknown;
// @ts-expect-error: `testInstance` is `unknown` so setting its property before `assertInstanceOf` should give a type error.
testInstance.property = "prop2";
assertInstanceOf(testInstance, ClassWithProperty);
// Now `testInstance` should be of type `ClassWithProperty`
testInstance.property = "prop3";
let x = 5 as unknown;
// @ts-expect-error: `x` is `unknown` so adding to it shouldn't work
x += 5;
assertInstanceOf(x, Number);
// @ts-expect-error: `x` is now `Number` rather than `number`, so this should still give a type error.
x += 5;
}
},
});

39
assert/assert_is_error.ts Normal file
View File

@ -0,0 +1,39 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
import { stripColor } from "../fmt/colors.ts";
/**
* Make an assertion that `error` is an `Error`.
* If not then an error will be thrown.
* An error class and a string that should be included in the
* error message can also be asserted.
*/
export function assertIsError<E extends Error = Error>(
error: unknown,
// deno-lint-ignore no-explicit-any
ErrorClass?: new (...args: any[]) => E,
msgIncludes?: string,
msg?: string,
): asserts error is E {
const msgSuffix = msg ? `: ${msg}` : ".";
if (error instanceof Error === false) {
throw new AssertionError(
`Expected "error" to be an Error object${msgSuffix}}`,
);
}
if (ErrorClass && !(error instanceof ErrorClass)) {
msg = `Expected error to be instance of "${ErrorClass.name}", but was "${
typeof error === "object" ? error?.constructor?.name : "[not an object]"
}"${msgSuffix}`;
throw new AssertionError(msg);
}
if (
msgIncludes && (!(error instanceof Error) ||
!stripColor(error.message).includes(stripColor(msgIncludes)))
) {
msg = `Expected error message to include "${msgIncludes}", but got "${
error instanceof Error ? error.message : "[not an Error]"
}"${msgSuffix}`;
throw new AssertionError(msg);
}
}

View File

@ -0,0 +1,37 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError, assertIsError, assertThrows } from "./mod.ts";
Deno.test("Assert Is Error Non-Error Fail", () => {
assertThrows(
() => assertIsError("Panic!", undefined, "Panic!"),
AssertionError,
`Expected "error" to be an Error object.`,
);
assertThrows(
() => assertIsError(null),
AssertionError,
`Expected "error" to be an Error object.`,
);
assertThrows(
() => assertIsError(undefined),
AssertionError,
`Expected "error" to be an Error object.`,
);
});
Deno.test("Assert Is Error Parent Error", () => {
assertIsError(new AssertionError("Fail!"), Error, "Fail!");
});
Deno.test("Assert Is Error with custom Error", () => {
class CustomError extends Error {}
class AnotherCustomError extends Error {}
assertIsError(new CustomError("failed"), CustomError, "fail");
assertThrows(
() => assertIsError(new AnotherCustomError("failed"), CustomError, "fail"),
AssertionError,
'Expected error to be instance of "CustomError", but was "AnotherCustomError".',
);
});

18
assert/assert_match.ts Normal file
View File

@ -0,0 +1,18 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` match RegExp `expected`. If not
* then throw.
*/
export function assertMatch(
actual: string,
expected: RegExp,
msg?: string,
) {
if (!expected.test(actual)) {
const msgSuffix = msg ? `: ${msg}` : ".";
msg = `Expected actual: "${actual}" to match: "${expected}"${msgSuffix}`;
throw new AssertionError(msg);
}
}

View File

@ -0,0 +1,21 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, AssertionError, assertMatch } from "./mod.ts";
Deno.test("AssertStringMatching", function () {
assertMatch("foobar@deno.com", RegExp(/[a-zA-Z]+@[a-zA-Z]+.com/));
});
Deno.test("AssertStringMatchingThrows", function () {
let didThrow = false;
try {
assertMatch("Denosaurus from Jurassic", RegExp(/Raptor/));
} catch (e) {
assert(e instanceof AssertionError);
assert(
e.message ===
`Expected actual: "Denosaurus from Jurassic" to match: "/Raptor/".`,
);
didThrow = true;
}
assert(didThrow);
});

View File

@ -0,0 +1,38 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { equal } from "./equal.ts";
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` and `expected` are not equal, deeply.
* If not then throw.
*
* Type parameter can be specified to ensure values under comparison have the same type.
*
* @example
* ```ts
* import { assertNotEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_not_equals.ts";
*
* assertNotEquals<number>(1, 2)
* ```
*/
export function assertNotEquals<T>(actual: T, expected: T, msg?: string) {
if (!equal(actual, expected)) {
return;
}
let actualString: string;
let expectedString: string;
try {
actualString = String(actual);
} catch {
actualString = "[Cannot display]";
}
try {
expectedString = String(expected);
} catch {
expectedString = "[Cannot display]";
}
const msgSuffix = msg ? `: ${msg}` : ".";
throw new AssertionError(
`Expected actual: ${actualString} not to be: ${expectedString}${msgSuffix}`,
);
}

View File

@ -0,0 +1,28 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import {
assert,
assertEquals,
AssertionError,
assertNotEquals,
} from "./mod.ts";
Deno.test("NotEquals", function () {
const a = { foo: "bar" };
const b = { bar: "foo" };
assertNotEquals<unknown>(a, b);
assertNotEquals("Denosaurus", "Tyrannosaurus");
assertNotEquals(
new Date(2019, 0, 3, 4, 20, 1, 10),
new Date(2019, 0, 3, 4, 20, 1, 20),
);
assertNotEquals(new Date("invalid"), new Date(2019, 0, 3, 4, 20, 1, 20));
let didThrow;
try {
assertNotEquals("Raptor", "Raptor");
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
});

View File

@ -0,0 +1,18 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertFalse } from "./assert_false.ts";
/**
* Make an assertion that `obj` is not an instance of `type`.
* If so, then throw.
*/
export function assertNotInstanceOf<A, T>(
actual: A,
// deno-lint-ignore no-explicit-any
unexpectedType: new (...args: any[]) => T,
msg?: string,
): asserts actual is Exclude<A, T> {
const msgSuffix = msg ? `: ${msg}` : ".";
msg =
`Expected object to not be an instance of "${typeof unexpectedType}"${msgSuffix}`;
assertFalse(actual instanceof unexpectedType, msg);
}

View File

@ -0,0 +1,11 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertNotInstanceOf } from "./mod.ts";
Deno.test({
name: "assertNotInstanceOf",
fn() {
assertNotInstanceOf("not a number", Number);
assertNotInstanceOf(42, String);
assertNotInstanceOf(new URL("http://example.com"), Boolean);
},
});

View File

@ -0,0 +1,19 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that `actual` not match RegExp `expected`. If match
* then throw.
*/
export function assertNotMatch(
actual: string,
expected: RegExp,
msg?: string,
) {
if (expected.test(actual)) {
const msgSuffix = msg ? `: ${msg}` : ".";
msg =
`Expected actual: "${actual}" to not match: "${expected}"${msgSuffix}`;
throw new AssertionError(msg);
}
}

View File

@ -0,0 +1,21 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, AssertionError, assertNotMatch } from "./mod.ts";
Deno.test("AssertStringNotMatching", function () {
assertNotMatch("foobar.deno.com", RegExp(/[a-zA-Z]+@[a-zA-Z]+.com/));
});
Deno.test("AssertStringNotMatchingThrows", function () {
let didThrow = false;
try {
assertNotMatch("Denosaurus from Jurassic", RegExp(/from/));
} catch (e) {
assert(e instanceof AssertionError);
assert(
e.message ===
`Expected actual: "Denosaurus from Jurassic" to not match: "/from/".`,
);
didThrow = true;
}
assert(didThrow);
});

View File

@ -0,0 +1,30 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
import { format } from "./_format.ts";
/**
* Make an assertion that `actual` and `expected` are not strictly equal.
* If the values are strictly equal then throw.
*
* ```ts
* import { assertNotStrictEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_not_strict_equals.ts";
*
* assertNotStrictEquals(1, 1)
* ```
*/
export function assertNotStrictEquals<T>(
actual: T,
expected: T,
msg?: string,
) {
if (!Object.is(actual, expected)) {
return;
}
const msgSuffix = msg ? `: ${msg}` : ".";
throw new AssertionError(
`Expected "actual" to be strictly unequal to: ${
format(actual)
}${msgSuffix}\n`,
);
}

View File

@ -0,0 +1,29 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError, assertNotStrictEquals, assertThrows } from "./mod.ts";
Deno.test({
name: "strictly unequal pass case",
fn() {
assertNotStrictEquals(true, false);
assertNotStrictEquals(10, 11);
assertNotStrictEquals("abc", "xyz");
assertNotStrictEquals<unknown>(1, "1");
assertNotStrictEquals(-0, +0);
const xs = [1, false, "foo"];
const ys = [1, true, "bar"];
assertNotStrictEquals(xs, ys);
const x = { a: 1 };
const y = { a: 2 };
assertNotStrictEquals(x, y);
},
});
Deno.test({
name: "strictly unequal fail case",
fn() {
assertThrows(() => assertNotStrictEquals(1, 1), AssertionError);
assertThrows(() => assertNotStrictEquals(NaN, NaN), AssertionError);
},
});

View File

@ -0,0 +1,92 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "./assert_equals.ts";
/**
* Make an assertion that `actual` object is a subset of `expected` object, deeply.
* If not, then throw.
*/
export function assertObjectMatch(
// deno-lint-ignore no-explicit-any
actual: Record<PropertyKey, any>,
expected: Record<PropertyKey, unknown>,
msg?: string,
) {
type loose = Record<PropertyKey, unknown>;
function filter(a: loose, b: loose) {
const seen = new WeakMap();
return fn(a, b);
function fn(a: loose, b: loose): loose {
// Prevent infinite loop with circular references with same filter
if ((seen.has(a)) && (seen.get(a) === b)) {
return a;
}
try {
seen.set(a, b);
} catch (err) {
if (err instanceof TypeError) {
throw new TypeError(
`Cannot assertObjectMatch ${
a === null ? null : `type ${typeof a}`
}`,
);
} else throw err;
}
// Filter keys and symbols which are present in both actual and expected
const filtered = {} as loose;
const entries = [
...Object.getOwnPropertyNames(a),
...Object.getOwnPropertySymbols(a),
]
.filter((key) => key in b)
.map((key) => [key, a[key as string]]) as Array<[string, unknown]>;
for (const [key, value] of entries) {
// On array references, build a filtered array and filter nested objects inside
if (Array.isArray(value)) {
const subset = (b as loose)[key];
if (Array.isArray(subset)) {
filtered[key] = fn({ ...value }, { ...subset });
continue;
}
} // On regexp references, keep value as it to avoid loosing pattern and flags
else if (value instanceof RegExp) {
filtered[key] = value;
continue;
} // On nested objects references, build a filtered object recursively
else if (typeof value === "object" && value !== null) {
const subset = (b as loose)[key];
if ((typeof subset === "object") && (subset)) {
// When both operands are maps, build a filtered map with common keys and filter nested objects inside
if ((value instanceof Map) && (subset instanceof Map)) {
filtered[key] = new Map(
[...value].filter(([k]) => subset.has(k)).map((
[k, v],
) => [k, typeof v === "object" ? fn(v, subset.get(k)) : v]),
);
continue;
}
// When both operands are set, build a filtered set with common values
if ((value instanceof Set) && (subset instanceof Set)) {
filtered[key] = new Set([...value].filter((v) => subset.has(v)));
continue;
}
filtered[key] = fn(value as loose, subset as loose);
continue;
}
}
filtered[key] = value;
}
return filtered;
}
}
return assertEquals(
// get the intersection of "actual" and "expected"
// side effect: all the instances' constructor field is "Object" now.
filter(actual, expected),
// set (nested) instances' constructor field to be "Object" without changing expected value.
// see https://github.com/denoland/deno_std/pull/1419
filter(expected, expected),
msg,
);
}

View File

@ -0,0 +1,339 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import {
assert,
assertEquals,
AssertionError,
assertObjectMatch,
assertThrows,
} from "./mod.ts";
Deno.test("AssertObjectMatching", function () {
const sym = Symbol("foo");
const a = { foo: true, bar: false };
const b = { ...a, baz: a };
const c = { ...b, qux: b };
const d = { corge: c, grault: c };
const e = { foo: true } as { [key: string]: unknown };
e.bar = e;
const f = { [sym]: true, bar: false };
interface r {
foo: boolean;
bar: boolean;
}
const g: r = { foo: true, bar: false };
const h = { foo: [1, 2, 3], bar: true };
const i = { foo: [a, e], bar: true };
const j = { foo: [[1, 2, 3]], bar: true };
const k = { foo: [[1, [2, [3]]]], bar: true };
const l = { foo: [[1, [2, [a, e, j, k]]]], bar: true };
const m = { foo: /abc+/i, bar: [/abc/g, /abc/m] };
const n = {
foo: new Set(["foo", "bar"]),
bar: new Map([
["foo", 1],
["bar", 2],
]),
baz: new Map([
["a", a],
["b", b],
]),
};
// Simple subset
assertObjectMatch(a, {
foo: true,
});
// Subset with another subset
assertObjectMatch(b, {
foo: true,
baz: { bar: false },
});
// Subset with multiple subsets
assertObjectMatch(c, {
foo: true,
baz: { bar: false },
qux: {
baz: { foo: true },
},
});
// Subset with same object reference as subset
assertObjectMatch(d, {
corge: {
foo: true,
qux: { bar: false },
},
grault: {
bar: false,
qux: { foo: true },
},
});
// Subset with circular reference
assertObjectMatch(e, {
foo: true,
bar: {
bar: {
bar: {
foo: true,
},
},
},
});
// Subset with interface
assertObjectMatch(g, { bar: false });
// Subset with same symbol
assertObjectMatch(f, {
[sym]: true,
});
// Subset with array inside
assertObjectMatch(h, { foo: [] });
assertObjectMatch(h, { foo: [1, 2] });
assertObjectMatch(h, { foo: [1, 2, 3] });
assertObjectMatch(i, { foo: [{ bar: false }] });
assertObjectMatch(i, {
foo: [{ bar: false }, { bar: { bar: { bar: { foo: true } } } }],
});
// Subset with nested array inside
assertObjectMatch(j, { foo: [[1, 2, 3]] });
assertObjectMatch(k, { foo: [[1, [2, [3]]]] });
assertObjectMatch(l, { foo: [[1, [2, [a, e, j, k]]]] });
// Regexp
assertObjectMatch(m, { foo: /abc+/i });
assertObjectMatch(m, { bar: [/abc/g, /abc/m] });
//Built-in data structures
assertObjectMatch(n, { foo: new Set(["foo"]) });
assertObjectMatch(n, { bar: new Map([["bar", 2]]) });
assertObjectMatch(n, { baz: new Map([["b", b]]) });
assertObjectMatch(n, { baz: new Map([["b", { foo: true }]]) });
// Missing key
{
let didThrow;
try {
assertObjectMatch(
{
foo: true,
},
{
foo: true,
bar: false,
},
);
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
}
// Simple subset
{
let didThrow;
try {
assertObjectMatch(a, {
foo: false,
});
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
}
// Subset with another subset
{
let didThrow;
try {
assertObjectMatch(b, {
foo: true,
baz: { bar: true },
});
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
}
// Subset with multiple subsets
{
let didThrow;
try {
assertObjectMatch(c, {
foo: true,
baz: { bar: false },
qux: {
baz: { foo: false },
},
});
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
}
// Subset with same object reference as subset
{
let didThrow;
try {
assertObjectMatch(d, {
corge: {
foo: true,
qux: { bar: true },
},
grault: {
bar: false,
qux: { foo: false },
},
});
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
}
// Subset with circular reference
{
let didThrow;
try {
assertObjectMatch(e, {
foo: true,
bar: {
bar: {
bar: {
foo: false,
},
},
},
});
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
}
// Subset with symbol key but with string key subset
{
let didThrow;
try {
assertObjectMatch(f, {
foo: true,
});
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
}
// Subset with array inside but doesn't match key subset
{
let didThrow;
try {
assertObjectMatch(i, {
foo: [1, 2, 3, 4],
});
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
}
{
let didThrow;
try {
assertObjectMatch(i, {
foo: [{ bar: true }, { foo: false }],
});
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
}
// actual/expected value as instance of class
{
class A {
a: number;
constructor(a: number) {
this.a = a;
}
}
assertObjectMatch({ test: new A(1) }, { test: { a: 1 } });
assertObjectMatch({ test: { a: 1 } }, { test: { a: 1 } });
assertObjectMatch({ test: { a: 1 } }, { test: new A(1) });
assertObjectMatch({ test: new A(1) }, { test: new A(1) });
}
{
// actual/expected contains same instance of Map/TypedArray/etc
const body = new Uint8Array([0, 1, 2]);
assertObjectMatch({ body, foo: "foo" }, { body });
}
{
// match subsets of arrays
assertObjectMatch(
{ positions: [[1, 2, 3, 4]] },
{
positions: [[1, 2, 3]],
},
);
}
//Regexp
assertThrows(() => assertObjectMatch(m, { foo: /abc+/ }), AssertionError);
assertThrows(() => assertObjectMatch(m, { foo: /abc*/i }), AssertionError);
assertThrows(
() => assertObjectMatch(m, { bar: [/abc/m, /abc/g] }),
AssertionError,
);
//Built-in data structures
assertThrows(
() => assertObjectMatch(n, { foo: new Set(["baz"]) }),
AssertionError,
);
assertThrows(
() => assertObjectMatch(n, { bar: new Map([["bar", 3]]) }),
AssertionError,
);
assertThrows(
() => assertObjectMatch(n, { baz: new Map([["a", { baz: true }]]) }),
AssertionError,
);
// null in the first argument throws an assertion error, rather than a TypeError: Invalid value used as weak map key
assertThrows(
() => assertObjectMatch({ foo: null }, { foo: { bar: 42 } }),
AssertionError,
);
assertObjectMatch({ foo: null, bar: null }, { foo: null });
assertObjectMatch({ foo: undefined, bar: null }, { foo: undefined });
assertThrows(
() => assertObjectMatch({ foo: undefined, bar: null }, { foo: null }),
AssertionError,
);
// Non mapable primative types should throw a readable type error
assertThrows(
// @ts-expect-error Argument of type 'null' is not assignable to parameter of type 'Record<PropertyKey, any>'
() => assertObjectMatch(null, { foo: 42 }),
TypeError,
"assertObjectMatch",
);
// @ts-expect-error Argument of type 'null' is not assignable to parameter of type 'Record<PropertyKey, any>'
assertThrows(() => assertObjectMatch(null, { foo: 42 }), TypeError, "null"); // since typeof null is "object", want to make sure user knows the bad value is "null"
assertThrows(
// @ts-expect-error Argument of type 'undefined' is not assignable to parameter of type 'Record<PropertyKey, any>'
() => assertObjectMatch(undefined, { foo: 42 }),
TypeError,
"assertObjectMatch",
);
// @ts-expect-error Argument of type 'number' is not assignable to parameter of type 'Record<PropertyKey, any>'
assertThrows(() => assertObjectMatch(21, 42), TypeError, "assertObjectMatch");
assertThrows(
// @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'Record<PropertyKey, any>'
() => assertObjectMatch("string", "string"),
TypeError,
"assertObjectMatch",
);
});

144
assert/assert_rejects.ts Normal file
View File

@ -0,0 +1,144 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
import { assertIsError } from "./assert_is_error.ts";
/**
* Executes a function which returns a promise, expecting it to reject.
*
* @example
* ```ts
* import { assertRejects } from "https://deno.land/std@$STD_VERSION/assert/assert_rejects.ts";
*
* Deno.test("doesThrow", async function () {
* await assertRejects(
* async () => {
* throw new TypeError("hello world!");
* },
* );
* await assertRejects(
* async () => {
* return Promise.reject(new Error());
* },
* );
* });
*
* // This test will not pass.
* Deno.test("fails", async function () {
* await assertRejects(
* async () => {
* console.log("Hello world");
* },
* );
* });
* ```
*/
export function assertRejects(
fn: () => PromiseLike<unknown>,
msg?: string,
): Promise<unknown>;
/**
* Executes a function which returns a promise, expecting it to reject.
* If it does not, then it throws. An error class and a string that should be
* included in the error message can also be asserted.
*
* @example
* ```ts
* import { assertRejects } from "https://deno.land/std@$STD_VERSION/assert/assert_rejects.ts";
*
* Deno.test("doesThrow", async function () {
* await assertRejects(async () => {
* throw new TypeError("hello world!");
* }, TypeError);
* await assertRejects(
* async () => {
* throw new TypeError("hello world!");
* },
* TypeError,
* "hello",
* );
* });
*
* // This test will not pass.
* Deno.test("fails", async function () {
* await assertRejects(
* async () => {
* console.log("Hello world");
* },
* );
* });
* ```
*/
export function assertRejects<E extends Error = Error>(
fn: () => PromiseLike<unknown>,
// deno-lint-ignore no-explicit-any
ErrorClass: new (...args: any[]) => E,
msgIncludes?: string,
msg?: string,
): Promise<E>;
export async function assertRejects<E extends Error = Error>(
fn: () => PromiseLike<unknown>,
errorClassOrMsg?:
// deno-lint-ignore no-explicit-any
| (new (...args: any[]) => E)
| string,
msgIncludesOrMsg?: string,
msg?: string,
): Promise<E | Error | unknown> {
// deno-lint-ignore no-explicit-any
let ErrorClass: (new (...args: any[]) => E) | undefined = undefined;
let msgIncludes: string | undefined = undefined;
let err;
if (typeof errorClassOrMsg !== "string") {
if (
errorClassOrMsg === undefined ||
errorClassOrMsg.prototype instanceof Error ||
errorClassOrMsg.prototype === Error.prototype
) {
// deno-lint-ignore no-explicit-any
ErrorClass = errorClassOrMsg as new (...args: any[]) => E;
msgIncludes = msgIncludesOrMsg;
}
} else {
msg = errorClassOrMsg;
}
let doesThrow = false;
let isPromiseReturned = false;
const msgSuffix = msg ? `: ${msg}` : ".";
try {
const possiblePromise = fn();
if (
possiblePromise &&
typeof possiblePromise === "object" &&
typeof possiblePromise.then === "function"
) {
isPromiseReturned = true;
await possiblePromise;
}
} catch (error) {
if (!isPromiseReturned) {
throw new AssertionError(
`Function throws when expected to reject${msgSuffix}`,
);
}
if (ErrorClass) {
if (error instanceof Error === false) {
throw new AssertionError(`A non-Error object was rejected${msgSuffix}`);
}
assertIsError(
error,
ErrorClass,
msgIncludes,
msg,
);
}
err = error;
doesThrow = true;
}
if (!doesThrow) {
throw new AssertionError(
`Expected function to reject${msgSuffix}`,
);
}
return err;
}

View File

@ -0,0 +1,109 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals, AssertionError, assertRejects } from "./mod.ts";
Deno.test("assertRejects with return type", async () => {
await assertRejects(() => {
return Promise.reject(new Error());
});
});
Deno.test("assertRejects with synchronous function that throws", async () => {
await assertRejects(() =>
assertRejects(() => {
throw new Error();
})
);
await assertRejects(
() =>
assertRejects(() => {
throw { wrong: "true" };
}),
AssertionError,
"Function throws when expected to reject.",
);
});
Deno.test("assertRejects with PromiseLike", async () => {
await assertRejects(
() => ({
then() {
throw new Error("some error");
},
}),
Error,
"some error",
);
});
Deno.test("assertRejects with non-error value rejected and error class", async () => {
await assertRejects(
() => {
return assertRejects(
() => {
return Promise.reject("Panic!");
},
Error,
"Panic!",
);
},
AssertionError,
"A non-Error object was rejected.",
);
});
Deno.test("assertRejects with non-error value rejected", async () => {
await assertRejects(() => {
return Promise.reject(null);
});
await assertRejects(() => {
return Promise.reject(undefined);
});
});
Deno.test("assertRejects with error class", async () => {
await assertRejects(
() => {
return Promise.reject(new Error("foo"));
},
Error,
"foo",
);
});
Deno.test("assertRejects resolves with caught error", async () => {
const error = await assertRejects(
() => {
return Promise.reject(new Error("foo"));
},
);
assert(error instanceof Error);
assertEquals(error.message, "foo");
});
Deno.test("Assert Throws Async Parent Error", async () => {
await assertRejects(
() => {
return Promise.reject(new AssertionError("Fail!"));
},
Error,
"Fail!",
);
});
Deno.test(
"Assert Throws Async promise rejected with custom Error",
async () => {
class CustomError extends Error {}
class AnotherCustomError extends Error {}
await assertRejects(
() =>
assertRejects(
() => Promise.reject(new AnotherCustomError("failed")),
CustomError,
"fail",
),
AssertionError,
'Expected error to be instance of "CustomError", but was "AnotherCustomError".',
);
},
);

View File

@ -0,0 +1,69 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { format } from "./_format.ts";
import { AssertionError } from "./assertion_error.ts";
import { buildMessage, diff, diffstr } from "../_util/diff.ts";
import { CAN_NOT_DISPLAY } from "./_constants.ts";
import { red } from "../fmt/colors.ts";
/**
* Make an assertion that `actual` and `expected` are strictly equal. If
* not then throw.
*
* @example
* ```ts
* import { assertStrictEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_strict_equals.ts";
*
* Deno.test("isStrictlyEqual", function (): void {
* const a = {};
* const b = a;
* assertStrictEquals(a, b);
* });
*
* // This test fails
* Deno.test("isNotStrictlyEqual", function (): void {
* const a = {};
* const b = {};
* assertStrictEquals(a, b);
* });
* ```
*/
export function assertStrictEquals<T>(
actual: unknown,
expected: T,
msg?: string,
): asserts actual is T {
if (Object.is(actual, expected)) {
return;
}
const msgSuffix = msg ? `: ${msg}` : ".";
let message: string;
const actualString = format(actual);
const expectedString = format(expected);
if (actualString === expectedString) {
const withOffset = actualString
.split("\n")
.map((l) => ` ${l}`)
.join("\n");
message =
`Values have the same structure but are not reference-equal${msgSuffix}\n\n${
red(withOffset)
}\n`;
} else {
try {
const stringDiff = (typeof actual === "string") &&
(typeof expected === "string");
const diffResult = stringDiff
? diffstr(actual as string, expected as string)
: diff(actualString.split("\n"), expectedString.split("\n"));
const diffMsg = buildMessage(diffResult, { stringDiff }).join("\n");
message = `Values are not strictly equal${msgSuffix}\n${diffMsg}`;
} catch {
message = `\n${red(CAN_NOT_DISPLAY)} + \n\n`;
}
}
throw new AssertionError(message);
}

View File

@ -0,0 +1,93 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError, assertStrictEquals, assertThrows } from "./mod.ts";
Deno.test({
name: "strict types test",
fn() {
const x = { number: 2 };
const y = x as Record<never, never>;
const z = x as unknown;
// y.number;
// ~~~~~~
// Property 'number' does not exist on type 'Record<never, never>'.deno-ts(2339)
assertStrictEquals(y, x);
y.number; // ok
// z.number;
// ~
// Object is of type 'unknown'.deno-ts(2571)
assertStrictEquals(z, x);
z.number; // ok
},
});
Deno.test({
name: "strict pass case",
fn() {
assertStrictEquals(true, true);
assertStrictEquals(10, 10);
assertStrictEquals("abc", "abc");
assertStrictEquals(NaN, NaN);
const xs = [1, false, "foo"];
const ys = xs;
assertStrictEquals(xs, ys);
const x = { a: 1 };
const y = x;
assertStrictEquals(x, y);
},
});
Deno.test({
name: "strict failed with structure diff",
fn() {
assertThrows(
() => assertStrictEquals({ a: 1, b: 2 }, { a: 1, c: [3] }),
AssertionError,
`
{
a: 1,
+ c: [
+ 3,
+ ],
- b: 2,
}`,
);
},
});
Deno.test({
name: "strict failed with reference diff",
fn() {
assertThrows(
() => assertStrictEquals({ a: 1, b: 2 }, { a: 1, b: 2 }),
AssertionError,
`Values have the same structure but are not reference-equal.
{
a: 1,
b: 2,
}`,
);
},
});
Deno.test({
name: "strict failed with custom msg",
fn() {
assertThrows(
() => assertStrictEquals({ a: 1 }, { a: 1 }, "CUSTOM MESSAGE"),
AssertionError,
`Values have the same structure but are not reference-equal: CUSTOM MESSAGE
{
a: 1,
}`,
);
},
});

View File

@ -0,0 +1,18 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/**
* Make an assertion that actual includes expected. If not
* then throw.
*/
export function assertStringIncludes(
actual: string,
expected: string,
msg?: string,
) {
if (!actual.includes(expected)) {
const msgSuffix = msg ? `: ${msg}` : ".";
msg = `Expected actual: "${actual}" to contain: "${expected}"${msgSuffix}`;
throw new AssertionError(msg);
}
}

View File

@ -0,0 +1,37 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import {
assert,
assertEquals,
AssertionError,
assertStringIncludes,
} from "./mod.ts";
Deno.test("AssertStringIncludes", function () {
assertStringIncludes("Denosaurus", "saur");
assertStringIncludes("Denosaurus", "Deno");
assertStringIncludes("Denosaurus", "rus");
let didThrow;
try {
assertStringIncludes("Denosaurus", "Raptor");
didThrow = false;
} catch (e) {
assert(e instanceof AssertionError);
didThrow = true;
}
assertEquals(didThrow, true);
});
Deno.test("AssertStringContainsThrow", function () {
let didThrow = false;
try {
assertStringIncludes("Denosaurus from Jurassic", "Raptor");
} catch (e) {
assert(e instanceof AssertionError);
assert(
e.message ===
`Expected actual: "Denosaurus from Jurassic" to contain: "Raptor".`,
);
didThrow = true;
}
assert(didThrow);
});

121
assert/assert_throws.ts Normal file
View File

@ -0,0 +1,121 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertIsError } from "./assert_is_error.ts";
import { AssertionError } from "./assertion_error.ts";
/**
* Executes a function, expecting it to throw. If it does not, then it
* throws.
*
* @example
* ```ts
* import { assertThrows } from "https://deno.land/std@$STD_VERSION/assert/assert_throws.ts";
*
* Deno.test("doesThrow", function (): void {
* assertThrows((): void => {
* throw new TypeError("hello world!");
* });
* });
*
* // This test will not pass.
* Deno.test("fails", function (): void {
* assertThrows((): void => {
* console.log("Hello world");
* });
* });
* ```
*/
export function assertThrows(
fn: () => unknown,
msg?: string,
): unknown;
/**
* Executes a function, expecting it to throw. If it does not, then it
* throws. An error class and a string that should be included in the
* error message can also be asserted.
*
* @example
* ```ts
* import { assertThrows } from "https://deno.land/std@$STD_VERSION/assert/assert_throws.ts";
*
* Deno.test("doesThrow", function (): void {
* assertThrows((): void => {
* throw new TypeError("hello world!");
* }, TypeError);
* assertThrows(
* (): void => {
* throw new TypeError("hello world!");
* },
* TypeError,
* "hello",
* );
* });
*
* // This test will not pass.
* Deno.test("fails", function (): void {
* assertThrows((): void => {
* console.log("Hello world");
* });
* });
* ```
*/
export function assertThrows<E extends Error = Error>(
fn: () => unknown,
// deno-lint-ignore no-explicit-any
ErrorClass: new (...args: any[]) => E,
msgIncludes?: string,
msg?: string,
): E;
export function assertThrows<E extends Error = Error>(
fn: () => unknown,
errorClassOrMsg?:
// deno-lint-ignore no-explicit-any
| (new (...args: any[]) => E)
| string,
msgIncludesOrMsg?: string,
msg?: string,
): E | Error | unknown {
// deno-lint-ignore no-explicit-any
let ErrorClass: (new (...args: any[]) => E) | undefined = undefined;
let msgIncludes: string | undefined = undefined;
let err;
if (typeof errorClassOrMsg !== "string") {
if (
errorClassOrMsg === undefined ||
errorClassOrMsg.prototype instanceof Error ||
errorClassOrMsg.prototype === Error.prototype
) {
// deno-lint-ignore no-explicit-any
ErrorClass = errorClassOrMsg as new (...args: any[]) => E;
msgIncludes = msgIncludesOrMsg;
} else {
msg = msgIncludesOrMsg;
}
} else {
msg = errorClassOrMsg;
}
let doesThrow = false;
const msgSuffix = msg ? `: ${msg}` : ".";
try {
fn();
} catch (error) {
if (ErrorClass) {
if (error instanceof Error === false) {
throw new AssertionError(`A non-Error object was thrown${msgSuffix}`);
}
assertIsError(
error,
ErrorClass,
msgIncludes,
msg,
);
}
err = error;
doesThrow = true;
}
if (!doesThrow) {
msg = `Expected function to throw${msgSuffix}`;
throw new AssertionError(msg);
}
return err;
}

View File

@ -0,0 +1,129 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import {
assert,
assertEquals,
AssertionError,
assertThrows,
fail,
} from "./mod.ts";
Deno.test("assertThrows with wrong error class", () => {
assertThrows(
() => {
//This next assertThrows will throw an AssertionError due to the wrong
//expected error class
assertThrows(
() => {
fail("foo");
},
TypeError,
"Failed assertion: foo",
);
},
AssertionError,
`Expected error to be instance of "TypeError", but was "AssertionError"`,
);
});
Deno.test("assertThrows with return type", () => {
assertThrows(() => {
throw new Error();
});
});
Deno.test("assertThrows with non-error value thrown and error class", () => {
assertThrows(
() => {
assertThrows(
() => {
throw "Panic!";
},
Error,
"Panic!",
);
},
AssertionError,
"A non-Error object was thrown.",
);
});
Deno.test("assertThrows with non-error value thrown", () => {
assertThrows(
() => {
throw "Panic!";
},
);
assertThrows(
() => {
throw null;
},
);
assertThrows(
() => {
throw undefined;
},
);
});
Deno.test("assertThrows with error class", () => {
assertThrows(
() => {
throw new Error("foo");
},
Error,
"foo",
);
});
Deno.test("assertThrows with thrown error returns caught error", () => {
const error = assertThrows(
() => {
throw new Error("foo");
},
);
assert(error instanceof Error);
assertEquals(error.message, "foo");
});
Deno.test("assertThrows with thrown non-error returns caught error", () => {
const stringError = assertThrows(
() => {
throw "Panic!";
},
);
assert(typeof stringError === "string");
assertEquals(stringError, "Panic!");
const numberError = assertThrows(
() => {
throw 1;
},
);
assert(typeof numberError === "number");
assertEquals(numberError, 1);
const nullError = assertThrows(
() => {
throw null;
},
);
assert(nullError === null);
const undefinedError = assertThrows(
() => {
throw undefined;
},
);
assert(typeof undefinedError === "undefined");
assertEquals(undefinedError, undefined);
});
Deno.test("Assert Throws Parent Error", () => {
assertThrows(
() => {
throw new AssertionError("Fail!");
},
Error,
"Fail!",
);
});

View File

@ -0,0 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
export class AssertionError extends Error {
override name = "AssertionError";
constructor(message: string) {
super(message);
}
}

111
assert/equal.ts Normal file
View File

@ -0,0 +1,111 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
function isKeyedCollection(x: unknown): x is Set<unknown> {
return [Symbol.iterator, "size"].every((k) => k in (x as Set<unknown>));
}
function constructorsEqual(a: object, b: object) {
return a.constructor === b.constructor ||
a.constructor === Object && !b.constructor ||
!a.constructor && b.constructor === Object;
}
/**
* Deep equality comparison used in assertions
* @param c actual value
* @param d expected value
*/
export function equal(c: unknown, d: unknown): boolean {
const seen = new Map();
return (function compare(a: unknown, b: unknown): boolean {
// Have to render RegExp & Date for string comparison
// unless it's mistreated as object
if (
a &&
b &&
((a instanceof RegExp && b instanceof RegExp) ||
(a instanceof URL && b instanceof URL))
) {
return String(a) === String(b);
}
if (a instanceof Date && b instanceof Date) {
const aTime = a.getTime();
const bTime = b.getTime();
// Check for NaN equality manually since NaN is not
// equal to itself.
if (Number.isNaN(aTime) && Number.isNaN(bTime)) {
return true;
}
return aTime === bTime;
}
if (typeof a === "number" && typeof b === "number") {
return Number.isNaN(a) && Number.isNaN(b) || a === b;
}
if (Object.is(a, b)) {
return true;
}
if (a && typeof a === "object" && b && typeof b === "object") {
if (a && b && !constructorsEqual(a, b)) {
return false;
}
if (a instanceof WeakMap || b instanceof WeakMap) {
if (!(a instanceof WeakMap && b instanceof WeakMap)) return false;
throw new TypeError("cannot compare WeakMap instances");
}
if (a instanceof WeakSet || b instanceof WeakSet) {
if (!(a instanceof WeakSet && b instanceof WeakSet)) return false;
throw new TypeError("cannot compare WeakSet instances");
}
if (seen.get(a) === b) {
return true;
}
if (Object.keys(a || {}).length !== Object.keys(b || {}).length) {
return false;
}
seen.set(a, b);
if (isKeyedCollection(a) && isKeyedCollection(b)) {
if (a.size !== b.size) {
return false;
}
let unmatchedEntries = a.size;
for (const [aKey, aValue] of a.entries()) {
for (const [bKey, bValue] of b.entries()) {
/* Given that Map keys can be references, we need
* to ensure that they are also deeply equal */
if (
(aKey === aValue && bKey === bValue && compare(aKey, bKey)) ||
(compare(aKey, bKey) && compare(aValue, bValue))
) {
unmatchedEntries--;
break;
}
}
}
return unmatchedEntries === 0;
}
const merged = { ...a, ...b };
for (
const key of [
...Object.getOwnPropertyNames(merged),
...Object.getOwnPropertySymbols(merged),
]
) {
type Key = keyof typeof merged;
if (!compare(a && a[key as Key], b && b[key as Key])) {
return false;
}
if (((key in a) && (!(key in b))) || ((key in b) && (!(key in a)))) {
return false;
}
}
if (a instanceof WeakRef || b instanceof WeakRef) {
if (!(a instanceof WeakRef && b instanceof WeakRef)) return false;
return compare(a.deref(), b.deref());
}
return true;
}
return false;
})(c, d);
}

266
assert/equal_test.ts Normal file
View File

@ -0,0 +1,266 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, assertFalse, assertThrows, equal } from "./mod.ts";
Deno.test("EqualDifferentZero", () => {
assert(equal(0, -0));
assert(equal(0, +0));
assert(equal(+0, -0));
assert(equal([0], [-0]));
assert(equal(["hello", 12.21, 0], ["hello", 12.21, -0]));
assert(equal(["hello", 12.21, 0], ["hello", 12.21, +0]));
assert(equal(["hello", 12.21, -0], ["hello", 12.21, +0]));
assert(equal({ msg: "hello", case: 0 }, { msg: "hello", case: -0 }));
assert(equal({ msg: "hello", array: [0] }, { msg: "hello", array: [-0] }));
});
Deno.test("Equal", function () {
assert(equal("world", "world"));
assert(!equal("hello", "world"));
assertFalse(equal("hello", "world"));
assert(equal(5, 5));
assert(!equal(5, 6));
assertFalse(equal(5, 6));
assert(equal(NaN, NaN));
assert(equal({ hello: "world" }, { hello: "world" }));
assert(!equal({ world: "hello" }, { hello: "world" }));
assertFalse(equal({ world: "hello" }, { hello: "world" }));
assert(
equal(
{ hello: "world", hi: { there: "everyone" } },
{ hello: "world", hi: { there: "everyone" } },
),
);
assert(
!equal(
{ hello: "world", hi: { there: "everyone" } },
{ hello: "world", hi: { there: "everyone else" } },
),
);
assertFalse(
equal(
{ hello: "world", hi: { there: "everyone" } },
{ hello: "world", hi: { there: "everyone else" } },
),
);
assert(equal({ [Symbol.for("foo")]: "bar" }, { [Symbol.for("foo")]: "bar" }));
assert(!equal({ [Symbol("foo")]: "bar" }, { [Symbol("foo")]: "bar" }));
assertFalse(equal({ [Symbol("foo")]: "bar" }, { [Symbol("foo")]: "bar" }));
assert(equal(/deno/, /deno/));
assert(!equal(/deno/, /node/));
assertFalse(equal(/deno/, /node/));
assert(equal(new Date(2019, 0, 3), new Date(2019, 0, 3)));
assert(!equal(new Date(2019, 0, 3), new Date(2019, 1, 3)));
assertFalse(equal(new Date(2019, 0, 3), new Date(2019, 1, 3)));
assert(
!equal(
new Date(2019, 0, 3, 4, 20, 1, 10),
new Date(2019, 0, 3, 4, 20, 1, 20),
),
);
assertFalse(
equal(
new Date(2019, 0, 3, 4, 20, 1, 10),
new Date(2019, 0, 3, 4, 20, 1, 20),
),
);
assert(equal(new Date("Invalid"), new Date("Invalid")));
assert(!equal(new Date("Invalid"), new Date(2019, 0, 3)));
assertFalse(equal(new Date("Invalid"), new Date(2019, 0, 3)));
assert(!equal(new Date("Invalid"), new Date(2019, 0, 3, 4, 20, 1, 10)));
assertFalse(equal(new Date("Invalid"), new Date(2019, 0, 3, 4, 20, 1, 10)));
assert(equal(new Set([1]), new Set([1])));
assert(!equal(new Set([1]), new Set([2])));
assertFalse(equal(new Set([1]), new Set([2])));
assert(equal(new Set([1, 2, 3]), new Set([3, 2, 1])));
assert(equal(new Set([1, new Set([2, 3])]), new Set([new Set([3, 2]), 1])));
assert(!equal(new Set([1, 2]), new Set([3, 2, 1])));
assertFalse(equal(new Set([1, 2]), new Set([3, 2, 1])));
assert(!equal(new Set([1, 2, 3]), new Set([4, 5, 6])));
assertFalse(equal(new Set([1, 2, 3]), new Set([4, 5, 6])));
assert(equal(new Set("denosaurus"), new Set("denosaurussss")));
assert(equal(new Map(), new Map()));
assert(
equal(
new Map([
["foo", "bar"],
["baz", "baz"],
]),
new Map([
["foo", "bar"],
["baz", "baz"],
]),
),
);
assert(
equal(
new Map([["foo", new Map([["bar", "baz"]])]]),
new Map([["foo", new Map([["bar", "baz"]])]]),
),
);
assert(
equal(
new Map([["foo", { bar: "baz" }]]),
new Map([["foo", { bar: "baz" }]]),
),
);
assert(
equal(
new Map([
["foo", "bar"],
["baz", "qux"],
]),
new Map([
["baz", "qux"],
["foo", "bar"],
]),
),
);
assert(equal(new Map([["foo", ["bar"]]]), new Map([["foo", ["bar"]]])));
assert(!equal(new Map([["foo", "bar"]]), new Map([["bar", "baz"]])));
assertFalse(equal(new Map([["foo", "bar"]]), new Map([["bar", "baz"]])));
assertFalse(equal(new Map([["foo", "bar"]]), new Map([["bar", "baz"]])));
assert(
!equal(
new Map([["foo", "bar"]]),
new Map([
["foo", "bar"],
["bar", "baz"],
]),
),
);
assertFalse(
equal(
new Map([["foo", "bar"]]),
new Map([
["foo", "bar"],
["bar", "baz"],
]),
),
);
assert(
!equal(
new Map([["foo", new Map([["bar", "baz"]])]]),
new Map([["foo", new Map([["bar", "qux"]])]]),
),
);
assert(equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 1 }, true]])));
assert(!equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 1 }, false]])));
assertFalse(equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 1 }, false]])));
assert(!equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 2 }, true]])));
assertFalse(equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 2 }, true]])));
assert(equal([1, 2, 3], [1, 2, 3]));
assert(equal([1, [2, 3]], [1, [2, 3]]));
assert(!equal([1, 2, 3, 4], [1, 2, 3]));
assertFalse(equal([1, 2, 3, 4], [1, 2, 3]));
assert(!equal([1, 2, 3, 4], [1, 2, 3]));
assertFalse(equal([1, 2, 3, 4], [1, 2, 3]));
assert(!equal([1, 2, 3, 4], [1, 4, 2, 3]));
assertFalse(equal([1, 2, 3, 4], [1, 4, 2, 3]));
assert(equal(new Uint8Array([1, 2, 3, 4]), new Uint8Array([1, 2, 3, 4])));
assert(!equal(new Uint8Array([1, 2, 3, 4]), new Uint8Array([2, 1, 4, 3])));
assertFalse(
equal(new Uint8Array([1, 2, 3, 4]), new Uint8Array([2, 1, 4, 3])),
);
assert(
equal(new URL("https://example.test"), new URL("https://example.test")),
);
assert(
!equal(
new URL("https://example.test"),
new URL("https://example.test/with-path"),
),
);
assertFalse(
equal(
new URL("https://example.test"),
new URL("https://example.test/with-path"),
),
);
assert(
!equal({ a: undefined, b: undefined }, { a: undefined, c: undefined }),
);
assertFalse(
equal({ a: undefined, b: undefined }, { a: undefined, c: undefined }),
);
assertFalse(equal({ a: undefined, b: undefined }, { a: undefined }));
assertThrows(() => equal(new WeakMap(), new WeakMap()));
assertThrows(() => equal(new WeakSet(), new WeakSet()));
assert(!equal(new WeakMap(), new WeakSet()));
assertFalse(equal(new WeakMap(), new WeakSet()));
assert(
equal(new WeakRef({ hello: "world" }), new WeakRef({ hello: "world" })),
);
assert(
!equal(new WeakRef({ world: "hello" }), new WeakRef({ hello: "world" })),
);
assertFalse(
equal(new WeakRef({ world: "hello" }), new WeakRef({ hello: "world" })),
);
assert(!equal({ hello: "world" }, new WeakRef({ hello: "world" })));
assertFalse(equal({ hello: "world" }, new WeakRef({ hello: "world" })));
assert(
!equal(
new WeakRef({ hello: "world" }),
new (class<T extends object> extends WeakRef<T> {})({ hello: "world" }),
),
);
assertFalse(
equal(
new WeakRef({ hello: "world" }),
new (class<T extends object> extends WeakRef<T> {})({ hello: "world" }),
),
);
assert(
!equal(
new WeakRef({ hello: "world" }),
new (class<T extends object> extends WeakRef<T> {
foo = "bar";
})({ hello: "world" }),
),
);
assertFalse(
equal(
new WeakRef({ hello: "world" }),
new (class<T extends object> extends WeakRef<T> {
foo = "bar";
})({ hello: "world" }),
),
);
assert(
!equal(
new (class A {
#hello = "world";
})(),
new (class B {
#hello = "world";
})(),
),
);
assertFalse(
equal(
new (class A {
#hello = "world";
})(),
new (class B {
#hello = "world";
})(),
),
);
});
Deno.test("EqualCircular", () => {
const objA: { prop?: unknown } = {};
objA.prop = objA;
const objB: { prop?: unknown } = {};
objB.prop = objB;
assert(equal(objA, objB));
const mapA = new Map();
mapA.set("prop", mapA);
const mapB = new Map();
mapB.set("prop", mapB);
assert(equal(mapA, mapB));
});

10
assert/fail.ts Normal file
View File

@ -0,0 +1,10 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert } from "./assert.ts";
/**
* Forcefully throws a failed assertion
*/
export function fail(msg?: string): never {
const msgSuffix = msg ? `: ${msg}` : ".";
assert(false, `Failed assertion${msgSuffix}`);
}

13
assert/fail_test.ts Normal file
View File

@ -0,0 +1,13 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError, assertThrows, fail } from "./mod.ts";
Deno.test("AssertFail", function () {
assertThrows(fail, AssertionError, "Failed assertion.");
assertThrows(
() => {
fail("foo");
},
AssertionError,
"Failed assertion: foo",
);
});

35
assert/mod.ts Normal file
View File

@ -0,0 +1,35 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
/** A library of assertion functions.
* If the assertion is false an `AssertionError` will be thrown which will
* result in pretty-printed diff of failing assertion.
*
* This module is browser compatible, but do not rely on good formatting of
* values for AssertionError messages in browsers.
*
* @module
*/
export * from "./assert_almost_equals.ts";
export * from "./assert_array_includes.ts";
export * from "./assert_equals.ts";
export * from "./assert_exists.ts";
export * from "./assert_false.ts";
export * from "./assert_instance_of.ts";
export * from "./assert_is_error.ts";
export * from "./assert_match.ts";
export * from "./assert_not_equals.ts";
export * from "./assert_not_instance_of.ts";
export * from "./assert_not_match.ts";
export * from "./assert_not_strict_equals.ts";
export * from "./assert_object_match.ts";
export * from "./assert_rejects.ts";
export * from "./assert_strict_equals.ts";
export * from "./assert_string_includes.ts";
export * from "./assert_throws.ts";
export * from "./assert.ts";
export * from "./assertion_error.ts";
export * from "./equal.ts";
export * from "./fail.ts";
export * from "./unimplemented.ts";
export * from "./unreachable.ts";

20
assert/shared_test.ts Normal file
View File

@ -0,0 +1,20 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import {
assertArrayIncludes,
assertEquals,
assertNotEquals,
assertNotStrictEquals,
assertStrictEquals,
} from "./mod.ts";
Deno.test({
name: "assert* functions with specified type parameter",
fn() {
assertEquals<string>("hello", "hello");
assertNotEquals<number>(1, 2);
assertArrayIncludes<boolean>([true, false], [true]);
const value = { x: 1 };
assertStrictEquals<typeof value>(value, value);
assertNotStrictEquals<object>(value, { x: 1 });
},
});

8
assert/unimplemented.ts Normal file
View File

@ -0,0 +1,8 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/** Use this to stub out methods that will throw when invoked. */
export function unimplemented(msg?: string): never {
const msgSuffix = msg ? `: ${msg}` : ".";
throw new AssertionError(`Unimplemented${msgSuffix}`);
}

View File

@ -0,0 +1,14 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, AssertionError, unimplemented } from "./mod.ts";
Deno.test("AssertsUnimplemented", function () {
let didThrow = false;
try {
unimplemented();
} catch (e) {
assert(e instanceof AssertionError);
assert(e.message === "Unimplemented.");
didThrow = true;
}
assert(didThrow);
});

7
assert/unreachable.ts Normal file
View File

@ -0,0 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { AssertionError } from "./assertion_error.ts";
/** Use this to assert unreachable code. */
export function unreachable(): never {
throw new AssertionError("unreachable");
}

View File

@ -0,0 +1,14 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, AssertionError, unreachable } from "./mod.ts";
Deno.test("AssertsUnreachable", function () {
let didThrow = false;
try {
unreachable();
} catch (e) {
assert(e instanceof AssertionError);
assert(e.message === "unreachable");
didThrow = true;
}
assert(didThrow);
});

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertRejects } from "../testing/asserts.ts";
import { assertEquals, assertRejects } from "../assert/mod.ts";
import { deferred } from "./deferred.ts";
import { abortable } from "./abortable.ts";

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertRejects } from "../testing/asserts.ts";
import { assertEquals, assertRejects } from "../assert/mod.ts";
import { delay } from "./delay.ts";
import { deadline, DeadlineError } from "./deadline.ts";

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertStrictEquals } from "../testing/asserts.ts";
import { assertEquals, assertStrictEquals } from "../assert/mod.ts";
import { debounce, DebouncedFunction } from "./debounce.ts";
import { delay } from "./delay.ts";

View File

@ -1,9 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import {
assertEquals,
assertRejects,
assertThrows,
} from "../testing/asserts.ts";
import { assertEquals, assertRejects, assertThrows } from "../assert/mod.ts";
import { deferred } from "./deferred.ts";
Deno.test("[async] deferred: resolve", async function () {

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { delay } from "./delay.ts";
import { assert, assertRejects } from "../testing/asserts.ts";
import { assert, assertRejects } from "../assert/mod.ts";
Deno.test("[async] delay", async function () {
const start = new Date();

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertRejects } from "../testing/asserts.ts";
import { assertEquals, assertRejects } from "../assert/mod.ts";
import { MuxAsyncIterator } from "./mux_async_iterator.ts";
async function* gen123(): AsyncIterableIterator<number> {

View File

@ -6,7 +6,7 @@ import {
assertEquals,
assertRejects,
assertStringIncludes,
} from "../testing/asserts.ts";
} from "../assert/mod.ts";
Deno.test("[async] pooledMap", async function () {
const start = new Date();

View File

@ -1,7 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { assert } from "../_util/asserts.ts";
import { assert } from "../assert/assert.ts";
export class RetryError extends Error {
constructor(cause: unknown, attempts: number) {

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { _exponentialBackoffWithJitter, retry, RetryError } from "./retry.ts";
import { assertEquals, assertRejects } from "../testing/asserts.ts";
import { assertEquals, assertRejects } from "../assert/mod.ts";
import { FakeTime } from "../testing/time.ts";
function generateErroringFunction(errorsBeforeSucceeds: number) {

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { tee } from "./tee.ts";
import { assertEquals } from "../testing/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
/** An example async generator */
const gen = async function* iter() {

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertThrows } from "../testing/asserts.ts";
import { assertEquals, assertThrows } from "../assert/mod.ts";
import { BytesList } from "./bytes_list.ts";
import * as bytes from "./mod.ts";
function setup() {

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "../testing/asserts.ts";
import { assert, assertEquals } from "../assert/mod.ts";
import { concat } from "./concat.ts";
Deno.test("[bytes] concat", () => {

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "../testing/asserts.ts";
import { assert, assertEquals } from "../assert/mod.ts";
import { copy } from "./copy.ts";
Deno.test("[bytes] copy", function () {

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert } from "../testing/asserts.ts";
import { assert } from "../assert/mod.ts";
import { endsWith } from "./ends_with.ts";
Deno.test("[bytes] endsWith", () => {

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { equals } from "./equals.ts";
import { assert } from "../testing/asserts.ts";
import { assert } from "../assert/mod.ts";
Deno.test("[bytes] equals", () => {
const v = equals(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2, 3]));

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { includesNeedle } from "./includes_needle.ts";
import { assert } from "../testing/asserts.ts";
import { assert } from "../assert/mod.ts";
Deno.test("[bytes] includesNeedle", () => {
const encoder = new TextEncoder();

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { indexOfNeedle } from "./index_of_needle.ts";
import { assertEquals } from "../testing/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
Deno.test("[bytes] indexOfNeedle1", () => {
const i = indexOfNeedle(

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
import { lastIndexOfNeedle } from "./last_index_of_needle.ts";
Deno.test("[bytes] lastIndexOfNeedle1", () => {

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertThrows } from "../testing/asserts.ts";
import { assertEquals, assertThrows } from "../assert/mod.ts";
import { repeat } from "./repeat.ts";
Deno.test("[bytes] repeat", () => {

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert } from "../testing/asserts.ts";
import { assert } from "../assert/mod.ts";
import { startsWith } from "./starts_with.ts";
Deno.test("[bytes] startsWith", () => {

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
import { ascend, descend } from "./_comparators.ts";
Deno.test("[collections/comparators] ascend", () => {

View File

@ -10,7 +10,7 @@ import { mapEntries } from "./map_entries.ts";
* @example
* ```ts
* import { aggregateGroups } from "https://deno.land/std@$STD_VERSION/collections/aggregate_groups.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const foodProperties = {
* "Curry": ["spicy", "vegan"],

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
import { aggregateGroups } from "./aggregate_groups.ts";
function aggregateGroupsTest<T, A>(

View File

@ -9,7 +9,7 @@
* @example
* ```ts
* import { associateBy } from "https://deno.land/std@$STD_VERSION/collections/associate_by.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const users = [
* { id: "a2e", userName: "Anna" },

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
import { associateBy } from "./associate_by.ts";
function associateByTest<T>(

View File

@ -9,7 +9,7 @@
* @example
* ```ts
* import { associateWith } from "https://deno.land/std@$STD_VERSION/collections/associate_with.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const names = ["Kim", "Lara", "Jonathan"];
* const namesToLength = associateWith(names, (it) => it.length);

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
import { associateWith } from "./associate_with.ts";
function associateWithTest<T>(

View File

@ -35,7 +35,7 @@ function getParentIndex(index: number) {
* BinaryHeap,
* descend,
* } from "https://deno.land/std@$STD_VERSION/collections/binary_heap.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const maxHeap = new BinaryHeap<number>();
* maxHeap.push(4, 1, 3, 5, 2);

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { assert } from "../_util/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
import { assert } from "../assert/assert.ts";
import { ascend, BinaryHeap, descend } from "./binary_heap.ts";
import { Container, MyMath } from "./_test_utils.ts";

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertStrictEquals } from "../testing/asserts.ts";
import { assertEquals, assertStrictEquals } from "../assert/mod.ts";
import { BinarySearchNode } from "./binary_search_node.ts";
let parent: BinarySearchNode<number>;

View File

@ -29,7 +29,7 @@ export * from "./_comparators.ts";
* BinarySearchTree,
* descend,
* } from "https://deno.land/std@$STD_VERSION/collections/binary_search_tree.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const values = [3, 10, 13, 4, 6, 7, 1, 14];
* const tree = new BinarySearchTree<number>();

View File

@ -4,7 +4,7 @@ import {
assertEquals,
assertStrictEquals,
assertThrows,
} from "../testing/asserts.ts";
} from "../assert/mod.ts";
import { ascend, BinarySearchTree, descend } from "./binary_search_tree.ts";
class MyMath {

View File

@ -7,7 +7,7 @@
* @example
* ```ts
* import { chunk } from "https://deno.land/std@$STD_VERSION/collections/chunk.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const words = [
* "lorem",

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertThrows } from "../testing/asserts.ts";
import { assertEquals, assertThrows } from "../assert/mod.ts";
import { chunk } from "./chunk.ts";
function chunkTest<I>(

View File

@ -16,7 +16,7 @@ const { hasOwn } = Object;
* @example
* ```ts
* import { deepMerge } from "https://deno.land/std@$STD_VERSION/collections/deep_merge.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const a = { foo: true };
* const b = { foo: { bar: true } };
@ -69,7 +69,7 @@ function deepMergeInternal<
>(
record: Readonly<T>,
other: Readonly<U>,
seen: Set<NonNullable<object>>,
seen: Set<NonNullable<unknown>>,
options?: Readonly<Options>,
) {
// Extract options
@ -119,15 +119,15 @@ function deepMergeInternal<
}
function mergeObjects(
left: Readonly<NonNullable<object>>,
right: Readonly<NonNullable<object>>,
seen: Set<NonNullable<object>>,
left: Readonly<NonNullable<Record<string, unknown>>>,
right: Readonly<NonNullable<Record<string, unknown>>>,
seen: Set<NonNullable<unknown>>,
options: Readonly<DeepMergeOptions> = {
arrays: "merge",
sets: "merge",
maps: "merge",
},
): Readonly<NonNullable<object>> {
): Readonly<NonNullable<Record<string, unknown> | Iterable<unknown>>> {
// Recursively merge mergeable objects
if (isMergeable(left) && isMergeable(right)) {
return deepMergeInternal(left, right, seen, options);
@ -177,22 +177,24 @@ function mergeObjects(
* are not considered mergeable (it means that reference will be copied)
*/
function isMergeable(
value: NonNullable<object>,
value: NonNullable<unknown>,
): value is Record<PropertyKey, unknown> {
return Object.getPrototypeOf(value) === Object.prototype;
}
function isIterable(
value: NonNullable<object>,
value: NonNullable<unknown>,
): value is Iterable<unknown> {
return typeof (value as Iterable<unknown>)[Symbol.iterator] === "function";
}
function isNonNullObject(value: unknown): value is NonNullable<object> {
function isNonNullObject(
value: unknown,
): value is NonNullable<Record<string, unknown>> {
return value !== null && typeof value === "object";
}
function getKeys<T extends object>(record: T): Array<keyof T> {
function getKeys<T extends Record<string, unknown>>(record: T): Array<keyof T> {
const ret = Object.getOwnPropertySymbols(record) as Array<keyof T>;
filterInPlace(
ret,

View File

@ -1,5 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertStrictEquals } from "../testing/asserts.ts";
import { assertEquals, assertStrictEquals } from "../assert/mod.ts";
import { deepMerge } from "./deep_merge.ts";
Deno.test("deepMerge: simple merge", () => {

View File

@ -8,7 +8,7 @@
* @example
* ```ts
* import { distinct } from "https://deno.land/std@$STD_VERSION/collections/distinct.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const numbers = [3, 2, 5, 2, 5];
* const distinctNumbers = distinct(numbers);

View File

@ -8,7 +8,7 @@
* @example
* ```ts
* import { distinctBy } from "https://deno.land/std@$STD_VERSION/collections/distinct_by.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const names = ["Anna", "Kim", "Arnold", "Kate"];
* const exampleNamesByFirstLetter = distinctBy(names, (it) => it.charAt(0));

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
import { distinctBy } from "./distinct_by.ts";
function distinctByTest<I>(

View File

@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { assertEquals } from "../assert/mod.ts";
import { distinct } from "./distinct.ts";
function distinctTest<I>(

View File

@ -8,7 +8,7 @@
* @example
* ```ts
* import { dropLastWhile } from "https://deno.land/std@$STD_VERSION/collections/drop_last_while.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts";
* import { assertEquals } from "https://deno.land/std@$STD_VERSION/assert/assert_equals.ts";
*
* const numbers = [22, 30, 44];
*

Some files were not shown because too many files have changed in this diff Show More