mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
fix(node/assert): port more test cases from node (#16895)
Co-authored-by: DonIsaac <22823424+DonIsaac@users.noreply.github.com> Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
This commit is contained in:
13
.vscode/launch.json
generated
vendored
13
.vscode/launch.json
generated
vendored
@@ -7,6 +7,19 @@
|
||||
// - "cppvsdbg" is used instead of "lldb" on Windows, because "lldb" is too slow
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "bun",
|
||||
"request": "launch",
|
||||
"name": "[js] bun test [file]",
|
||||
"runtime": "${workspaceFolder}/build/debug/bun-debug",
|
||||
"args": ["test", "${file}"],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {
|
||||
"BUN_DEBUG_QUIET_LOGS": "1",
|
||||
"BUN_DEBUG_jest": "1",
|
||||
"BUN_GARBAGE_COLLECTOR_LEVEL": "1",
|
||||
},
|
||||
},
|
||||
// bun test [file]
|
||||
{
|
||||
"type": "lldb",
|
||||
|
||||
@@ -492,12 +492,12 @@ JSC_DEFINE_HOST_FUNCTION(functionBunDeepEquals, (JSGlobalObject * globalObject,
|
||||
|
||||
JSC::JSValue arg1 = callFrame->uncheckedArgument(0);
|
||||
JSC::JSValue arg2 = callFrame->uncheckedArgument(1);
|
||||
JSC::JSValue arg3 = callFrame->argument(2);
|
||||
JSC::JSValue strict = callFrame->argument(2);
|
||||
|
||||
Vector<std::pair<JSValue, JSValue>, 16> stack;
|
||||
MarkedArgumentBuffer gcBuffer;
|
||||
|
||||
if (arg3.isBoolean() && arg3.asBoolean()) {
|
||||
if (strict.isBoolean() && strict.asBoolean()) {
|
||||
|
||||
bool isEqual = Bun__deepEquals<true, false>(globalObject, arg1, arg2, gcBuffer, stack, &scope, true);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
@@ -672,6 +672,10 @@ template<bool isStrict, bool enableAsymmetricMatchers>
|
||||
bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, MarkedArgumentBuffer& gcBuffer, Vector<std::pair<JSC::JSValue, JSC::JSValue>, 16>& stack, ThrowScope* scope, bool addToStack)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
if (UNLIKELY(!vm.isSafeToRecurse())) {
|
||||
throwStackOverflowError(globalObject, *scope);
|
||||
return false;
|
||||
}
|
||||
|
||||
// need to check this before primitives, asymmetric matchers
|
||||
// can match against any type of value.
|
||||
@@ -754,7 +758,7 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
|
||||
if (v1Array != v2Array)
|
||||
return false;
|
||||
|
||||
if (v1Array && v2Array) {
|
||||
if (v1Array && v2Array && !(o1->isProxy() || o2->isProxy())) {
|
||||
JSC::JSArray* array1 = JSC::jsCast<JSC::JSArray*>(v1);
|
||||
JSC::JSArray* array2 = JSC::jsCast<JSC::JSArray*>(v2);
|
||||
|
||||
@@ -1070,6 +1074,7 @@ std::optional<bool> specialObjectsDequal(JSC__JSGlobalObject* globalObject, Mark
|
||||
foundMatchingKey = true;
|
||||
break;
|
||||
}
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
}
|
||||
|
||||
if (!foundMatchingKey) {
|
||||
@@ -1108,6 +1113,7 @@ std::optional<bool> specialObjectsDequal(JSC__JSGlobalObject* globalObject, Mark
|
||||
foundMatchingKey = true;
|
||||
break;
|
||||
}
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
}
|
||||
|
||||
if (!foundMatchingKey) {
|
||||
@@ -1187,6 +1193,7 @@ std::optional<bool> specialObjectsDequal(JSC__JSGlobalObject* globalObject, Mark
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE(@DonIsaac): could `left` ever _not_ be a JSC::ErrorInstance?
|
||||
if (JSC::ErrorInstance* left = jsDynamicCast<JSC::ErrorInstance*, JSCell>(c1)) {
|
||||
JSC::ErrorInstance* right = jsDynamicCast<JSC::ErrorInstance*, JSCell>(c2);
|
||||
|
||||
@@ -1194,9 +1201,109 @@ std::optional<bool> specialObjectsDequal(JSC__JSGlobalObject* globalObject, Mark
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
left->sanitizedNameString(globalObject) == right->sanitizedNameString(globalObject) && left->sanitizedMessageString(globalObject) == right->sanitizedMessageString(globalObject));
|
||||
if (
|
||||
left->errorType() != right->errorType() || // quick check on ctors (does not handle subclasses)
|
||||
left->sanitizedNameString(globalObject) != right->sanitizedNameString(globalObject) || // manual `.name` changes (usually in subclasses)
|
||||
left->sanitizedMessageString(globalObject) != right->sanitizedMessageString(globalObject) // `.message`
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (isStrict) {
|
||||
if (left->runtimeTypeForCause() != right->runtimeTypeForCause()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VM& vm = globalObject->vm();
|
||||
|
||||
// `.cause` is non-enumerable, so it must be checked explicitly.
|
||||
// note that an undefined cause is different than a missing cause in
|
||||
// strict mode.
|
||||
const PropertyName cause(vm.propertyNames->cause);
|
||||
if constexpr (isStrict) {
|
||||
if (left->hasProperty(globalObject, cause) != right->hasProperty(globalObject, cause)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto leftCause = left->get(globalObject, cause);
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
auto rightCause = right->get(globalObject, cause);
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
if (!Bun__deepEquals<isStrict, enableAsymmetricMatchers>(globalObject, leftCause, rightCause, gcBuffer, stack, scope, true)) {
|
||||
return false;
|
||||
}
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
|
||||
// check arbitrary enumerable properties. `.stack` is not checked.
|
||||
left->materializeErrorInfoIfNeeded(vm);
|
||||
right->materializeErrorInfoIfNeeded(vm);
|
||||
JSC::PropertyNameArray a1(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
|
||||
JSC::PropertyNameArray a2(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
|
||||
left->getPropertyNames(globalObject, a1, DontEnumPropertiesMode::Exclude);
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
right->getPropertyNames(globalObject, a2, DontEnumPropertiesMode::Exclude);
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
|
||||
const size_t propertyArrayLength1 = a1.size();
|
||||
const size_t propertyArrayLength2 = a2.size();
|
||||
if constexpr (isStrict) {
|
||||
if (propertyArrayLength1 != propertyArrayLength2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// take a property name from one, try to get it from both
|
||||
size_t i;
|
||||
for (i = 0; i < propertyArrayLength1; i++) {
|
||||
Identifier i1 = a1[i];
|
||||
if (i1 == vm.propertyNames->stack) continue;
|
||||
PropertyName propertyName1 = PropertyName(i1);
|
||||
|
||||
JSValue prop1 = left->get(globalObject, propertyName1);
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
|
||||
if (UNLIKELY(!prop1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSValue prop2 = right->getIfPropertyExists(globalObject, propertyName1);
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
|
||||
if constexpr (!isStrict) {
|
||||
if (prop1.isUndefined() && prop2.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prop2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Bun__deepEquals<isStrict, enableAsymmetricMatchers>(globalObject, prop1, prop2, gcBuffer, stack, scope, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
}
|
||||
|
||||
// for the remaining properties in the other object, make sure they are undefined
|
||||
for (; i < propertyArrayLength2; i++) {
|
||||
Identifier i2 = a2[i];
|
||||
if (i2 == vm.propertyNames->stack) continue;
|
||||
PropertyName propertyName2 = PropertyName(i2);
|
||||
|
||||
JSValue prop2 = right->getIfPropertyExists(globalObject, propertyName2);
|
||||
RETURN_IF_EXCEPTION(*scope, false);
|
||||
|
||||
if (!prop2.isUndefined()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Int8ArrayType:
|
||||
case Uint8ArrayType:
|
||||
@@ -1213,6 +1320,11 @@ std::optional<bool> specialObjectsDequal(JSC__JSGlobalObject* globalObject, Mark
|
||||
if (!isTypedArrayType(static_cast<JSC::JSType>(c2Type)) || c1Type != c2Type) {
|
||||
return false;
|
||||
}
|
||||
auto info = c1->classInfo();
|
||||
auto info2 = c2->classInfo();
|
||||
if (!info || !info2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSC::JSArrayBufferView* left = jsCast<JSArrayBufferView*, JSCell>(c1);
|
||||
JSC::JSArrayBufferView* right = jsCast<JSArrayBufferView*, JSCell>(c2);
|
||||
@@ -1373,7 +1485,20 @@ std::optional<bool> specialObjectsDequal(JSC__JSGlobalObject* globalObject, Mark
|
||||
compareAsNormalValue:
|
||||
break;
|
||||
}
|
||||
|
||||
// globalThis is only equal to globalThis
|
||||
// NOTE: Zig::GlobalObject is tagged as GlobalProxyType
|
||||
case GlobalObjectType: {
|
||||
if (c1Type != c2Type) return false;
|
||||
auto* g1 = jsDynamicCast<JSC::JSGlobalObject*, JSCell>(c1);
|
||||
auto* g2 = jsDynamicCast<JSC::JSGlobalObject*, JSCell>(c2);
|
||||
return g1->m_globalThis == g2->m_globalThis;
|
||||
}
|
||||
case GlobalProxyType: {
|
||||
if (c1Type != c2Type) return false;
|
||||
auto* gp1 = jsDynamicCast<JSC::JSGlobalProxy*, JSCell>(c1);
|
||||
auto* gp2 = jsDynamicCast<JSC::JSGlobalProxy*, JSCell>(c2);
|
||||
return gp1->target()->m_globalThis == gp2->target()->m_globalThis;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
@@ -1528,6 +1653,19 @@ bool Bun__deepMatch(
|
||||
return true;
|
||||
}
|
||||
|
||||
// anonymous namespace to avoid name collision
|
||||
namespace {
|
||||
template<bool isStrict, bool enableAsymmetricMatchers>
|
||||
inline bool deepEqualsWrapperImpl(JSC__JSValue a, JSC__JSValue b, JSC__JSGlobalObject* global)
|
||||
{
|
||||
auto& vm = global->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
Vector<std::pair<JSC::JSValue, JSC::JSValue>, 16> stack;
|
||||
MarkedArgumentBuffer args;
|
||||
return Bun__deepEquals<isStrict, enableAsymmetricMatchers>(global, JSC::JSValue::decode(a), JSC::JSValue::decode(b), args, stack, &scope, true);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool WebCore__FetchHeaders__isEmpty(WebCore__FetchHeaders* arg0)
|
||||
@@ -2427,35 +2565,6 @@ size_t JSC__VM__heapSize(JSC__VM* arg0)
|
||||
return arg0->heap.size();
|
||||
}
|
||||
|
||||
// This is very naive!
|
||||
JSC__JSInternalPromise* JSC__VM__reloadModule(JSC__VM* vm, JSC__JSGlobalObject* arg1,
|
||||
ZigString arg2)
|
||||
{
|
||||
return nullptr;
|
||||
// JSC::JSMap *map = JSC::jsDynamicCast<JSC::JSMap *>(
|
||||
// arg1->vm(), arg1->moduleLoader()->getDirect(
|
||||
// arg1->vm(), JSC::Identifier::fromString(arg1->vm(), "registry"_s)));
|
||||
|
||||
// const JSC::Identifier identifier = Zig::toIdentifier(arg2, arg1);
|
||||
// JSC::JSValue val = JSC::identifierToJSValue(arg1->vm(), identifier);
|
||||
|
||||
// if (!map->has(arg1, val)) return nullptr;
|
||||
|
||||
// if (JSC::JSObject *registryEntry =
|
||||
// JSC::jsDynamicCast<JSC::JSObject *>(arg1-> map->get(arg1, val))) {
|
||||
// auto moduleIdent = JSC::Identifier::fromString(arg1->vm(), "module");
|
||||
// if (JSC::JSModuleRecord *record = JSC::jsDynamicCast<JSC::JSModuleRecord *>(
|
||||
// arg1->vm(), registryEntry->getDirect(arg1->vm(), moduleIdent))) {
|
||||
// registryEntry->putDirect(arg1->vm(), moduleIdent, JSC::jsUndefined());
|
||||
// JSC::JSModuleRecord::destroy(static_cast<JSC::JSCell *>(record));
|
||||
// }
|
||||
// map->remove(arg1, val);
|
||||
// return JSC__JSModuleLoader__loadAndEvaluateModule(arg1, arg2);
|
||||
// }
|
||||
|
||||
// return nullptr;
|
||||
}
|
||||
|
||||
bool JSC__JSValue__isSameValue(JSC__JSValue JSValue0, JSC__JSValue JSValue1,
|
||||
JSC__JSGlobalObject* globalObject)
|
||||
{
|
||||
@@ -2466,50 +2575,26 @@ bool JSC__JSValue__isSameValue(JSC__JSValue JSValue0, JSC__JSValue JSValue1,
|
||||
|
||||
bool JSC__JSValue__deepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject)
|
||||
{
|
||||
ASSERT_NO_PENDING_EXCEPTION(globalObject);
|
||||
JSValue v1 = JSValue::decode(JSValue0);
|
||||
JSValue v2 = JSValue::decode(JSValue1);
|
||||
|
||||
ThrowScope scope = DECLARE_THROW_SCOPE(globalObject->vm());
|
||||
Vector<std::pair<JSValue, JSValue>, 16> stack;
|
||||
MarkedArgumentBuffer args;
|
||||
return Bun__deepEquals<false, false>(globalObject, v1, v2, args, stack, &scope, true);
|
||||
return deepEqualsWrapperImpl<false, false>(JSValue0, JSValue1, globalObject);
|
||||
}
|
||||
|
||||
bool JSC__JSValue__jestDeepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject)
|
||||
{
|
||||
JSValue v1 = JSValue::decode(JSValue0);
|
||||
JSValue v2 = JSValue::decode(JSValue1);
|
||||
|
||||
ThrowScope scope = DECLARE_THROW_SCOPE(globalObject->vm());
|
||||
Vector<std::pair<JSValue, JSValue>, 16> stack;
|
||||
MarkedArgumentBuffer args;
|
||||
return Bun__deepEquals<false, true>(globalObject, v1, v2, args, stack, &scope, true);
|
||||
return deepEqualsWrapperImpl<false, true>(JSValue0, JSValue1, globalObject);
|
||||
}
|
||||
|
||||
bool JSC__JSValue__strictDeepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject)
|
||||
{
|
||||
JSValue v1 = JSValue::decode(JSValue0);
|
||||
JSValue v2 = JSValue::decode(JSValue1);
|
||||
|
||||
ThrowScope scope = DECLARE_THROW_SCOPE(globalObject->vm());
|
||||
Vector<std::pair<JSValue, JSValue>, 16> stack;
|
||||
MarkedArgumentBuffer args;
|
||||
return Bun__deepEquals<true, false>(globalObject, v1, v2, args, stack, &scope, true);
|
||||
return deepEqualsWrapperImpl<true, false>(JSValue0, JSValue1, globalObject);
|
||||
}
|
||||
|
||||
bool JSC__JSValue__jestStrictDeepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject)
|
||||
{
|
||||
JSValue v1 = JSValue::decode(JSValue0);
|
||||
JSValue v2 = JSValue::decode(JSValue1);
|
||||
|
||||
ThrowScope scope = DECLARE_THROW_SCOPE(globalObject->vm());
|
||||
Vector<std::pair<JSValue, JSValue>, 16> stack;
|
||||
MarkedArgumentBuffer args;
|
||||
|
||||
return Bun__deepEquals<true, true>(globalObject, v1, v2, args, stack, &scope, true);
|
||||
return deepEqualsWrapperImpl<true, true>(JSValue0, JSValue1, globalObject);
|
||||
}
|
||||
|
||||
#undef IMPL_DEEP_EQUALS_WRAPPER
|
||||
|
||||
bool JSC__JSValue__deepMatch(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject, bool replacePropsWithAsymmetricMatchers)
|
||||
{
|
||||
JSValue obj = JSValue::decode(JSValue0);
|
||||
|
||||
@@ -5642,38 +5642,51 @@ pub const JSValue = enum(i64) {
|
||||
}
|
||||
|
||||
/// Object.is()
|
||||
///
|
||||
/// This algorithm differs from the IsStrictlyEqual Algorithm by treating all NaN values as equivalent and by differentiating +0𝔽 from -0𝔽.
|
||||
/// https://tc39.es/ecma262/#sec-samevalue
|
||||
pub fn isSameValue(this: JSValue, other: JSValue, global: *JSGlobalObject) bool {
|
||||
return @intFromEnum(this) == @intFromEnum(other) or cppFn("isSameValue", .{ this, other, global });
|
||||
}
|
||||
|
||||
pub fn deepEquals(this: JSValue, other: JSValue, global: *JSGlobalObject) bool {
|
||||
return cppFn("deepEquals", .{ this, other, global });
|
||||
pub fn deepEquals(this: JSValue, other: JSValue, global: *JSGlobalObject) JSError!bool {
|
||||
// JSC__JSValue__deepEquals
|
||||
const result = cppFn("deepEquals", .{ this, other, global });
|
||||
if (global.hasException()) return error.JSError;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// same as `JSValue.deepEquals`, but with jest asymmetric matchers enabled
|
||||
pub fn jestDeepEquals(this: JSValue, other: JSValue, global: *JSGlobalObject) bun.JSError!bool {
|
||||
pub fn jestDeepEquals(this: JSValue, other: JSValue, global: *JSGlobalObject) JSError!bool {
|
||||
const result = cppFn("jestDeepEquals", .{ this, other, global });
|
||||
if (global.hasException()) return error.JSError;
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn strictDeepEquals(this: JSValue, other: JSValue, global: *JSGlobalObject) bool {
|
||||
return cppFn("strictDeepEquals", .{ this, other, global });
|
||||
pub fn strictDeepEquals(this: JSValue, other: JSValue, global: *JSGlobalObject) JSError!bool {
|
||||
// JSC__JSValue__strictDeepEquals
|
||||
const result = cppFn("strictDeepEquals", .{ this, other, global });
|
||||
if (global.hasException()) return error.JSError;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// same as `JSValue.strictDeepEquals`, but with jest asymmetric matchers enabled
|
||||
pub fn jestStrictDeepEquals(this: JSValue, other: JSValue, global: *JSGlobalObject) bool {
|
||||
return cppFn("jestStrictDeepEquals", .{ this, other, global });
|
||||
pub fn jestStrictDeepEquals(this: JSValue, other: JSValue, global: *JSGlobalObject) JSError!bool {
|
||||
// JSC__JSValue__jestStrictDeepEquals
|
||||
const result = cppFn("jestStrictDeepEquals", .{ this, other, global });
|
||||
if (global.hasException()) return error.JSError;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// NOTE: can throw. Check for exceptions.
|
||||
pub fn deepMatch(this: JSValue, subset: JSValue, global: *JSGlobalObject, replace_props_with_asymmetric_matchers: bool) bool {
|
||||
// JSC__JSValue__deepMatch
|
||||
return cppFn("deepMatch", .{ this, subset, global, replace_props_with_asymmetric_matchers });
|
||||
}
|
||||
|
||||
/// same as `JSValue.deepMatch`, but with jest asymmetric matchers enabled
|
||||
pub fn jestDeepMatch(this: JSValue, subset: JSValue, global: *JSGlobalObject, replace_props_with_asymmetric_matchers: bool) bool {
|
||||
// JSC__JSValue__jestDeepMatch
|
||||
return cppFn("jestDeepMatch", .{ this, subset, global, replace_props_with_asymmetric_matchers });
|
||||
}
|
||||
|
||||
|
||||
@@ -385,6 +385,7 @@ extern "C" size_t Bun__encoding__byteLengthUTF16(const UChar* ptr, size_t len, E
|
||||
extern "C" int64_t Bun__encoding__constructFromLatin1(void*, const unsigned char* ptr, size_t len, Encoding encoding);
|
||||
extern "C" int64_t Bun__encoding__constructFromUTF16(void*, const UChar* ptr, size_t len, Encoding encoding);
|
||||
|
||||
/// @note throws a JS exception and returns false if a stack overflow occurs
|
||||
template<bool isStrict, bool enableAsymmetricMatchers>
|
||||
bool Bun__deepEquals(JSC::JSGlobalObject* globalObject, JSC::JSValue v1, JSC::JSValue v2, JSC::MarkedArgumentBuffer&, Vector<std::pair<JSC::JSValue, JSC::JSValue>, 16>& stack, JSC::ThrowScope* scope, bool addToStack);
|
||||
|
||||
|
||||
@@ -528,7 +528,7 @@ pub const Expect = struct {
|
||||
}
|
||||
|
||||
const signature = comptime getSignature("toBe", "<green>expected<r>", false);
|
||||
if (left.deepEquals(right, globalThis) or left.strictDeepEquals(right, globalThis)) {
|
||||
if (try left.deepEquals(right, globalThis) or try left.strictDeepEquals(right, globalThis)) {
|
||||
const fmt =
|
||||
(if (!has_custom_label) "\n\n<d>If this test should pass, replace \"toBe\" with \"toEqual\" or \"toStrictEqual\"<r>" else "") ++
|
||||
"\n\nExpected: <green>{any}<r>\n" ++
|
||||
@@ -1629,7 +1629,7 @@ pub const Expect = struct {
|
||||
const value: JSValue = try this.getValue(globalThis, thisValue, "toStrictEqual", "<green>expected<r>");
|
||||
|
||||
const not = this.flags.not;
|
||||
var pass = value.jestStrictDeepEquals(expected, globalThis);
|
||||
var pass = try value.jestStrictDeepEquals(expected, globalThis);
|
||||
|
||||
if (not) pass = !pass;
|
||||
if (pass) return .undefined;
|
||||
@@ -2351,7 +2351,7 @@ pub const Expect = struct {
|
||||
|
||||
if (Expect.isAsymmetricMatcher(expected_value)) {
|
||||
const signature = comptime getSignature("toThrow", "<green>expected<r>", false);
|
||||
const is_equal = result.jestStrictDeepEquals(expected_value, globalThis);
|
||||
const is_equal = try result.jestStrictDeepEquals(expected_value, globalThis);
|
||||
|
||||
if (globalThis.hasException()) {
|
||||
return .zero;
|
||||
|
||||
@@ -377,6 +377,11 @@ class AssertionError extends Error {
|
||||
this.operator = operator;
|
||||
}
|
||||
ErrorCaptureStackTrace(this, stackStartFn || stackStartFunction);
|
||||
// JSC::Interpreter::getStackTrace() sometimes short-circuits without creating a .stack property.
|
||||
// e.g.: https://github.com/oven-sh/WebKit/blob/e32c6356625cfacebff0c61d182f759abf6f508a/Source/JavaScriptCore/interpreter/Interpreter.cpp#L501
|
||||
if ($isUndefinedOrNull(this.stack)) {
|
||||
ErrorCaptureStackTrace(this, AssertionError);
|
||||
}
|
||||
// Create error message including the error code in the name.
|
||||
this.stack; // eslint-disable-line no-unused-expressions
|
||||
// Reset the name.
|
||||
|
||||
@@ -129,6 +129,14 @@ export default {
|
||||
}
|
||||
},
|
||||
),
|
||||
SafeWeakMap: makeSafe(
|
||||
WeakMap,
|
||||
class SafeWeakMap extends WeakMap {
|
||||
constructor(i) {
|
||||
super(i);
|
||||
}
|
||||
},
|
||||
),
|
||||
SetPrototypeGetSize: getGetter(Set, "size"),
|
||||
String,
|
||||
TypedArrayPrototypeGetLength: getGetter(Uint8Array, "length"),
|
||||
|
||||
@@ -189,9 +189,8 @@ assert.equal = function equal(actual: unknown, expected: unknown, message?: stri
|
||||
if (arguments.length < 2) {
|
||||
throw $ERR_MISSING_ARGS("actual", "expected");
|
||||
}
|
||||
// eslint-disable-next-line eqeqeq
|
||||
// if (actual != expected && (!NumberIsNaN(actual) || !NumberIsNaN(expected))) {
|
||||
if (actual != expected && !(isNaN(actual) && isNaN(expected))) {
|
||||
|
||||
if (actual != expected && (!NumberIsNaN(actual) || !NumberIsNaN(expected))) {
|
||||
innerFail({
|
||||
actual,
|
||||
expected,
|
||||
@@ -672,7 +671,7 @@ function getActual(fn) {
|
||||
return NO_EXCEPTION_SENTINEL;
|
||||
}
|
||||
|
||||
function checkIsPromise(obj) {
|
||||
function checkIsPromise(obj): obj is Promise<unknown> {
|
||||
// Accept native ES6 promises and promises that are implemented in a similar
|
||||
// way. Do not accept thenables that use a function as `obj` and that have no
|
||||
// `catch` handler.
|
||||
|
||||
@@ -1571,7 +1571,7 @@ export class VerdaccioRegistry {
|
||||
await rm(join(this.packagesPath, "private-pkg-dont-touch"), { force: true });
|
||||
const packageDir = tmpdirSync();
|
||||
const packageJson = join(packageDir, "package.json");
|
||||
this.writeBunfig(packageDir, bunfigOpts);
|
||||
await this.writeBunfig(packageDir, bunfigOpts);
|
||||
this.users = {};
|
||||
return { packageDir, packageJson };
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import vm from "node:vm";
|
||||
|
||||
describe.each([true, false])("Bun.deepEquals(a, b, strict: %p)", strict => {
|
||||
const deepEquals = (a: unknown, b: unknown) => Bun.deepEquals(a, b, strict);
|
||||
it.each([
|
||||
@@ -55,4 +57,23 @@ describe.each([true, false])("Bun.deepEquals(a, b, strict: %p)", strict => {
|
||||
expect(deepEquals(foo, bar)).toBe(false);
|
||||
expect(deepEquals(foo, baz)).toBe(false);
|
||||
});
|
||||
|
||||
describe("global object", () => {
|
||||
let contexts: [vm.Context, vm.Context];
|
||||
|
||||
beforeEach(() => {
|
||||
contexts = [vm.createContext(), vm.createContext()];
|
||||
});
|
||||
afterEach(() => {});
|
||||
|
||||
// TODO: re-enable when https://github.com/oven-sh/bun/issues/17080 is resolved
|
||||
it.skip("main global object is not equal to vm global objects", () => {
|
||||
const [ctx] = contexts;
|
||||
expect(deepEquals(global, ctx)).toBe(false);
|
||||
|
||||
ctx.mainGlobal = global;
|
||||
const areEqual = vm.runInContext("Bun.deepEquals(globalThis, mainGlobal)", ctx);
|
||||
expect(areEqual).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,6 +43,47 @@ describe("expect()", () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
describe("toBe()", () => {
|
||||
let obj = {};
|
||||
it.each([
|
||||
[0, 0.0],
|
||||
[+0, +0],
|
||||
[0, +0],
|
||||
[-0, -0],
|
||||
[1, 1],
|
||||
[1, 1.0],
|
||||
[NaN, NaN],
|
||||
[Infinity, Infinity],
|
||||
[obj, obj],
|
||||
[Symbol.for("a"), Symbol.for("a")],
|
||||
])("expect(%p).toBe(%p) == true", (a, b) => {
|
||||
expect(a).toBe(b);
|
||||
expect(b).toBe(a);
|
||||
});
|
||||
it.each([
|
||||
[0, false],
|
||||
[0, ""],
|
||||
[0, -0],
|
||||
[+0, -0],
|
||||
[1, 2],
|
||||
[1, true],
|
||||
[1, "1"],
|
||||
[Infinity, -Infinity],
|
||||
["foo", "Foo"],
|
||||
["foo", "bar"],
|
||||
["", " "],
|
||||
["", " "],
|
||||
["", true],
|
||||
[{}, {}], //
|
||||
[new Set(), new Set()], //
|
||||
[function a() {}, function a() {}], //
|
||||
[Symbol.for("a"), Symbol.for("b")],
|
||||
[Symbol("a"), Symbol("a")],
|
||||
])("expect(%p).toBe(%p) == false", (a, b) => {
|
||||
expect(a).not.toBe(b);
|
||||
expect(b).not.toBe(a);
|
||||
});
|
||||
});
|
||||
|
||||
test("rejects", async () => {
|
||||
await expect(Promise.reject(4)).rejects.toBe(4);
|
||||
|
||||
4
test/js/bun/test/jest.d.ts
vendored
4
test/js/bun/test/jest.d.ts
vendored
@@ -3,3 +3,7 @@ declare var describe: typeof import("bun:test").describe;
|
||||
declare var test: typeof import("bun:test").test;
|
||||
declare var expect: typeof import("bun:test").expect;
|
||||
declare var it: typeof import("bun:test").it;
|
||||
declare var beforeEach: typeof import("bun:test").beforeEach;
|
||||
declare var afterEach: typeof import("bun:test").afterEach;
|
||||
declare var beforeAll: typeof import("bun:test").beforeAll;
|
||||
declare var afterAll: typeof import("bun:test").afterAll;
|
||||
|
||||
96
test/js/node/assert/assert.spec.ts
Normal file
96
test/js/node/assert/assert.spec.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { describe, beforeEach, it, expect } from "bun:test";
|
||||
import assert, { AssertionError } from "assert";
|
||||
|
||||
describe("assert(expr)", () => {
|
||||
// https://github.com/oven-sh/bun/issues/941
|
||||
it.each([true, 1, "foo"])(`assert(%p) does not throw`, expr => {
|
||||
expect(() => assert(expr)).not.toThrow();
|
||||
});
|
||||
|
||||
it.each([false, 0, "", null, undefined])(`assert(%p) throws`, expr => {
|
||||
expect(() => assert(expr)).toThrow(AssertionError);
|
||||
});
|
||||
|
||||
it("is an alias for assert.ok", () => {
|
||||
expect(assert as Function).toBe(assert.ok);
|
||||
});
|
||||
});
|
||||
|
||||
describe("assert.equal(actual, expected)", () => {
|
||||
it.each([
|
||||
["foo", "foo"],
|
||||
[1, 1],
|
||||
[1, true],
|
||||
[0, ""],
|
||||
[0, false],
|
||||
[Symbol.for("foo"), Symbol.for("foo")],
|
||||
])(`%p == %p`, (actual, expected) => {
|
||||
expect(() => assert.equal(actual, expected)).not.toThrow();
|
||||
});
|
||||
it.each([
|
||||
//
|
||||
["foo", "bar"],
|
||||
[1, 0],
|
||||
[true, false],
|
||||
[{}, {}],
|
||||
[Symbol("foo"), Symbol("foo")],
|
||||
[new Error("oops"), new Error("oops")],
|
||||
])("%p != %p", (actual, expected) => {
|
||||
expect(() => assert.equal(actual, expected)).toThrow(AssertionError);
|
||||
});
|
||||
});
|
||||
|
||||
describe("assert.deepEqual(actual, expected)", () => {
|
||||
describe("error instances", () => {
|
||||
let e1: Error & Record<string, any>, e2: Error & Record<string, any>;
|
||||
|
||||
beforeEach(() => {
|
||||
e1 = new Error("oops");
|
||||
e2 = new Error("oops");
|
||||
});
|
||||
|
||||
it("errors with the same message and constructor are equal", () => {
|
||||
expect(() => assert.deepEqual(e1, e2)).not.toThrow();
|
||||
});
|
||||
|
||||
it("errors with different messages are not equal", () => {
|
||||
e2.message = "nope";
|
||||
expect(() => assert.deepEqual(e1, e2)).toThrow(AssertionError);
|
||||
});
|
||||
|
||||
it("errors with different constructors are not equal", () => {
|
||||
e2 = new TypeError("oops");
|
||||
expect(() => assert.deepEqual(e1, e2)).toThrow(AssertionError);
|
||||
});
|
||||
|
||||
it("errors with different names are not equal", () => {
|
||||
e2.name = "SpecialError";
|
||||
expect(() => assert.deepEqual(e1, e2)).toThrow(AssertionError);
|
||||
});
|
||||
|
||||
it("errors with different causes are not equal", () => {
|
||||
e1.cause = { property: "value" };
|
||||
expect(() => assert.deepEqual(e1, e2)).toThrow(AssertionError);
|
||||
e2.cause = { property: "another value" };
|
||||
expect(() => assert.deepEqual(e1, e2)).toThrow(AssertionError);
|
||||
});
|
||||
|
||||
it("errors with the same cause are equal", () => {
|
||||
e1.cause = { property: "value" };
|
||||
e2.cause = { property: "value" };
|
||||
expect(() => assert.deepEqual(e1, e2)).not.toThrow();
|
||||
});
|
||||
|
||||
it("adding different arbitrary properties makes errors unequal", () => {
|
||||
expect(() => assert.deepEqual(e1, e2)).not.toThrow();
|
||||
e1.a = 1;
|
||||
expect(() => assert.deepEqual(e1, e2)).toThrow(AssertionError);
|
||||
e2.a = 1;
|
||||
expect(() => assert.deepEqual(e1, e2)).not.toThrow();
|
||||
e2.a = { foo: "bar" };
|
||||
expect(() => assert.deepEqual(e1, e2)).toThrow(AssertionError);
|
||||
e1.a = { foo: "baz" };
|
||||
expect(() => assert.deepEqual(e1, e2)).toThrow(AssertionError);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
import assert from "assert";
|
||||
import { expect, test } from "bun:test";
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/941
|
||||
test("assert as a function does not throw", () => assert(true));
|
||||
test("assert as a function does throw", () => {
|
||||
try {
|
||||
assert(false);
|
||||
expect.unreachable();
|
||||
} catch (e) {}
|
||||
});
|
||||
@@ -348,8 +348,9 @@ if (normalized.includes("node/test/parallel")) {
|
||||
}
|
||||
}
|
||||
|
||||
function describe(labelOrFn: string | Function, maybeFn?: Function) {
|
||||
const [label, fn] = typeof labelOrFn == "function" ? [labelOrFn.name, labelOrFn] : [labelOrFn, maybeFn];
|
||||
function describe(labelOrFn: string | Function, maybeFnOrOptions?: Function, maybeFn?: Function) {
|
||||
const [label, fn] =
|
||||
typeof labelOrFn == "function" ? [labelOrFn.name, labelOrFn] : [labelOrFn, maybeFn ?? maybeFnOrOptions];
|
||||
if (typeof fn !== "function") throw new TypeError("Second argument to describe() must be a function.");
|
||||
|
||||
getContext().testStack.push(label);
|
||||
@@ -372,7 +373,9 @@ if (normalized.includes("node/test/parallel")) {
|
||||
|
||||
return {
|
||||
test,
|
||||
it: test,
|
||||
describe,
|
||||
suite: describe,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
require('../../common');
|
||||
const assert = require('assert');
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
|
||||
describe('assert.CallTracker.getCalls()', { concurrency: !process.env.TEST_PARALLEL }, () => {
|
||||
const tracker = new assert.CallTracker();
|
||||
|
||||
it('should return empty list when no calls', () => {
|
||||
const fn = tracker.calls();
|
||||
assert.deepStrictEqual(tracker.getCalls(fn), []);
|
||||
});
|
||||
|
||||
it('should return calls', () => {
|
||||
const fn = tracker.calls(() => {});
|
||||
const arg1 = {};
|
||||
const arg2 = {};
|
||||
fn(arg1, arg2);
|
||||
fn.call(arg2, arg2);
|
||||
assert.deepStrictEqual(tracker.getCalls(fn), [
|
||||
{ arguments: [arg1, arg2], thisArg: undefined },
|
||||
{ arguments: [arg2], thisArg: arg2 }]);
|
||||
});
|
||||
|
||||
it('should throw when getting calls of a non-tracked function', () => {
|
||||
[() => {}, 1, true, null, undefined, {}, []].forEach((fn) => {
|
||||
assert.throws(() => tracker.getCalls(fn), { code: 'ERR_INVALID_ARG_VALUE' });
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a frozen object', () => {
|
||||
const fn = tracker.calls();
|
||||
fn();
|
||||
const calls = tracker.getCalls(fn);
|
||||
// NOTE: v8 and jsc use different error messages for mutating frozen objects.
|
||||
assert.throws(() => calls.push(1), /Attempted to assign to readonly property/);
|
||||
assert.throws(() => Object.assign(calls[0], { foo: 'bar' }), /object that is not extensible/);
|
||||
assert.throws(() => calls[0].arguments.push(1), /Attempted to assign to readonly property/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('assert.CallTracker.reset()', () => {
|
||||
const tracker = new assert.CallTracker();
|
||||
|
||||
it('should reset calls', () => {
|
||||
const fn = tracker.calls();
|
||||
fn();
|
||||
fn();
|
||||
fn();
|
||||
assert.strictEqual(tracker.getCalls(fn).length, 3);
|
||||
tracker.reset(fn);
|
||||
assert.deepStrictEqual(tracker.getCalls(fn), []);
|
||||
});
|
||||
|
||||
it('should reset all calls', () => {
|
||||
const fn1 = tracker.calls();
|
||||
const fn2 = tracker.calls();
|
||||
fn1();
|
||||
fn2();
|
||||
assert.strictEqual(tracker.getCalls(fn1).length, 1);
|
||||
assert.strictEqual(tracker.getCalls(fn2).length, 1);
|
||||
tracker.reset();
|
||||
assert.deepStrictEqual(tracker.getCalls(fn1), []);
|
||||
assert.deepStrictEqual(tracker.getCalls(fn2), []);
|
||||
});
|
||||
|
||||
|
||||
it('should throw when resetting a non-tracked function', () => {
|
||||
[() => {}, 1, true, null, {}, []].forEach((fn) => {
|
||||
assert.throws(() => tracker.reset(fn), { code: 'ERR_INVALID_ARG_VALUE' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
const { hasCrypto } = require('../../common');
|
||||
const { test } = require('node:test');
|
||||
const assert = require('assert');
|
||||
|
||||
// Turn off no-restricted-properties because we are testing deepEqual!
|
||||
/* eslint-disable no-restricted-properties */
|
||||
|
||||
// Disable colored output to prevent color codes from breaking assertion
|
||||
// message comparisons. This should only be an issue when process.stdout
|
||||
// is a TTY.
|
||||
if (process.stdout.isTTY)
|
||||
process.env.NODE_DISABLE_COLORS = '1';
|
||||
|
||||
test('', { skip: !hasCrypto }, () => {
|
||||
// See https://github.com/nodejs/node/issues/10258
|
||||
{
|
||||
const date = new Date('2016');
|
||||
function FakeDate() {}
|
||||
FakeDate.prototype = Date.prototype;
|
||||
const fake = new FakeDate();
|
||||
|
||||
assert.notDeepEqual(date, fake);
|
||||
assert.notDeepEqual(fake, date);
|
||||
|
||||
// For deepStrictEqual we check the runtime type,
|
||||
// then reveal the fakeness of the fake date
|
||||
assert.throws(
|
||||
() => assert.deepStrictEqual(date, fake),
|
||||
{
|
||||
message: 'Expected values to be strictly deep-equal:\n' +
|
||||
'+ actual - expected\n' +
|
||||
'\n' +
|
||||
'+ 2016-01-01T00:00:00.000Z\n' +
|
||||
'- Date {}\n'
|
||||
}
|
||||
);
|
||||
assert.throws(
|
||||
() => assert.deepStrictEqual(fake, date),
|
||||
{
|
||||
message: 'Expected values to be strictly deep-equal:\n' +
|
||||
'+ actual - expected\n' +
|
||||
'\n' +
|
||||
'+ Date {}\n' +
|
||||
'- 2016-01-01T00:00:00.000Z\n'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
{ // At the moment global has its own type tag
|
||||
const fakeGlobal = {};
|
||||
Object.setPrototypeOf(fakeGlobal, Object.getPrototypeOf(globalThis));
|
||||
for (const prop of Object.keys(globalThis)) {
|
||||
fakeGlobal[prop] = global[prop];
|
||||
}
|
||||
assert.notDeepEqual(fakeGlobal, globalThis);
|
||||
// Message will be truncated anyway, don't validate
|
||||
assert.throws(() => assert.deepStrictEqual(fakeGlobal, globalThis),
|
||||
assert.AssertionError);
|
||||
}
|
||||
|
||||
{ // At the moment process has its own type tag
|
||||
const fakeProcess = {};
|
||||
Object.setPrototypeOf(fakeProcess, Object.getPrototypeOf(process));
|
||||
for (const prop of Object.keys(process)) {
|
||||
fakeProcess[prop] = process[prop];
|
||||
}
|
||||
assert.notDeepEqual(fakeProcess, process);
|
||||
// Message will be truncated anyway, don't validate
|
||||
assert.throws(() => assert.deepStrictEqual(fakeProcess, process),
|
||||
assert.AssertionError);
|
||||
}
|
||||
});
|
||||
/* eslint-enable */
|
||||
@@ -0,0 +1,82 @@
|
||||
'use strict';
|
||||
require('../../common');
|
||||
const assert = require('assert');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const defaultStartMessage = 'Expected values to be strictly deep-equal:\n' +
|
||||
'+ actual - expected\n' +
|
||||
'\n';
|
||||
|
||||
test('Handle error causes', () => {
|
||||
assert.deepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new Error('x') }));
|
||||
assert.deepStrictEqual(
|
||||
new Error('a', { cause: new RangeError('x') }),
|
||||
new Error('a', { cause: new RangeError('x') }),
|
||||
);
|
||||
|
||||
assert.throws(() => {
|
||||
assert.deepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new Error('y') }));
|
||||
}, { message: defaultStartMessage + ' [Error: a] {\n' +
|
||||
'+ [cause]: [Error: x]\n' +
|
||||
'- [cause]: [Error: y]\n' +
|
||||
' }\n' });
|
||||
|
||||
assert.throws(() => {
|
||||
assert.deepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new TypeError('x') }));
|
||||
}, { message: defaultStartMessage + ' [Error: a] {\n' +
|
||||
'+ [cause]: [Error: x]\n' +
|
||||
'- [cause]: [TypeError: x]\n' +
|
||||
' }\n' });
|
||||
|
||||
assert.throws(() => {
|
||||
assert.deepStrictEqual(new Error('a'), new Error('a', { cause: new Error('y') }));
|
||||
}, { message: defaultStartMessage + '+ [Error: a]\n' +
|
||||
'- [Error: a] {\n' +
|
||||
'- [cause]: [Error: y]\n' +
|
||||
'- }\n' });
|
||||
|
||||
assert.throws(() => {
|
||||
assert.deepStrictEqual(new Error('a'), new Error('a', { cause: { prop: 'value' } }));
|
||||
}, { message: defaultStartMessage + '+ [Error: a]\n' +
|
||||
'- [Error: a] {\n' +
|
||||
'- [cause]: {\n' +
|
||||
'- prop: \'value\'\n' +
|
||||
'- }\n' +
|
||||
'- }\n' });
|
||||
|
||||
assert.notDeepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new Error('y') }));
|
||||
assert.notDeepStrictEqual(
|
||||
new Error('a', { cause: { prop: 'value' } }),
|
||||
new Error('a', { cause: { prop: 'a different value' } })
|
||||
);
|
||||
});
|
||||
|
||||
test('Handle undefined causes', () => {
|
||||
assert.deepStrictEqual(new Error('a', { cause: undefined }), new Error('a', { cause: undefined }));
|
||||
|
||||
assert.notDeepStrictEqual(new Error('a', { cause: 'undefined' }), new Error('a', { cause: undefined }));
|
||||
assert.notDeepStrictEqual(new Error('a', { cause: undefined }), new Error('a'));
|
||||
assert.notDeepStrictEqual(new Error('a'), new Error('a', { cause: undefined }));
|
||||
|
||||
assert.throws(() => {
|
||||
assert.deepStrictEqual(new Error('a'), new Error('a', { cause: undefined }));
|
||||
}, { message: defaultStartMessage +
|
||||
'+ [Error: a]\n' +
|
||||
'- [Error: a] {\n' +
|
||||
'- [cause]: undefined\n' +
|
||||
'- }\n' });
|
||||
|
||||
assert.throws(() => {
|
||||
assert.deepStrictEqual(new Error('a', { cause: undefined }), new Error('a'));
|
||||
}, { message: defaultStartMessage +
|
||||
'+ [Error: a] {\n' +
|
||||
'+ [cause]: undefined\n' +
|
||||
'+ }\n' +
|
||||
'- [Error: a]\n' });
|
||||
assert.throws(() => {
|
||||
assert.deepStrictEqual(new Error('a', { cause: undefined }), new Error('a', { cause: 'undefined' }));
|
||||
}, { message: defaultStartMessage + ' [Error: a] {\n' +
|
||||
'+ [cause]: undefined\n' +
|
||||
'- [cause]: \'undefined\'\n' +
|
||||
' }\n' });
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
const { spawnPromisified } = require('../../common');
|
||||
const assert = require('node:assert');
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const fileImports = {
|
||||
commonjs: 'const assert = require("assert");',
|
||||
module: 'import assert from "assert";',
|
||||
};
|
||||
|
||||
describe('ensure the assert.ok throwing similar error messages for esm and cjs files', () => {
|
||||
it('should return code 1 for each command', async () => {
|
||||
const errorsMessages = [];
|
||||
for (const [inputType, header] of Object.entries(fileImports)) {
|
||||
const { stderr, code } = await spawnPromisified(process.execPath, [
|
||||
'--input-type',
|
||||
inputType,
|
||||
'--eval',
|
||||
`${header}\nassert.ok(0 === 2);\n`,
|
||||
]);
|
||||
assert.strictEqual(code, 1);
|
||||
// For each error message, filter the lines which will starts with AssertionError
|
||||
errorsMessages.push(
|
||||
stderr.split('\n').find((s) => s.startsWith('AssertionError'))
|
||||
);
|
||||
}
|
||||
assert.strictEqual(errorsMessages.length, 2);
|
||||
assert.deepStrictEqual(errorsMessages[0], errorsMessages[1]);
|
||||
});
|
||||
});
|
||||
50
test/js/node/test/parallel/needs-test/test-assert-fail.js
Normal file
50
test/js/node/test/parallel/needs-test/test-assert-fail.js
Normal file
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
require('../../common');
|
||||
const assert = require('assert');
|
||||
const { test } = require('node:test');
|
||||
|
||||
test('No args', () => {
|
||||
assert.throws(
|
||||
() => { assert.fail(); },
|
||||
{
|
||||
code: 'ERR_ASSERTION',
|
||||
name: 'AssertionError',
|
||||
message: 'Failed',
|
||||
operator: 'fail',
|
||||
actual: undefined,
|
||||
expected: undefined,
|
||||
generatedMessage: true,
|
||||
stack: /Failed/
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('One arg = message', () => {
|
||||
assert.throws(() => {
|
||||
assert.fail('custom message');
|
||||
}, {
|
||||
code: 'ERR_ASSERTION',
|
||||
name: 'AssertionError',
|
||||
message: 'custom message',
|
||||
operator: 'fail',
|
||||
actual: undefined,
|
||||
expected: undefined,
|
||||
generatedMessage: false
|
||||
});
|
||||
});
|
||||
|
||||
test('One arg = Error', () => {
|
||||
assert.throws(() => {
|
||||
assert.fail(new TypeError('custom message'));
|
||||
}, {
|
||||
name: 'TypeError',
|
||||
message: 'custom message'
|
||||
});
|
||||
});
|
||||
|
||||
test('Object prototype get', () => {
|
||||
Object.prototype.get = () => { throw new Error('failed'); };
|
||||
assert.throws(() => assert.fail(''), { code: 'ERR_ASSERTION' });
|
||||
delete Object.prototype.get;
|
||||
});
|
||||
104
test/js/node/test/parallel/needs-test/test-assert-if-error.js
Normal file
104
test/js/node/test/parallel/needs-test/test-assert-if-error.js
Normal file
@@ -0,0 +1,104 @@
|
||||
'use strict';
|
||||
|
||||
require('../../common');
|
||||
const assert = require('assert');
|
||||
const { test } = require('node:test');
|
||||
|
||||
test('Test that assert.ifError has the correct stack trace of both stacks', () => {
|
||||
let err;
|
||||
// Create some random error frames.
|
||||
(function a() {
|
||||
(function b() {
|
||||
(function c() {
|
||||
err = new Error('test error');
|
||||
})();
|
||||
})();
|
||||
})();
|
||||
|
||||
const msg = err.message;
|
||||
const stack = err.stack;
|
||||
|
||||
(function x() {
|
||||
(function y() {
|
||||
(function z() {
|
||||
let threw = false;
|
||||
try {
|
||||
assert.ifError(err);
|
||||
} catch (e) {
|
||||
assert.strictEqual(e.message,
|
||||
'ifError got unwanted exception: test error');
|
||||
assert.strictEqual(err.message, msg);
|
||||
assert.strictEqual(e.actual, err);
|
||||
assert.strictEqual(e.actual.stack, stack);
|
||||
assert.strictEqual(e.expected, null);
|
||||
assert.strictEqual(e.operator, 'ifError');
|
||||
threw = true;
|
||||
}
|
||||
assert(threw);
|
||||
})();
|
||||
})();
|
||||
})();
|
||||
});
|
||||
|
||||
test('General ifError tests', () => {
|
||||
assert.throws(
|
||||
() => {
|
||||
const error = new Error();
|
||||
error.stack = 'Error: containing weird stack\nYes!\nI am part of a stack.';
|
||||
assert.ifError(error);
|
||||
},
|
||||
(error) => {
|
||||
assert(!error.stack.includes('Yes!'));
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => assert.ifError(new TypeError()),
|
||||
{
|
||||
message: 'ifError got unwanted exception: TypeError'
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => assert.ifError({ stack: false }),
|
||||
{
|
||||
message: 'ifError got unwanted exception: { stack: false }'
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => assert.ifError({ constructor: null, message: '' }),
|
||||
{
|
||||
message: 'ifError got unwanted exception: '
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => { assert.ifError(false); },
|
||||
{
|
||||
message: 'ifError got unwanted exception: false'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('Should not throw', () => {
|
||||
assert.ifError(null);
|
||||
assert.ifError();
|
||||
assert.ifError(undefined);
|
||||
});
|
||||
|
||||
test('https://github.com/nodejs/node-v0.x-archive/issues/2893', () => {
|
||||
let threw = false;
|
||||
try {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
assert.throws(() => {
|
||||
assert.ifError(null);
|
||||
});
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
assert.strictEqual(e.message, 'Missing expected exception.');
|
||||
assert(!e.stack.includes('throws'), e);
|
||||
}
|
||||
assert(threw);
|
||||
});
|
||||
128
test/js/node/test/parallel/test-assert-calltracker-calls.js
Normal file
128
test/js/node/test/parallel/test-assert-calltracker-calls.js
Normal file
@@ -0,0 +1,128 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
// This test ensures that assert.CallTracker.calls() works as intended.
|
||||
|
||||
const tracker = new assert.CallTracker();
|
||||
|
||||
function bar() {}
|
||||
|
||||
const err = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
};
|
||||
|
||||
// Ensures calls() throws on invalid input types.
|
||||
assert.throws(() => {
|
||||
const callsbar = tracker.calls(bar, '1');
|
||||
callsbar();
|
||||
}, err
|
||||
);
|
||||
|
||||
assert.throws(() => {
|
||||
const callsbar = tracker.calls(bar, 0.1);
|
||||
callsbar();
|
||||
}, { code: 'ERR_OUT_OF_RANGE' }
|
||||
);
|
||||
|
||||
assert.throws(() => {
|
||||
const callsbar = tracker.calls(bar, true);
|
||||
callsbar();
|
||||
}, err
|
||||
);
|
||||
|
||||
assert.throws(() => {
|
||||
const callsbar = tracker.calls(bar, () => {});
|
||||
callsbar();
|
||||
}, err
|
||||
);
|
||||
|
||||
assert.throws(() => {
|
||||
const callsbar = tracker.calls(bar, null);
|
||||
callsbar();
|
||||
}, err
|
||||
);
|
||||
|
||||
// Expects an error as tracker.calls() cannot be called within a process exit
|
||||
// handler.
|
||||
process.on('exit', () => {
|
||||
assert.throws(() => tracker.calls(bar, 1), {
|
||||
code: 'ERR_UNAVAILABLE_DURING_EXIT',
|
||||
});
|
||||
});
|
||||
|
||||
const msg = 'Expected to throw';
|
||||
|
||||
function func() {
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
const callsfunc = tracker.calls(func, 1);
|
||||
|
||||
// Expects callsfunc() to call func() which throws an error.
|
||||
assert.throws(
|
||||
() => callsfunc(),
|
||||
{ message: msg }
|
||||
);
|
||||
|
||||
{
|
||||
const tracker = new assert.CallTracker();
|
||||
const callsNoop = tracker.calls(1);
|
||||
callsNoop();
|
||||
tracker.verify();
|
||||
}
|
||||
|
||||
{
|
||||
const tracker = new assert.CallTracker();
|
||||
const callsNoop = tracker.calls(undefined, 1);
|
||||
callsNoop();
|
||||
tracker.verify();
|
||||
}
|
||||
|
||||
{
|
||||
function func() {}
|
||||
const tracker = new assert.CallTracker();
|
||||
const callsfunc = tracker.calls(func);
|
||||
assert.strictEqual(callsfunc.length, 0);
|
||||
}
|
||||
|
||||
{
|
||||
function func(a, b, c = 2) {}
|
||||
const tracker = new assert.CallTracker();
|
||||
const callsfunc = tracker.calls(func);
|
||||
assert.strictEqual(callsfunc.length, 2);
|
||||
}
|
||||
|
||||
{
|
||||
function func(a, b, c = 2) {}
|
||||
delete func.length;
|
||||
const tracker = new assert.CallTracker();
|
||||
const callsfunc = tracker.calls(func);
|
||||
assert.strictEqual(Object.hasOwn(callsfunc, 'length'), false);
|
||||
}
|
||||
|
||||
{
|
||||
const ArrayIteratorPrototype = Reflect.getPrototypeOf(
|
||||
Array.prototype.values()
|
||||
);
|
||||
const { next } = ArrayIteratorPrototype;
|
||||
ArrayIteratorPrototype.next = common.mustNotCall(
|
||||
'%ArrayIteratorPrototype%.next'
|
||||
);
|
||||
Object.prototype.get = common.mustNotCall('%Object.prototype%.get');
|
||||
|
||||
const customPropertyValue = Symbol();
|
||||
function func(a, b, c = 2) {
|
||||
return a + b + c;
|
||||
}
|
||||
func.customProperty = customPropertyValue;
|
||||
Object.defineProperty(func, 'length', { get: common.mustNotCall() });
|
||||
const tracker = new assert.CallTracker();
|
||||
const callsfunc = tracker.calls(func);
|
||||
assert.strictEqual(Object.hasOwn(callsfunc, 'length'), true);
|
||||
assert.strictEqual(callsfunc.customProperty, customPropertyValue);
|
||||
assert.strictEqual(callsfunc(1, 2, 3), 6);
|
||||
|
||||
ArrayIteratorPrototype.next = next;
|
||||
delete Object.prototype.get;
|
||||
}
|
||||
26
test/js/node/test/parallel/test-assert-calltracker-report.js
Normal file
26
test/js/node/test/parallel/test-assert-calltracker-report.js
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
// This test ensures that the assert.CallTracker.report() works as intended.
|
||||
|
||||
const tracker = new assert.CallTracker();
|
||||
|
||||
function foo() {}
|
||||
|
||||
const callsfoo = tracker.calls(foo, 1);
|
||||
|
||||
// Ensures that foo was added to the callChecks array.
|
||||
assert.strictEqual(tracker.report()[0].operator, 'foo');
|
||||
|
||||
callsfoo();
|
||||
|
||||
// Ensures that foo was removed from the callChecks array after being called the
|
||||
// expected number of times.
|
||||
assert.strictEqual(typeof tracker.report()[0], 'undefined');
|
||||
|
||||
callsfoo();
|
||||
|
||||
// Ensures that foo was added back to the callChecks array after being called
|
||||
// more than the expected number of times.
|
||||
assert.strictEqual(tracker.report()[0].operator, 'foo');
|
||||
53
test/js/node/test/parallel/test-assert-calltracker-verify.js
Normal file
53
test/js/node/test/parallel/test-assert-calltracker-verify.js
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
// This test ensures that assert.CallTracker.verify() works as intended.
|
||||
|
||||
const tracker = new assert.CallTracker();
|
||||
|
||||
const generic_msg = 'Functions were not called the expected number of times';
|
||||
|
||||
function foo() {}
|
||||
|
||||
function bar() {}
|
||||
|
||||
const callsfoo = tracker.calls(foo, 1);
|
||||
const callsbar = tracker.calls(bar, 1);
|
||||
|
||||
// Expects an error as callsfoo() and callsbar() were called less than one time.
|
||||
assert.throws(
|
||||
() => tracker.verify(),
|
||||
{ message: generic_msg }
|
||||
);
|
||||
|
||||
callsfoo();
|
||||
|
||||
// Expects an error as callsbar() was called less than one time.
|
||||
assert.throws(
|
||||
() => tracker.verify(),
|
||||
{ message: 'Expected the bar function to be executed 1 time(s) but was executed 0 time(s).' }
|
||||
);
|
||||
callsbar();
|
||||
|
||||
// Will throw an error if callsfoo() and callsbar isn't called exactly once.
|
||||
tracker.verify();
|
||||
|
||||
const callsfoobar = tracker.calls(foo, 1);
|
||||
|
||||
callsfoo();
|
||||
|
||||
// Expects an error as callsfoo() was called more than once and callsfoobar() was called less than one time.
|
||||
assert.throws(
|
||||
() => tracker.verify(),
|
||||
{ message: generic_msg }
|
||||
);
|
||||
|
||||
callsfoobar();
|
||||
|
||||
|
||||
// Expects an error as callsfoo() was called more than once
|
||||
assert.throws(
|
||||
() => tracker.verify(),
|
||||
{ message: 'Expected the foo function to be executed 1 time(s) but was executed 2 time(s).' }
|
||||
);
|
||||
Reference in New Issue
Block a user