diff options
author | 2023-08-09 07:14:30 +0200 | |
---|---|---|
committer | 2023-08-08 22:14:30 -0700 | |
commit | 40d00a961ec4aa4398e18bada520eaf5791151cc (patch) | |
tree | 6d47891914742498df7becf253575d633386919c /src/bun.js/test | |
parent | 1941dbbd71c6d6730ca78b21ef2fd20f51124950 (diff) | |
download | bun-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.zig | 200 | ||||
-rw-r--r-- | src/bun.js/test/jest.classes.ts | 8 |
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, |