aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/test
diff options
context:
space:
mode:
authorGravatar Tiramify (A.K. Daniel) <94789999+TiranexDev@users.noreply.github.com> 2023-08-09 07:14:30 +0200
committerGravatar GitHub <noreply@github.com> 2023-08-08 22:14:30 -0700
commit40d00a961ec4aa4398e18bada520eaf5791151cc (patch)
tree6d47891914742498df7becf253575d633386919c /src/bun.js/test
parent1941dbbd71c6d6730ca78b21ef2fd20f51124950 (diff)
downloadbun-40d00a961ec4aa4398e18bada520eaf5791151cc.tar.gz
bun-40d00a961ec4aa4398e18bada520eaf5791151cc.tar.zst
bun-40d00a961ec4aa4398e18bada520eaf5791151cc.zip
feat(bun/test): Implement "toSatisfy" & "toIncludeRepeated" (fwup) (#3651)
* Fix merge issues * oop * make codegen * Fix build issues --------- Co-authored-by: dave caruso <me@paperdave.net>
Diffstat (limited to 'src/bun.js/test')
-rw-r--r--src/bun.js/test/expect.zig200
-rw-r--r--src/bun.js/test/jest.classes.ts8
2 files changed, 208 insertions, 0 deletions
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,