mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
feat(testing): add assertInstanceOf
(#2028)
This commit is contained in:
parent
8f560b4887
commit
8e01ec6cb7
@ -20,6 +20,8 @@ pretty-printed diff of failing assertion.
|
||||
for non-primitives the values must reference the same instance.
|
||||
- `assertAlmostEquals()` - Make an assertion that `actual` is almost equal to
|
||||
`expected`, according to a given `epsilon` _(defaults to `1e-7`)_
|
||||
- `assertInstanceOf()` - Make an assertion that `actual` is an instance of
|
||||
`expectedType`.
|
||||
- `assertStringIncludes()` - Make an assertion that `actual` includes
|
||||
`expected`.
|
||||
- `assertMatch()` - Make an assertion that `actual` match RegExp `expected`.
|
||||
|
@ -438,6 +438,48 @@ delta "${f(delta)}" is greater than "${f(tolerance)}"`,
|
||||
);
|
||||
}
|
||||
|
||||
// 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 (!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}".`;
|
||||
} else if (actualTypeStr == "function") {
|
||||
msg =
|
||||
`Expected object to be an instance of "${expectedTypeStr}" but was not an instanced object.`;
|
||||
} else {
|
||||
msg =
|
||||
`Expected object to be an instance of "${expectedTypeStr}" but was "${actualTypeStr}".`;
|
||||
}
|
||||
}
|
||||
assert(actual instanceof expectedType, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an assertion that actual is not null or undefined.
|
||||
* If not then throw.
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
assertArrayIncludes,
|
||||
assertEquals,
|
||||
assertExists,
|
||||
assertInstanceOf,
|
||||
AssertionError,
|
||||
assertIsError,
|
||||
assertMatch,
|
||||
@ -1016,6 +1017,119 @@ Deno.test("assert almost equals number", () => {
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "assertInstanceOf",
|
||||
fn(): void {
|
||||
class TestClass1 {}
|
||||
class TestClass2 {}
|
||||
|
||||
// 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 TestClass1 {}
|
||||
TestClassWithSameName = TestClass1;
|
||||
}
|
||||
assertThrows(
|
||||
() => assertInstanceOf(new TestClassWithSameName(), TestClass1),
|
||||
AssertionError,
|
||||
`Expected object to be an instance of "TestClass1".`,
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "assert* functions with specified type parameter",
|
||||
fn(): void {
|
||||
|
Loading…
Reference in New Issue
Block a user