aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp31
-rw-r--r--src/bun.js/bindings/bindings.zig41
-rw-r--r--src/bun.js/bindings/generated_classes.zig3
-rw-r--r--src/bun.js/test/jest.classes.ts21
-rw-r--r--src/bun.js/test/jest.zig89
5 files changed, 176 insertions, 9 deletions
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index d338bcb9c..5482c461f 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -2671,6 +2671,9 @@ JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toBeCloseToCallback);
extern "C" EncodedJSValue ExpectPrototype__toBeDefined(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toBeDefinedCallback);
+extern "C" EncodedJSValue ExpectPrototype__toBeEmpty(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toBeEmptyCallback);
+
extern "C" EncodedJSValue ExpectPrototype__toBeEven(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toBeEvenCallback);
@@ -2779,6 +2782,7 @@ static const HashTableValue JSExpectPrototypeTableValues[] = {
{ "toBe"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toBeCallback, 1 } },
{ "toBeCloseTo"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toBeCloseToCallback, 1 } },
{ "toBeDefined"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toBeDefinedCallback, 0 } },
+ { "toBeEmpty"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toBeEmptyCallback, 0 } },
{ "toBeEven"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toBeEvenCallback, 0 } },
{ "toBeFalsy"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toBeFalsyCallback, 0 } },
{ "toBeGreaterThan"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toBeGreaterThanCallback, 1 } },
@@ -2945,6 +2949,33 @@ JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toBeDefinedCallback, (JSGlobalObject *
return ExpectPrototype__toBeDefined(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toBeEmptyCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSExpect* thisObject = jsDynamicCast<JSExpect*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
+ return ExpectPrototype__toBeEmpty(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toBeEvenCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index 3bf2b9172..99c2306f4 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -3113,6 +3113,10 @@ pub const JSValue = enum(JSValueReprInt) {
pub const LastMaybeFalsyCellPrimitive = JSType.HeapBigInt;
pub const LastJSCObject = JSType.DerivedStringObject; // This is the last "JSC" Object type. After this, we have embedder's (e.g., WebCore) extended object types.
+ pub inline fn isString(this: JSType) bool {
+ return this == .String;
+ }
+
pub inline fn isStringLike(this: JSType) bool {
return switch (this) {
.String, .StringObject, .DerivedStringObject => true,
@@ -3127,6 +3131,42 @@ pub const JSValue = enum(JSValueReprInt) {
};
}
+ pub inline fn isArrayLike(this: JSType) bool {
+ return switch (this) {
+ .Array,
+ .DerivedArray,
+
+ .ArrayBuffer,
+ .BigInt64Array,
+ .BigUint64Array,
+ .Float32Array,
+ .Float64Array,
+ .Int16Array,
+ .Int32Array,
+ .Int8Array,
+ .Uint16Array,
+ .Uint32Array,
+ .Uint8Array,
+ .Uint8ClampedArray,
+ => true,
+ else => false,
+ };
+ }
+
+ pub inline fn isSet(this: JSType) bool {
+ return switch (this) {
+ .JSSet, .JSWeakSet => true,
+ else => false,
+ };
+ }
+
+ pub inline fn isMap(this: JSType) bool {
+ return switch (this) {
+ .JSMap, .JSWeakMap => true,
+ else => false,
+ };
+ }
+
pub inline fn isIndexable(this: JSType) bool {
return switch (this) {
.Object,
@@ -3787,6 +3827,7 @@ pub const JSValue = enum(JSValueReprInt) {
return jsType(this).isStringLike();
}
+
pub fn isBigInt(this: JSValue) bool {
return cppFn("isBigInt", .{this});
}
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index 43acdc98b..a30774aa1 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -882,6 +882,8 @@ pub const JSExpect = struct {
@compileLog("Expected Expect.toBeCloseTo to be a callback but received " ++ @typeName(@TypeOf(Expect.toBeCloseTo)));
if (@TypeOf(Expect.toBeDefined) != CallbackType)
@compileLog("Expected Expect.toBeDefined to be a callback but received " ++ @typeName(@TypeOf(Expect.toBeDefined)));
+ if (@TypeOf(Expect.toBeEmpty) != CallbackType)
+ @compileLog("Expected Expect.toBeEmpty to be a callback but received " ++ @typeName(@TypeOf(Expect.toBeEmpty)));
if (@TypeOf(Expect.toBeEven) != CallbackType)
@compileLog("Expected Expect.toBeEven to be a callback but received " ++ @typeName(@TypeOf(Expect.toBeEven)));
if (@TypeOf(Expect.toBeFalsy) != CallbackType)
@@ -1002,6 +1004,7 @@ pub const JSExpect = struct {
@export(Expect.toBe, .{ .name = "ExpectPrototype__toBe" });
@export(Expect.toBeCloseTo, .{ .name = "ExpectPrototype__toBeCloseTo" });
@export(Expect.toBeDefined, .{ .name = "ExpectPrototype__toBeDefined" });
+ @export(Expect.toBeEmpty, .{ .name = "ExpectPrototype__toBeEmpty" });
@export(Expect.toBeEven, .{ .name = "ExpectPrototype__toBeEven" });
@export(Expect.toBeFalsy, .{ .name = "ExpectPrototype__toBeFalsy" });
@export(Expect.toBeGreaterThan, .{ .name = "ExpectPrototype__toBeGreaterThan" });
diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts
index 612fc0268..bc2dbb1a1 100644
--- a/src/bun.js/test/jest.classes.ts
+++ b/src/bun.js/test/jest.classes.ts
@@ -121,10 +121,6 @@ export default [
fn: "toBeCloseTo",
length: 1,
},
- toBeEven: {
- fn: "toBeEven",
- length: 0,
- },
toBeGreaterThan: {
fn: "toBeGreaterThan",
length: 1,
@@ -141,10 +137,6 @@ export default [
fn: "toBeLessThanOrEqual",
length: 1,
},
- toBeOdd: {
- fn: "toBeOdd",
- length: 0,
- },
toBeInstanceOf: {
fn: "toBeInstanceOf",
length: 1,
@@ -229,6 +221,19 @@ export default [
getter: "getRejects",
this: true,
},
+ // jest-extended
+ toBeEmpty: {
+ fn: "toBeEmpty",
+ length: 0,
+ },
+ toBeEven: {
+ fn: "toBeEven",
+ length: 0,
+ },
+ toBeOdd: {
+ fn: "toBeOdd",
+ length: 0,
+ },
},
}),
];
diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig
index ad9cf9b5b..58a6a3efe 100644
--- a/src/bun.js/test/jest.zig
+++ b/src/bun.js/test/jest.zig
@@ -1304,7 +1304,7 @@ pub const Expect = struct {
const actual_length = value.getLengthIfPropertyExistsInternal(globalObject);
- if (actual_length == std.math.f64_max) {
+ if (actual_length == std.math.inf(f64)) {
var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject, .quote_strings = true };
globalObject.throw("Received value does not have a length property: {any}", .{value.toFmt(globalObject, &fmt)});
return .zero;
@@ -3008,6 +3008,93 @@ pub const Expect = struct {
return thisValue;
}
+ pub fn toBeEmpty(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue {
+ defer this.postMatch(globalObject);
+
+ const thisValue = callFrame.this();
+ const value: JSValue = 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 (this.scope.tests.items.len <= this.test_id) {
+ globalObject.throw("toBeEmpty() must be called in a test", .{});
+ return .zero;
+ }
+
+ active_test_expectation_counter.actual += 1;
+
+ const not = this.op.contains(.not);
+ var pass = false;
+ var formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject, .quote_strings = true };
+
+ const actual_length = value.getLengthIfPropertyExistsInternal(globalObject);
+
+ if (actual_length == std.math.inf(f64)) {
+ if (value.jsTypeLoose().isObject()) {
+ if (value.isIterable(globalObject)) {
+ var any_properties_in_iterator = false;
+ value.forEach(globalObject, &any_properties_in_iterator, struct {
+ pub fn anythingInIterator(
+ _: *JSC.VM,
+ _: *JSGlobalObject,
+ any_: ?*anyopaque,
+ _: JSValue,
+ ) callconv(.C) void {
+ bun.cast(*bool, any_.?).* = true;
+ }
+ }.anythingInIterator);
+ pass = !any_properties_in_iterator;
+ } else {
+ var props_iter = JSC.JSPropertyIterator(.{
+ .skip_empty_name = false,
+
+ .include_value = true,
+ }).init(globalObject, value.asObjectRef());
+ defer props_iter.deinit();
+ pass = props_iter.len == 0;
+ }
+ } else {
+ const signature = comptime getSignature("toBeEmpty", "", false);
+ const fmt = signature ++ "\n\nExpected value to be a string, object, or iterable" ++
+ "\n\nReceived: <red>{any}<r>\n";
+ globalObject.throwPretty(fmt, .{value.toFmt(globalObject, &formatter)});
+ return .zero;
+ }
+ } else if (std.math.isNan(actual_length)) {
+ globalObject.throw("Received value has non-number length property: {}", .{actual_length});
+ return .zero;
+ } else {
+ pass = actual_length == 0;
+ }
+
+ if (not and pass) {
+ const signature = comptime getSignature("toBeEmpty", "", true);
+ const fmt = signature ++ "\n\nExpected value <b>not<r> to be a string, object, or iterable" ++
+ "\n\nReceived: <red>{any}<r>\n";
+ globalObject.throwPretty(fmt, .{value.toFmt(globalObject, &formatter)});
+ return .zero;
+ }
+
+ if (not) pass = !pass;
+ if (pass) return thisValue;
+
+ if (not) {
+ const signature = comptime getSignature("toBeEmpty", "", true);
+ const fmt = signature ++ "\n\nExpected value <b>not<r> to be empty" ++
+ "\n\nReceived: <red>{any}<r>\n";
+ globalObject.throwPretty(fmt, .{value.toFmt(globalObject, &formatter)});
+ return .zero;
+ }
+
+ const signature = comptime getSignature("toBeEmpty", "", false);
+ const fmt = signature ++ "\n\nExpected value to be empty" ++
+ "\n\nReceived: <red>{any}<r>\n";
+ globalObject.throwPretty(fmt, .{value.toFmt(globalObject, &formatter)});
+ return .zero;
+ }
+
pub const PropertyMatcherIterator = struct {
received_object: JSValue,
failed: bool,