aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp62
-rw-r--r--src/bun.js/bindings/generated_classes.zig6
-rw-r--r--src/bun.js/test/expect.zig200
-rw-r--r--src/bun.js/test/jest.classes.ts8
4 files changed, 276 insertions, 0 deletions
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index 25aca16c4..c2780bd7f 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -2855,6 +2855,9 @@ JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toHaveReturnedWithCallback);
extern "C" EncodedJSValue ExpectPrototype__toInclude(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toIncludeCallback);
+extern "C" EncodedJSValue ExpectPrototype__toIncludeRepeated(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toIncludeRepeatedCallback);
+
extern "C" EncodedJSValue ExpectPrototype__toMatch(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toMatchCallback);
@@ -2867,6 +2870,9 @@ JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toMatchObjectCallback);
extern "C" EncodedJSValue ExpectPrototype__toMatchSnapshot(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toMatchSnapshotCallback);
+extern "C" EncodedJSValue ExpectPrototype__toSatisfy(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toSatisfyCallback);
+
extern "C" EncodedJSValue ExpectPrototype__toStartWith(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toStartWithCallback);
@@ -2939,10 +2945,12 @@ static const HashTableValue JSExpectPrototypeTableValues[] = {
{ "toHaveReturnedTimes"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveReturnedTimesCallback, 1 } },
{ "toHaveReturnedWith"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toHaveReturnedWithCallback, 1 } },
{ "toInclude"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toIncludeCallback, 1 } },
+ { "toIncludeRepeated"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toIncludeRepeatedCallback, 2 } },
{ "toMatch"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toMatchCallback, 1 } },
{ "toMatchInlineSnapshot"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toMatchInlineSnapshotCallback, 1 } },
{ "toMatchObject"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toMatchObjectCallback, 1 } },
{ "toMatchSnapshot"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toMatchSnapshotCallback, 1 } },
+ { "toSatisfy"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toSatisfyCallback, 1 } },
{ "toStartWith"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toStartWithCallback, 1 } },
{ "toStrictEqual"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toStrictEqualCallback, 1 } },
{ "toThrow"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toThrowCallback, 1 } },
@@ -4377,6 +4385,33 @@ JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toIncludeCallback, (JSGlobalObject * l
return ExpectPrototype__toInclude(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toIncludeRepeatedCallback, (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__toIncludeRepeated(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toMatchCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
@@ -4485,6 +4520,33 @@ JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toMatchSnapshotCallback, (JSGlobalObje
return ExpectPrototype__toMatchSnapshot(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toSatisfyCallback, (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__toSatisfy(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__toStartWithCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index e8cc701a3..9cf63a2e1 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -991,6 +991,8 @@ pub const JSExpect = struct {
@compileLog("Expected Expect.toHaveReturnedWith to be a callback but received " ++ @typeName(@TypeOf(Expect.toHaveReturnedWith)));
if (@TypeOf(Expect.toInclude) != CallbackType)
@compileLog("Expected Expect.toInclude to be a callback but received " ++ @typeName(@TypeOf(Expect.toInclude)));
+ if (@TypeOf(Expect.toIncludeRepeated) != CallbackType)
+ @compileLog("Expected Expect.toIncludeRepeated to be a callback but received " ++ @typeName(@TypeOf(Expect.toIncludeRepeated)));
if (@TypeOf(Expect.toMatch) != CallbackType)
@compileLog("Expected Expect.toMatch to be a callback but received " ++ @typeName(@TypeOf(Expect.toMatch)));
if (@TypeOf(Expect.toMatchInlineSnapshot) != CallbackType)
@@ -999,6 +1001,8 @@ pub const JSExpect = struct {
@compileLog("Expected Expect.toMatchObject to be a callback but received " ++ @typeName(@TypeOf(Expect.toMatchObject)));
if (@TypeOf(Expect.toMatchSnapshot) != CallbackType)
@compileLog("Expected Expect.toMatchSnapshot to be a callback but received " ++ @typeName(@TypeOf(Expect.toMatchSnapshot)));
+ if (@TypeOf(Expect.toSatisfy) != CallbackType)
+ @compileLog("Expected Expect.toSatisfy to be a callback but received " ++ @typeName(@TypeOf(Expect.toSatisfy)));
if (@TypeOf(Expect.toStartWith) != CallbackType)
@compileLog("Expected Expect.toStartWith to be a callback but received " ++ @typeName(@TypeOf(Expect.toStartWith)));
if (@TypeOf(Expect.toStrictEqual) != CallbackType)
@@ -1111,10 +1115,12 @@ pub const JSExpect = struct {
@export(Expect.toHaveReturnedTimes, .{ .name = "ExpectPrototype__toHaveReturnedTimes" });
@export(Expect.toHaveReturnedWith, .{ .name = "ExpectPrototype__toHaveReturnedWith" });
@export(Expect.toInclude, .{ .name = "ExpectPrototype__toInclude" });
+ @export(Expect.toIncludeRepeated, .{ .name = "ExpectPrototype__toIncludeRepeated" });
@export(Expect.toMatch, .{ .name = "ExpectPrototype__toMatch" });
@export(Expect.toMatchInlineSnapshot, .{ .name = "ExpectPrototype__toMatchInlineSnapshot" });
@export(Expect.toMatchObject, .{ .name = "ExpectPrototype__toMatchObject" });
@export(Expect.toMatchSnapshot, .{ .name = "ExpectPrototype__toMatchSnapshot" });
+ @export(Expect.toSatisfy, .{ .name = "ExpectPrototype__toSatisfy" });
@export(Expect.toStartWith, .{ .name = "ExpectPrototype__toStartWith" });
@export(Expect.toStrictEqual, .{ .name = "ExpectPrototype__toStrictEqual" });
@export(Expect.toThrow, .{ .name = "ExpectPrototype__toThrow" });
diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig
index c56818efc..de3f185d5 100644
--- a/src/bun.js/test/expect.zig
+++ b/src/bun.js/test/expect.zig
@@ -2812,6 +2812,206 @@ pub const Expect = struct {
return .zero;
}
+ pub fn toIncludeRepeated(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue {
+ defer this.postMatch(globalThis);
+
+ const thisValue = callFrame.this();
+ const arguments_ = callFrame.arguments(2);
+ const arguments = arguments_.ptr[0..arguments_.len];
+
+ if (arguments.len < 2) {
+ globalThis.throwInvalidArguments("toIncludeRepeated() requires 2 arguments", .{});
+ return .zero;
+ }
+
+ if (this.scope.tests.items.len <= this.test_id) {
+ globalThis.throw("toIncludeRepeated() must be called in a test", .{});
+ return .zero;
+ }
+
+ active_test_expectation_counter.actual += 1;
+
+ const substring = arguments[0];
+ substring.ensureStillAlive();
+
+ if (!substring.isString()) {
+ globalThis.throw("toIncludeRepeated() requires the first argument to be a string", .{});
+ return .zero;
+ }
+
+ const count = arguments[1];
+ count.ensureStillAlive();
+
+ if (!count.isAnyInt()) {
+ globalThis.throw("toIncludeRepeated() requires the second argument to be a number", .{});
+ return .zero;
+ }
+
+ const countAsNum = count.toU32();
+
+ const expect_string = Expect.capturedValueGetCached(thisValue) orelse {
+ globalThis.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{});
+ return .zero;
+ };
+
+ if (!expect_string.isString()) {
+ globalThis.throw("toIncludeRepeated() requires the expect(value) to be a string", .{});
+ return .zero;
+ }
+
+ const not = this.flags.not;
+ var pass = false;
+
+ const _expectStringAsStr = expect_string.toSliceOrNull(globalThis) orelse return .zero;
+ const _subStringAsStr = substring.toSliceOrNull(globalThis) orelse return .zero;
+
+ defer {
+ _expectStringAsStr.deinit();
+ _subStringAsStr.deinit();
+ }
+
+ var expectStringAsStr = _expectStringAsStr.slice();
+ var subStringAsStr = _subStringAsStr.slice();
+
+ if (subStringAsStr.len == 0) {
+ globalThis.throw("toIncludeRepeated() requires the first argument to be a non-empty string", .{});
+ return .zero;
+ }
+
+ if (countAsNum == 0)
+ pass = !strings.contains(expectStringAsStr, subStringAsStr)
+ else
+ pass = std.mem.containsAtLeast(u8, expectStringAsStr, countAsNum, subStringAsStr);
+
+ if (not) pass = !pass;
+ if (pass) return thisValue;
+
+ var formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalThis, .quote_strings = true };
+ const expect_string_fmt = expect_string.toFmt(globalThis, &formatter);
+ const substring_fmt = substring.toFmt(globalThis, &formatter);
+ const times_fmt = count.toFmt(globalThis, &formatter);
+
+ const received_line = "Received: <red>{any}<r>\n";
+
+ if (not) {
+ if (countAsNum == 0) {
+ const expected_line = "Expected to include: <green>{any}<r> \n";
+ const fmt = comptime getSignature("toIncludeRepeated", "<green>expected<r>", true) ++ "\n\n" ++ expected_line ++ received_line;
+ globalThis.throwPretty(fmt, .{ substring_fmt, expect_string_fmt });
+ } else if (countAsNum == 1) {
+ const expected_line = "Expected not to include: <green>{any}<r> \n";
+ const fmt = comptime getSignature("toIncludeRepeated", "<green>expected<r>", true) ++ "\n\n" ++ expected_line ++ received_line;
+ globalThis.throwPretty(fmt, .{ substring_fmt, expect_string_fmt });
+ } else {
+ const expected_line = "Expected not to include: <green>{any}<r> <green>{any}<r> times \n";
+ const fmt = comptime getSignature("toIncludeRepeated", "<green>expected<r>", true) ++ "\n\n" ++ expected_line ++ received_line;
+ globalThis.throwPretty(fmt, .{ substring_fmt, times_fmt, expect_string_fmt });
+ }
+
+ return .zero;
+ }
+
+ if (countAsNum == 0) {
+ const expected_line = "Expected to not include: <green>{any}<r>\n";
+ const fmt = comptime getSignature("toIncludeRepeated", "<green>expected<r>", false) ++ "\n\n" ++ expected_line ++ received_line;
+ globalThis.throwPretty(fmt, .{ substring_fmt, expect_string_fmt });
+ } else if (countAsNum == 1) {
+ const expected_line = "Expected to include: <green>{any}<r>\n";
+ const fmt = comptime getSignature("toIncludeRepeated", "<green>expected<r>", false) ++ "\n\n" ++ expected_line ++ received_line;
+ globalThis.throwPretty(fmt, .{ substring_fmt, expect_string_fmt });
+ } else {
+ const expected_line = "Expected to include: <green>{any}<r> <green>{any}<r> times \n";
+ const fmt = comptime getSignature("toIncludeRepeated", "<green>expected<r>", false) ++ "\n\n" ++ expected_line ++ received_line;
+ globalThis.throwPretty(fmt, .{ substring_fmt, times_fmt, expect_string_fmt });
+ }
+
+ return .zero;
+ }
+
+ pub fn toSatisfy(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue {
+ defer this.postMatch(globalThis);
+
+ const thisValue = callFrame.this();
+ const arguments_ = callFrame.arguments(1);
+ const arguments = arguments_.ptr[0..arguments_.len];
+
+ if (arguments.len < 1) {
+ globalThis.throwInvalidArguments("toSatisfy() requires 1 argument", .{});
+ return .zero;
+ }
+
+ if (this.scope.tests.items.len <= this.test_id) {
+ globalThis.throw("toSatisfy() must be called in a test", .{});
+ return .zero;
+ }
+
+ active_test_expectation_counter.actual += 1;
+
+ const predicate = arguments[0];
+ predicate.ensureStillAlive();
+
+ if (!predicate.isCallable(globalThis.vm())) {
+ globalThis.throw("toSatisfy() argument must be a function", .{});
+ return .zero;
+ }
+
+ const value = Expect.capturedValueGetCached(thisValue) orelse {
+ globalThis.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{});
+ return .zero;
+ };
+ value.ensureStillAlive();
+
+ const result = predicate.call(globalThis, &.{value});
+
+ if (result.toError()) |err| {
+ var errors: [1]*anyopaque = undefined;
+ var _err = errors[0..errors.len];
+
+ _err[0] = err.asVoid();
+
+ const fmt = ZigString.init("toSatisfy() predicate threw an exception");
+ globalThis.vm().throwError(globalThis, globalThis.createAggregateError(_err.ptr, _err.len, &fmt));
+ return .zero;
+ }
+
+ const not = this.flags.not;
+ const pass = (result.isBoolean() and result.toBoolean()) != not;
+
+ if (pass) return thisValue;
+
+ var formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalThis, .quote_strings = true };
+
+ if (not) {
+ const signature = comptime getSignature("toSatisfy", "<green>expected<r>", true);
+ const fmt = signature ++ "\n\nExpected: not <green>{any}<r>\n";
+ if (Output.enable_ansi_colors) {
+ globalThis.throw(Output.prettyFmt(fmt, true), .{predicate.toFmt(globalThis, &formatter)});
+ return .zero;
+ }
+ globalThis.throw(Output.prettyFmt(fmt, false), .{predicate.toFmt(globalThis, &formatter)});
+ return .zero;
+ }
+
+ const signature = comptime getSignature("toSatisfy", "<green>expected<r>", false);
+
+ const fmt = signature ++ "\n\nExpected: <green>{any}<r>\nReceived: <red>{any}<r>\n";
+
+ if (Output.enable_ansi_colors) {
+ globalThis.throw(Output.prettyFmt(fmt, true), .{
+ predicate.toFmt(globalThis, &formatter),
+ value.toFmt(globalThis, &formatter),
+ });
+ return .zero;
+ }
+
+ globalThis.throw(Output.prettyFmt(fmt, false), .{
+ predicate.toFmt(globalThis, &formatter),
+ value.toFmt(globalThis, &formatter),
+ });
+
+ return .zero;
+ }
+
pub fn toStartWith(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue {
defer this.postMatch(globalThis);
diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts
index c337ab4ec..d40acbf07 100644
--- a/src/bun.js/test/jest.classes.ts
+++ b/src/bun.js/test/jest.classes.ts
@@ -353,6 +353,14 @@ export default [
fn: "toInclude",
length: 1,
},
+ toIncludeRepeated: {
+ fn: "toIncludeRepeated",
+ length: 2,
+ },
+ toSatisfy: {
+ fn: "toSatisfy",
+ length: 1,
+ },
toStartWith: {
fn: "toStartWith",
length: 1,