aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2022-11-28 23:15:12 -0800
committerGravatar GitHub <noreply@github.com> 2022-11-28 23:15:12 -0800
commit56884b5f1dd60a2673ef765a5556bd37d00c1292 (patch)
tree8ab86ac1015bcaf91c5d33851022a325d3e1fcb6
parent887496bcf9bc3e87ca18637f4cd059eecc324102 (diff)
downloadbun-56884b5f1dd60a2673ef765a5556bd37d00c1292.tar.gz
bun-56884b5f1dd60a2673ef765a5556bd37d00c1292.tar.zst
bun-56884b5f1dd60a2673ef765a5556bd37d00c1292.zip
toHaveProperty and tests (#1558)
* toHaveProperty and tests * emoji tests
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp2
-rw-r--r--src/bun.js/bindings/bindings.cpp119
-rw-r--r--src/bun.js/bindings/bindings.zig6
-rw-r--r--src/bun.js/bindings/headers.h3
-rw-r--r--src/bun.js/bindings/headers.zig1
-rw-r--r--src/bun.js/test/jest.classes.ts2
-rw-r--r--src/bun.js/test/jest.zig72
-rw-r--r--test/bun.js/test-test.test.ts445
8 files changed, 532 insertions, 118 deletions
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index f9cffcdce..6a6caab67 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -6065,7 +6065,7 @@ static const HashTableValue JSExpectPrototypeTableValues[] = {
{ "toHaveLastReturnedWith"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveLastReturnedWithCallback, 1 } },
{ "toHaveLength"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveLengthCallback, 1 } },
{ "toHaveNthReturnedWith"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveNthReturnedWithCallback, 1 } },
- { "toHaveProperty"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHavePropertyCallback, 1 } },
+ { "toHaveProperty"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHavePropertyCallback, 2 } },
{ "toHaveReturnedTimes"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveReturnedTimesCallback, 1 } },
{ "toHaveReturnedWith"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveReturnedWithCallback, 1 } },
{ "toMatch"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toMatchCallback, 1 } },
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index b2b0973db..3ff0227f7 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -2609,6 +2609,125 @@ JSC__JSValue JSC__JSValue__getIfPropertyExistsImpl(JSC__JSValue JSValue0,
return JSC::JSValue::encode(object->getIfPropertyExists(globalObject, propertyName));
}
+JSC__JSValue JSC__JSValue__getIfPropertyExistsFromPath(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, JSC__JSValue arg1)
+{
+ VM& vm = globalObject->vm();
+ ThrowScope scope = DECLARE_THROW_SCOPE(vm);
+ JSValue value = JSValue::decode(JSValue0);
+ JSValue path = JSValue::decode(arg1);
+
+ if (path.isString()) {
+ String pathString = path.toWTFString(globalObject);
+ uint32_t length = pathString.length();
+
+ if (length == 0) {
+ JSValue prop = value.toObject(globalObject)->getIfPropertyExists(globalObject, PropertyName(Identifier::EmptyIdentifier));
+ RETURN_IF_EXCEPTION(scope, {});
+ return JSValue::encode(prop);
+ }
+
+ // Jest doesn't check for valid dot/bracket notation. It will skip all "[" and "]", and search for
+ // an empty string for "." when it's the first or last character of the path, or if there are
+ // two in a row.
+
+ JSValue currProp = value;
+ uint32_t i = 0;
+ uint32_t j = 0;
+
+ // if "." is the only character, it will search for an empty string twice.
+ if (pathString.characterAt(0) == '.') {
+ currProp = currProp.toObject(globalObject)->getIfPropertyExists(globalObject, PropertyName(Identifier::EmptyIdentifier));
+ RETURN_IF_EXCEPTION(scope, {});
+ if (currProp.isEmpty()) {
+ return JSValue::encode(currProp);
+ }
+ }
+
+ while (i < length) {
+ UChar ic = pathString.characterAt(i);
+ while (ic == '[' || ic == ']' || ic == '.') {
+ i += 1;
+ if (i == length) {
+
+ if (ic == '.') {
+ currProp = currProp.toObject(globalObject)->getIfPropertyExists(globalObject, PropertyName(Identifier::EmptyIdentifier));
+ RETURN_IF_EXCEPTION(scope, {});
+ return JSValue::encode(currProp);
+ }
+
+ // nothing found.
+ if (j == 0) {
+ return JSValue::encode({});
+ }
+
+ return JSValue::encode(currProp);
+ }
+
+ UChar previous = ic;
+ ic = pathString.characterAt(i);
+ if (previous == '.' && ic == '.') {
+ currProp = currProp.toObject(globalObject)->getIfPropertyExists(globalObject, PropertyName(Identifier::EmptyIdentifier));
+ RETURN_IF_EXCEPTION(scope, {});
+ if (currProp.isEmpty()) {
+ return JSValue::encode(currProp);
+ }
+ continue;
+ }
+ }
+
+ j = i;
+ UChar jc = pathString.characterAt(j);
+ while (!(jc == '[' || jc == ']' || jc == '.')) {
+ j += 1;
+ if (j == length) {
+ // break and search for property
+ break;
+ }
+ jc = pathString.characterAt(j);
+ }
+
+ PropertyName propName = PropertyName(Identifier::fromString(vm, pathString.substring(i, j - i)));
+ currProp = currProp.toObject(globalObject)->getIfPropertyExists(globalObject, propName);
+ RETURN_IF_EXCEPTION(scope, {});
+ if (currProp.isEmpty()) {
+ return JSValue::encode(currProp);
+ }
+
+ i = j;
+ }
+
+ return JSValue::encode(currProp);
+ }
+
+ if (isArray(globalObject, path)) {
+ // each item in array is property name, ignore dot/bracket notation
+ JSValue currProp = value;
+ forEachInArrayLike(globalObject, path.toObject(globalObject), [&](JSValue item) -> bool {
+ if (!(item.isString() || item.isNumber())) {
+ currProp = {};
+ return false;
+ }
+
+ JSString* propNameString = item.toString(globalObject);
+ RETURN_IF_EXCEPTION(scope, false);
+ PropertyName propName = PropertyName(propNameString->toIdentifier(globalObject));
+ RETURN_IF_EXCEPTION(scope, false);
+
+ currProp = currProp.toObject(globalObject)->getIfPropertyExists(globalObject, propName);
+ RETURN_IF_EXCEPTION(scope, false);
+ if (currProp.isEmpty()) {
+ return false;
+ }
+
+ return true;
+ });
+
+ return JSValue::encode(currProp);
+ }
+
+ return JSValue::encode({});
+}
+
void JSC__JSValue__getSymbolDescription(JSC__JSValue symbolValue_, JSC__JSGlobalObject* arg1, ZigString* arg2)
{
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index d28b71b66..ab8286b06 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -3390,6 +3390,10 @@ pub const JSValue = enum(JSValueReprInt) {
return cppFn("getIfPropertyExistsImpl", .{ this, global, ptr, len });
}
+ pub fn getIfPropertyExistsFromPath(this: JSValue, global: *JSGlobalObject, path: JSValue) JSValue {
+ return cppFn("getIfPropertyExistsFromPath", .{ this, global, path });
+ }
+
pub fn getSymbolDescription(this: JSValue, global: *JSGlobalObject, str: *ZigString) void {
cppFn("getSymbolDescription", .{ this, global, str });
}
@@ -3642,7 +3646,7 @@ pub const JSValue = enum(JSValueReprInt) {
return this.asNullableVoid().?;
}
- pub const Extern = [_][]const u8{ "forEachProperty", "coerceToInt32", "fastGet_", "getStaticProperty", "createUninitializedUint8Array", "fromInt64NoTruncate", "fromUInt64NoTruncate", "toUInt64NoTruncate", "asPromise", "toInt64", "_then", "put", "makeWithNameAndPrototype", "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt64", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable", "toBooleanSlow", "deepEquals" };
+ pub const Extern = [_][]const u8{ "forEachProperty", "coerceToInt32", "fastGet_", "getStaticProperty", "createUninitializedUint8Array", "fromInt64NoTruncate", "fromUInt64NoTruncate", "toUInt64NoTruncate", "asPromise", "toInt64", "_then", "put", "makeWithNameAndPrototype", "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt64", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable", "toBooleanSlow", "deepEquals", "getIfPropertyExistsFromPath" };
};
extern "c" fn Microtask__run(*Microtask, *JSGlobalObject) void;
diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h
index 9d335ee90..346cfa50e 100644
--- a/src/bun.js/bindings/headers.h
+++ b/src/bun.js/bindings/headers.h
@@ -1,5 +1,5 @@
// clang-format off
-//-- AUTOGENERATED FILE -- 1669539205
+//-- AUTOGENERATED FILE -- 1669680738
#pragma once
#include <stddef.h>
@@ -497,6 +497,7 @@ CPP_DECL JSC__JSValue JSC__JSValue__fromInt64NoTruncate(JSC__JSGlobalObject* arg
CPP_DECL JSC__JSValue JSC__JSValue__fromUInt64NoTruncate(JSC__JSGlobalObject* arg0, uint64_t arg1);
CPP_DECL void JSC__JSValue__getClassName(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2);
CPP_DECL JSC__JSValue JSC__JSValue__getErrorsProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
+CPP_DECL JSC__JSValue JSC__JSValue__getIfPropertyExistsFromPath(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2);
CPP_DECL JSC__JSValue JSC__JSValue__getIfPropertyExistsImpl(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, const unsigned char* arg2, uint32_t arg3);
CPP_DECL uint64_t JSC__JSValue__getLengthOfArray(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
CPP_DECL void JSC__JSValue__getNameProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2);
diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig
index 20f767d73..55e947f02 100644
--- a/src/bun.js/bindings/headers.zig
+++ b/src/bun.js/bindings/headers.zig
@@ -290,6 +290,7 @@ pub extern fn JSC__JSValue__fromInt64NoTruncate(arg0: ?*JSC__JSGlobalObject, arg
pub extern fn JSC__JSValue__fromUInt64NoTruncate(arg0: ?*JSC__JSGlobalObject, arg1: u64) JSC__JSValue;
pub extern fn JSC__JSValue__getClassName(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject, arg2: [*c]ZigString) void;
pub extern fn JSC__JSValue__getErrorsProperty(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject) JSC__JSValue;
+pub extern fn JSC__JSValue__getIfPropertyExistsFromPath(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject, JSValue2: JSC__JSValue) JSC__JSValue;
pub extern fn JSC__JSValue__getIfPropertyExistsImpl(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject, arg2: [*c]const u8, arg3: u32) JSC__JSValue;
pub extern fn JSC__JSValue__getLengthOfArray(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject) u64;
pub extern fn JSC__JSValue__getNameProperty(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject, arg2: [*c]ZigString) void;
diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts
index ad2a5d6e8..1dcb9ae0a 100644
--- a/src/bun.js/test/jest.classes.ts
+++ b/src/bun.js/test/jest.classes.ts
@@ -102,7 +102,7 @@ export default [
},
toHaveProperty: {
fn: "toHaveProperty",
- length: 1,
+ length: 2,
},
toBeCloseTo: {
fn: "toBeCloseTo",
diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig
index dafb7dc0c..34265789f 100644
--- a/src/bun.js/test/jest.zig
+++ b/src/bun.js/test/jest.zig
@@ -676,6 +676,8 @@ pub const Expect = struct {
}
pub fn toEqual(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ defer this.postMatch(globalObject);
+
const thisValue = callFrame.this();
const _arguments = callFrame.arguments(1);
const arguments: []const JSValue = _arguments.ptr[0.._arguments.len];
@@ -715,7 +717,75 @@ pub const Expect = struct {
return .zero;
}
- pub const toHaveProperty = notImplementedJSCFn;
+ pub fn toHaveProperty(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ defer this.postMatch(globalObject);
+
+ const thisValue = callFrame.this();
+ const _arguments = callFrame.arguments(2);
+ const arguments: []const JSValue = _arguments.ptr[0.._arguments.len];
+
+ if (arguments.len < 1) {
+ globalObject.throwInvalidArguments("toHaveProperty() requires at least 1 argument", .{});
+ return .zero;
+ }
+
+ if (this.scope.tests.items.len <= this.test_id) {
+ globalObject.throw("toHaveProperty must be called in a test", .{});
+ return .zero;
+ }
+
+ active_test_expectation_counter.actual += 1;
+
+ const expected_property_path = arguments[0];
+ expected_property_path.ensureStillAlive();
+ const expected_value: ?JSValue = if (arguments.len > 1) arguments[1] else null;
+ if (expected_value) |ev| ev.ensureStillAlive();
+
+ const value = Expect.capturedValueGetCached(thisValue) orelse {
+ globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{});
+ return .zero;
+ };
+ value.ensureStillAlive();
+
+ if (!expected_property_path.isString() and !expected_property_path.isIterable(globalObject)) {
+ globalObject.throw("Expected path must be a string or an array", .{});
+ return .zero;
+ }
+
+ const not = this.op.contains(.not);
+ var path_string = ZigString.Empty;
+ expected_property_path.toZigString(&path_string, globalObject);
+
+ const expected_property = value.getIfPropertyExistsFromPath(globalObject, expected_property_path);
+
+ var pass = !expected_property.isEmpty();
+
+ if (pass and expected_value != null) {
+ pass = expected_property.deepEquals(expected_value.?, globalObject);
+ }
+
+ if (not) pass = !pass;
+ if (pass) return thisValue;
+
+ // handle failure
+ var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject };
+ if (not) {
+ if (!expected_property.isEmpty() and expected_value != null) {
+ globalObject.throw("Expected property \"{any}\" to not be equal to: {any}", .{ expected_property.toFmt(globalObject, &fmt), expected_value.?.toFmt(globalObject, &fmt) });
+ } else {
+ globalObject.throw("Expected \"{any}\" to not have property: {any}", .{ value.toFmt(globalObject, &fmt), expected_property_path.toFmt(globalObject, &fmt) });
+ }
+ } else {
+ if (!expected_property.isEmpty() and expected_value != null) {
+ globalObject.throw("Expected property \"{any}\" to be equal to: {any}", .{ expected_property.toFmt(globalObject, &fmt), expected_value.?.toFmt(globalObject, &fmt) });
+ } else {
+ globalObject.throw("Expected \"{any}\" to have property: {any}", .{ value.toFmt(globalObject, &fmt), expected_property_path.toFmt(globalObject, &fmt) });
+ }
+ }
+
+ return .zero;
+ }
+
pub const toHaveBeenCalledTimes = notImplementedJSCFn;
pub const toHaveBeenCalledWith = notImplementedJSCFn;
pub const toHaveBeenLastCalledWith = notImplementedJSCFn;
diff --git a/test/bun.js/test-test.test.ts b/test/bun.js/test-test.test.ts
index 79bac9e74..b71f30ff2 100644
--- a/test/bun.js/test-test.test.ts
+++ b/test/bun.js/test-test.test.ts
@@ -648,7 +648,7 @@ test("properties with the same circularity are equal", () => {
expect(c).toEqual(d);
});
-test("arrays", () => {
+test("toEqual() - arrays", () => {
expect([1, 2, 3]).toEqual([1, 2, 3]);
expect([1, 2, 3, 4]).not.toEqual([1, 2, 3]);
});
@@ -899,118 +899,337 @@ test("testing Bun.deepEquals() using isEqual()", () => {
expect(-Infinity).toEqual(-1 / 0);
});
-// test("toHaveProperty()", () => {
-// const a = new Array(["a", "b", "c"]);
-// // expect(a).toHaveProperty("0.1", "b");
-// const b = new Array("a", "b", "c");
-// expect({ a: { b: { c: 1 } } }).toHaveProperty(b);
-// const c = {
-// a: { b: 1 },
-// "a.b": 2,
-// };
-// const d = new Array("a.b");
-// expect(c).toHaveProperty(d, 2);
-// const houseForSale = {
-// bath: true,
-// bedrooms: 4,
-// kitchen: {
-// amenities: ["oven", "stove", "washer"],
-// area: 20,
-// wallColor: "white",
-// "nice.oven": true,
-// },
-// livingroom: {
-// amenities: [
-// {
-// couch: [
-// ["large", { dimensions: [20, 20] }],
-// ["small", { dimensions: [10, 10] }],
-// ],
-// },
-// ],
-// },
-// sunroom: "yes",
-// "ceiling.height": 20,
-// "nono.nooooo": 3,
-// nono: { nooooo: 5 },
-// };
-// expect(houseForSale).toHaveProperty("nono.nooooo");
-// expect(houseForSale).toHaveProperty(["nono", "nooooo"]);
-// expect(houseForSale).toHaveProperty(["nono.nooooo"]);
-// expect(houseForSale).not.toHaveProperty(".");
-// expect(houseForSale).not.toHaveProperty("]");
-// expect(houseForSale).not.toHaveProperty("[");
-// expect(houseForSale).not.toHaveProperty("[]");
-// expect(houseForSale).not.toHaveProperty("[[]]");
-// expect(houseForSale).not.toHaveProperty("[[");
-// expect(houseForSale).not.toHaveProperty("]]");
-// expect(houseForSale).not.toHaveProperty("[]]");
-// expect(houseForSale).not.toHaveProperty("[[]");
-// expect(houseForSale).not.toHaveProperty(".]");
-// expect(houseForSale).not.toHaveProperty(".[");
-// expect(houseForSale).not.toHaveProperty("[.");
-// expect(houseForSale).not.toHaveProperty("].");
-// expect(houseForSale).not.toHaveProperty("].[");
-// expect(houseForSale).not.toHaveProperty("].]");
-// expect(houseForSale).not.toHaveProperty("[.]");
-// expect(houseForSale).not.toHaveProperty("[.[");
-// expect(houseForSale).toHaveProperty("bath");
-// expect(houseForSale).not.toHaveProperty("jacuzzi");
-// // // expect(houseForSale).toHaveProperty("jacuzzi");
-// // // // expect(houseForSale).not.toHaveProperty("bath");
-// expect(houseForSale).toHaveProperty("bath", true);
-// expect(houseForSale).not.toHaveProperty("bath", false);
-// // // // expect(houseForSale).toHaveProperty("bath", false);
-// // // // expect(houseForSale).not.toHaveProperty("bath", true);
-// expect(houseForSale).toHaveProperty("bedrooms", 4);
-// expect(houseForSale).toHaveProperty(["sunroom"], "yes");
-// expect(houseForSale).toHaveProperty("kitchen.area", 20);
-// expect(houseForSale).toHaveProperty("kitchen.amenities", [
-// "oven",
-// "stove",
-// "washer",
-// ]);
-// expect(houseForSale).not.toHaveProperty(["kitchen", "area"], 21);
-// expect(houseForSale).toHaveProperty(["kitchen", "area"], 20);
-// expect(houseForSale).not.toHaveProperty(["kitchen", "area"], 29);
-// expect(houseForSale).toHaveProperty(
-// ["kitchen", "amenities"],
-// ["oven", "stove", "washer"],
-// );
-// expect(houseForSale).toHaveProperty("kitchen.amenities[2]", "washer");
-// expect(houseForSale).toHaveProperty(["kitchen", "amenities", 1], "stove");
-// expect(houseForSale).toHaveProperty(["kitchen", "amenities", 0], "oven");
-// expect(houseForSale).toHaveProperty(
-// "livingroom.amenities[0].couch[0][1].dimensions[0]",
-// 20,
-// );
-// expect(houseForSale).toHaveProperty(["kitchen", "nice.oven"]);
-// expect(houseForSale).not.toHaveProperty(["kitchen", "open"]);
-// expect(houseForSale).toHaveProperty(["ceiling.height"], 20);
-// expect({ a: { b: 1 } }).toHaveProperty("a.b");
-// expect({ a: [2, 3, 4] }).toHaveProperty("a.0");
-// expect({ a: [2, 3, 4] }).toHaveProperty("a.1");
-// expect({ a: [2, 3, 4] }).toHaveProperty("a.2");
-// expect({ a: [2, 3, 4] }).toHaveProperty("a[1]");
-// expect([2, 3, 4]).toHaveProperty("1");
-// expect([2, 3, 4]).toHaveProperty("[1]");
-// expect([2, [6, 9], 4]).toHaveProperty("1.1");
-// expect([2, [6, 9], 4]).toHaveProperty("1[1]");
-// expect([2, [6, 9], 4]).toHaveProperty("[1].1");
-// expect([2, [6, 9], 4]).toHaveProperty("[1][1]");
-// expect([2, [6, 9], 4]).toHaveProperty([0], 2);
-// expect({ a: { b: 1 } }).toHaveProperty("a.b");
-// expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a.2.1.b");
-// expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a");
-// expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a[2][1].b");
-// expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a[2][1]");
-// expect({ a: [1, 2, [3, { b: 1 }]] }).not.toHaveProperty("a[2][1].c");
-// expect("test").toHaveProperty("length");
-// expect({}).toHaveProperty("constructor");
-// expect({}).toHaveProperty("constructor.name");
-// expect({}).toHaveProperty("constructor.name", "Object");
-// expect(new Date()).toHaveProperty("getTime");
-// });
+test("toHaveProperty() - emojis", () => {
+ expect({ "πŸ‘": "thumbs up" }).toHaveProperty("πŸ‘", "thumbs up");
+ expect({ "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘§": "family" }).toHaveProperty("πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘§", "family");
+ expect({ "πŸ˜Άβ€πŸŒ«οΈ": "fog" }).toHaveProperty("πŸ˜Άβ€πŸŒ«οΈ", "fog");
+ expect({ "πŸ‘©β€β€οΈβ€πŸ‘¨": "couple" }).toHaveProperty("πŸ‘©β€β€οΈβ€πŸ‘¨", "couple");
+ expect({ "πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§β€πŸ‘§": "family" }).toHaveProperty("πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§β€πŸ‘§", "family");
+ expect({ "πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§": "family" }).toHaveProperty("πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§", "family");
+ expect({ "πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§": "family" }).not.toHaveProperty("πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§β€πŸ‘§", "family");
+
+ // emojis in array
+ expect(["πŸ‘", "πŸ‘Ž"]).toHaveProperty("0", "πŸ‘");
+ expect(["πŸ‘", "πŸ‘Ž"]).toHaveProperty("1", "πŸ‘Ž");
+ expect(["πŸ‘", "πŸ‘Ž"]).not.toHaveProperty("0", "πŸ‘Ž");
+ expect(["πŸ‘", "πŸ‘Ž"]).not.toHaveProperty("1", "πŸ‘");
+ expect(["πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§β€πŸ‘§"]).toHaveProperty("0", "πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§β€πŸ‘§");
+ expect(["πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§β€πŸ‘§"]).toHaveProperty([0], "πŸ‘©β€β€οΈβ€πŸ‘¨β€πŸ‘¨β€πŸ‘§β€πŸ‘§");
+ expect(["πŸ˜Άβ€πŸŒ«οΈ"]).toHaveProperty([0], "πŸ˜Άβ€πŸŒ«οΈ");
+});
+
+test("toHaveProperty() - dot and bracket notation edge cases", () => {
+ expect({ a: 1 }).not.toHaveProperty(".");
+ expect({ a: 1 }).not.toHaveProperty("]");
+ expect({ a: 1 }).not.toHaveProperty("[");
+ expect({ a: 1 }).not.toHaveProperty("[]");
+ expect({ a: 1 }).not.toHaveProperty("[[]]");
+ expect({ a: 1 }).not.toHaveProperty("[[");
+ expect({ a: 1 }).not.toHaveProperty("]]");
+ expect({ a: 1 }).not.toHaveProperty("[]]");
+ expect({ a: 1 }).not.toHaveProperty("[[]");
+ expect({ a: 1 }).not.toHaveProperty(".]");
+ expect({ a: 1 }).not.toHaveProperty(".[");
+ expect({ "": 1 }).toHaveProperty("[.", 1);
+ expect({ a: 1 }).not.toHaveProperty("[.");
+ expect({ a: 1 }).not.toHaveProperty("].");
+ expect({ a: 1 }).not.toHaveProperty("].[");
+ expect({ a: 1 }).not.toHaveProperty("].]");
+ expect({ a: 1 }).not.toHaveProperty("[.]");
+ expect({ a: 1 }).not.toHaveProperty("[.[");
+
+ expect([1]).toHaveProperty("[0]", 1);
+ expect([1]).toHaveProperty("[0][", 1);
+ expect([1]).toHaveProperty("[0]]", 1);
+ expect([1]).toHaveProperty("[0][[", 1);
+ expect([1]).toHaveProperty("[][[[0]", 1);
+ expect([1]).toHaveProperty("[][[[]][[][][.0", 1);
+ expect([1]).toHaveProperty("[][[[]][[][][.[][[][[[][][0", 1);
+ expect([1]).not.toHaveProperty("......1.............", 1);
+ expect([1]).not.toHaveProperty("......0.............", 1);
+ expect([1]).not.toHaveProperty(".0", 1);
+ expect([1]).not.toHaveProperty("0.", 1);
+ expect([{ "": 1 }]).toHaveProperty("0.", 1);
+ expect({ "": { "": 1 } }).toHaveProperty(".", 1);
+ expect({ "": { "": { "": 1 } } }).toHaveProperty("..", 1);
+ expect({ "": { "": { "": 1 } } }).not.toHaveProperty(".", 1);
+ expect({ "": { "": { "": 1 } } }).not.toHaveProperty("...", 1);
+ expect({ "": { "": { "": 1 } } }).not.toHaveProperty("....", 1);
+ expect([1]).toHaveProperty("0.[[[][][]][[[][[]]]]", 1);
+ expect([1]).not.toHaveProperty("[0].", 1);
+ expect([1]).toHaveProperty("0", 1);
+ expect([1]).toHaveProperty("[].0", 1);
+ expect([1]).toHaveProperty("[.0", 1);
+ expect([1]).toHaveProperty("].0", 1);
+ expect([1]).toHaveProperty("0[]][[[]", 1);
+ expect([1]).toHaveProperty("[[]][[[][][0", 1);
+ expect([1]).toHaveProperty("0", 1);
+ expect([1]).toHaveProperty("0.[", 1);
+ expect([1]).not.toHaveProperty("0........[", 1);
+ expect([1]).not.toHaveProperty("0..[", 1);
+ expect([1]).not.toHaveProperty(".0", 1);
+ expect([1]).toHaveProperty("[].0", 1);
+ expect([1]).not.toHaveProperty("[]..0", 1);
+ expect([1]).toHaveProperty("[.][.[[.]]]]].[.[].].]]]]].].].0", 1);
+ expect([1]).not.toHaveProperty("[.][.[[.]]]]].[.[].].]]0]]].].].", 1);
+ expect([1]).toHaveProperty("[.][.[[.]]]]].[.[].].]]0]]].].]", 1);
+ expect([1]).not.toHaveProperty("[.][.[[..]]]]].[.[].].]]0]]].].]", 1);
+ expect([1]).toHaveProperty("[.][.[[.]]]]].[.[].].0.]]]]].].]", 1);
+ expect([1]).not.toHaveProperty("[.][.[[.]]]]].[.[].].0.]]] ]].].]", 1);
+ expect([1]).not.toHaveProperty("0 ", 1);
+ expect([1]).not.toHaveProperty(" 0 ", 1);
+ expect([1]).not.toHaveProperty(" 0[] ", 1);
+ expect([1]).not.toHaveProperty(" 0] ", 1);
+ expect([1]).not.toHaveProperty(" .[0]", 1);
+
+ expect({ "": 1 }).not.toHaveProperty(".", 1);
+ expect({ "": 1 }).not.toHaveProperty("]", 1);
+ expect({ "": 1 }).not.toHaveProperty("[", 1);
+ expect({ "": 1 }).toHaveProperty("", 1);
+
+ expect({ "": 1 }).not.toHaveProperty("..", 1);
+ expect({ "": { "": 1 } }).not.toHaveProperty("..", 1);
+ expect([{ "": 1 }]).toHaveProperty("0.", 1);
+ expect([{ "": 1 }]).not.toHaveProperty(".0.", 1);
+ expect({ "": [1] }).toHaveProperty(".0", 1);
+ expect({ "": [1] }).not.toHaveProperty("..0", 1);
+ expect([{ "": 1 }]).not.toHaveProperty("0..", 1);
+ expect([{ "": { "": 1 } }]).toHaveProperty("0..", 1);
+
+ expect([1]).not.toHaveProperty("[0].", 1);
+ expect([1]).not.toHaveProperty("[0][0]", 1);
+ expect({ a: [1] }).toHaveProperty("a[[[[[[[[[0]]]", 1);
+ expect({ "[[[": 0 }).not.toHaveProperty("[[[", 0);
+});
+
+test("toHaveProperty() - with string or array", () => {
+ const a = new Array(["a", "b", "c"]);
+ expect(a).toHaveProperty("0.1", "b");
+ const b = new Array("a", "b", "c");
+ expect({ a: { b: { c: 1 } } }).toHaveProperty(b);
+ const c = {
+ a: { b: 1 },
+ "a.b": 2,
+ };
+ const d = new Array("a.b");
+ expect(c).toHaveProperty(d, 2);
+ const houseForSale = {
+ bath: true,
+ bedrooms: 4,
+ kitchen: {
+ amenities: ["oven", "stove", "washer"],
+ area: 20,
+ wallColor: "white",
+ "nice.oven": true,
+ },
+ livingroom: {
+ amenities: [
+ {
+ couch: [
+ ["large", { dimensions: [20, 20] }],
+ ["small", { dimensions: [10, 10] }],
+ ],
+ },
+ ],
+ },
+ sunroom: "yes",
+ "ceiling.height": 20,
+ "entrance.window": 3,
+ entrance: { window: 5 },
+ };
+ expect(houseForSale).toHaveProperty("entrance.window", 5);
+ expect(houseForSale).toHaveProperty(["entrance", "window"], 5);
+ expect(houseForSale).toHaveProperty(["entrance.window"], 3);
+ expect(houseForSale).toHaveProperty("bath");
+ expect(houseForSale).not.toHaveProperty("jacuzzi");
+ // expect(houseForSale).toHaveProperty("jacuzzi");
+ // expect(houseForSale).not.toHaveProperty("bath");
+ expect(houseForSale).toHaveProperty("bath", true);
+ expect(houseForSale).not.toHaveProperty("bath", false);
+ // expect(houseForSale).toHaveProperty("bath", false);
+ // expect(houseForSale).not.toHaveProperty("bath", true);
+ expect(houseForSale).toHaveProperty("bedrooms", 4);
+ expect(houseForSale).toHaveProperty(["sunroom"], "yes");
+ expect(houseForSale).toHaveProperty("kitchen.area", 20);
+ expect(houseForSale).toHaveProperty("kitchen.amenities", [
+ "oven",
+ "stove",
+ "washer",
+ ]);
+ expect(houseForSale).not.toHaveProperty(["kitchen", "area"], 21);
+ expect(houseForSale).toHaveProperty(["kitchen", "area"], 20);
+ expect(houseForSale).not.toHaveProperty(["kitchen", "area"], 29);
+ expect(houseForSale).toHaveProperty(
+ ["kitchen", "amenities"],
+ ["oven", "stove", "washer"],
+ );
+ expect(houseForSale).toHaveProperty("kitchen.amenities[2]", "washer");
+ expect(houseForSale).toHaveProperty(["kitchen", "amenities", 1], "stove");
+ expect(houseForSale).toHaveProperty(["kitchen", "amenities", 0], "oven");
+ expect(houseForSale).toHaveProperty(
+ "livingroom.amenities[0].couch[0][1].dimensions[0]",
+ 20,
+ );
+ expect(houseForSale).toHaveProperty(["kitchen", "nice.oven"]);
+ expect(houseForSale).not.toHaveProperty(["kitchen", "open"]);
+ expect(houseForSale).toHaveProperty(["ceiling.height"], 20);
+ expect({ a: { b: 1 } }).toHaveProperty("a.b");
+ expect({ a: [2, 3, 4] }).toHaveProperty("a.0");
+ expect({ a: [2, 3, 4] }).toHaveProperty("a.1");
+ expect({ a: [2, 3, 4] }).toHaveProperty("a.2");
+ expect({ a: [2, 3, 4] }).toHaveProperty("a[1]");
+ expect([2, 3, 4]).toHaveProperty("1");
+ expect([2, 3, 4]).toHaveProperty("[1]");
+ expect([2, [6, 9], 4]).toHaveProperty("1.1");
+ expect([2, [6, 9], 4]).toHaveProperty("1[1]");
+ expect([2, [6, 9], 4]).toHaveProperty("[1].1");
+ expect([2, [6, 9], 4]).toHaveProperty("[1][1]");
+ expect([2, [6, 9], 4]).toHaveProperty([0], 2);
+ expect({ a: { b: 1 } }).toHaveProperty("a.b");
+ expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a.2.1.b");
+ expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a");
+ expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a[2][1].b");
+ expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a[2][1]");
+ expect({ a: [1, 2, [3, { b: 1 }]] }).not.toHaveProperty("a[2][1].c");
+ expect("test").toHaveProperty("length");
+ expect({}).toHaveProperty("constructor");
+ expect({}).toHaveProperty("constructor.name");
+ expect({}).toHaveProperty("constructor.name", "Object");
+ expect(new Date()).toHaveProperty("getTime");
+});
+
+test("toHaveProperty() - all", () => {
+ expect({ a: 1 }).toHaveProperty("a");
+ expect({ a: 1 }).toHaveProperty("a", 1);
+ expect({ a: 1 }).not.toHaveProperty("b");
+ expect({ a: 1 }).not.toHaveProperty("a", 2);
+
+ // test with object with property "a" with all types of values (including undefined)
+ expect({ a: undefined }).toHaveProperty("a");
+ expect({ a: null }).toHaveProperty("a");
+ expect({ a: 0 }).toHaveProperty("a");
+ expect({ a: false }).toHaveProperty("a");
+ expect({ a: "" }).toHaveProperty("a");
+ expect({ a: {} }).toHaveProperty("a");
+ expect({ a: [] }).toHaveProperty("a");
+ expect({ a: () => {} }).toHaveProperty("a");
+
+ // test with object with property "a" with all types of values (including undefined)
+ expect({ a: undefined }).toHaveProperty("a", undefined);
+ expect({ a: null }).toHaveProperty("a", null);
+ expect({ a: 0 }).toHaveProperty("a", 0);
+ expect({ a: false }).toHaveProperty("a", false);
+ expect({ a: "" }).toHaveProperty("a", "");
+ expect({ a: {} }).toHaveProperty("a", {});
+ expect({ a: [] }).toHaveProperty("a", []);
+ expect({ a: () => {} }).not.toHaveProperty("a", () => {});
+
+ // test with object with property "a" with all types of values (including undefined)
+
+ expect({ a: undefined }).not.toHaveProperty("a", null);
+ expect({ a: null }).not.toHaveProperty("a", undefined);
+ expect({ a: 0 }).not.toHaveProperty("a", null);
+ expect({ a: false }).not.toHaveProperty("a", null);
+ expect({ a: "" }).not.toHaveProperty("a", null);
+ expect({ a: {} }).not.toHaveProperty("a", null);
+ expect({ a: [] }).not.toHaveProperty("a", null);
+ expect({ a: () => {} }).not.toHaveProperty("a", null);
+
+ expect({ a: undefined }).not.toHaveProperty("a", 0);
+ expect({ a: null }).not.toHaveProperty("a", 0);
+ expect({ a: 0 }).not.toHaveProperty("a", 1);
+ expect({ a: false }).not.toHaveProperty("a", 0);
+ expect({ a: "" }).not.toHaveProperty("a", 0);
+ expect({ a: {} }).not.toHaveProperty("a", 0);
+ expect({ a: [] }).not.toHaveProperty("a", 0);
+ expect({ a: () => {} }).not.toHaveProperty("a", 0);
+
+ expect({ a: undefined }).not.toHaveProperty("a", false);
+ expect({ a: null }).not.toHaveProperty("a", false);
+ expect({ a: 0 }).not.toHaveProperty("a", false);
+ expect({ a: false }).not.toHaveProperty("a", true);
+ expect({ a: "" }).not.toHaveProperty("a", false);
+ expect({ a: {} }).not.toHaveProperty("a", false);
+ expect({ a: [] }).not.toHaveProperty("a", false);
+ expect({ a: () => {} }).not.toHaveProperty("a", false);
+
+ expect({ a: undefined }).not.toHaveProperty("a", "");
+ expect({ a: null }).not.toHaveProperty("a", "");
+ expect({ a: 0 }).not.toHaveProperty("a", "");
+ expect({ a: false }).not.toHaveProperty("a", "");
+ expect({ a: "" }).not.toHaveProperty("a", "a");
+ expect({ a: {} }).not.toHaveProperty("a", "");
+ expect({ a: [] }).not.toHaveProperty("a", "");
+ expect({ a: () => {} }).not.toHaveProperty("a", "");
+
+ expect({ a: undefined }).not.toHaveProperty("a", {});
+ expect({ a: null }).not.toHaveProperty("a", {});
+ expect({ a: 0 }).not.toHaveProperty("a", {});
+ expect({ a: false }).not.toHaveProperty("a", {});
+ expect({ a: "" }).not.toHaveProperty("a", {});
+ expect({ a: {} }).not.toHaveProperty("a", { a: 1 });
+ expect({ a: [] }).not.toHaveProperty("a", {});
+ expect({ a: () => {} }).not.toHaveProperty("a", {});
+
+ // test object with property "a" with value set, map, string
+ expect({ a: new Set([1, 2, 3]) }).toHaveProperty("a", new Set([3, 2, 1]));
+ expect({ a: new Map([{ a: 1 }, { b: 2 }, { c: 3 }]) }).toHaveProperty(
+ "a",
+ new Map([{ c: 3 }, { b: 2 }, { a: 1 }]),
+ );
+ expect({ a: new String("a") }).toHaveProperty("a", new String("a"));
+ expect({ a: new String("a") }).not.toHaveProperty("a", "a");
+ expect({ a: new String("a") }).not.toHaveProperty("a", "b");
+ expect({ a: new String("a") }).not.toHaveProperty("a", new String("b"));
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Number(1));
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Boolean(true));
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Boolean(false));
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Object());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Function());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Date());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new RegExp());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Error());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Promise(() => {}));
+ expect({ a: new String("a") }).not.toHaveProperty("a", new WeakSet());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new WeakMap());
+ expect({ a: new String("a") }).not.toHaveProperty("a", Symbol("a"));
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Int8Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Uint8Array());
+ expect({ a: new String("a") }).not.toHaveProperty(
+ "a",
+ new Uint8ClampedArray(),
+ );
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Int16Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Uint16Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Int32Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Uint32Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Float32Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new Float64Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new BigInt64Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new BigUint64Array());
+ expect({ a: new String("a") }).not.toHaveProperty("a", new ArrayBuffer());
+ expect({ a: new String("a") }).not.toHaveProperty(
+ "a",
+ new SharedArrayBuffer(),
+ );
+ expect({ a: new String("a") }).not.toHaveProperty(
+ "a",
+ new DataView(new ArrayBuffer(1)),
+ );
+
+ // test property equality with sets, maps, objects, arrays, and String
+ expect({ a: new Set([1, 2, 3]) }).toHaveProperty("a", new Set([1, 2, 3]));
+ expect({ a: new Map([{ a: 1 }, { b: 2 }, { c: 3 }]) }).toHaveProperty(
+ "a",
+ new Map([{ a: 1 }, { b: 2 }, { c: 3 }]),
+ );
+ expect({ a: { a: 1, b: 2, c: 3 } }).toHaveProperty("a", { a: 1, b: 2, c: 3 });
+ expect({ a: [1, 2, 3] }).toHaveProperty("a", [1, 2, 3]);
+ expect({ a: "a" }).toHaveProperty("a", "a");
+ expect({ a: new String("a") }).toHaveProperty("a", new String("a"));
+ expect({ a: new String("a") }).not.toHaveProperty("a", "a");
+});
test("toBe()", () => {
const a = 1;