diff options
Diffstat (limited to 'src/bun.js/test')
-rw-r--r-- | src/bun.js/test/jest.classes.ts | 24 | ||||
-rw-r--r-- | src/bun.js/test/jest.zig | 387 |
2 files changed, 361 insertions, 50 deletions
diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts index d1d225f64..ad2a5d6e8 100644 --- a/src/bun.js/test/jest.classes.ts +++ b/src/bun.js/test/jest.classes.ts @@ -128,6 +128,30 @@ export default [ fn: "toBeInstanceOf", length: 1, }, + toBeTruthy: { + fn: "toBeTruthy", + length: 0, + }, + toBeUndefined: { + fn: "toBeUndefined", + length: 0, + }, + toBeNaN: { + fn: "toBeNaN", + length: 0, + }, + toBeNull: { + fn: "toBeNull", + length: 0, + }, + toBeFalsy: { + fn: "toBeFalsy", + length: 0, + }, + toBeDefined: { + fn: "toBeDefined", + length: 0, + }, toContain: { fn: "toContain", length: 1, diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 4d67ea97f..3439e46a4 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -37,6 +37,7 @@ const JSPromise = JSC.JSPromise; const JSValue = JSC.JSValue; const JSError = JSC.JSError; const JSGlobalObject = JSC.JSGlobalObject; +const JSObject = JSC.JSObject; const VirtualMachine = @import("../javascript.zig").VirtualMachine; const Task = @import("../javascript.zig").Task; @@ -315,31 +316,29 @@ pub const Expect = struct { return .zero; }; right.ensureStillAlive(); - const eql = left.isSameValue(right, globalObject); + + const not = this.op.contains(.not); + var pass = left.isSameValue(right, globalObject); if (comptime Environment.allow_assert) { - std.debug.assert(eql == JSC.C.JSValueIsStrictEqual(globalObject, left.asObjectRef(), right.asObjectRef())); + std.debug.assert(pass == JSC.C.JSValueIsStrictEqual(globalObject, left.asObjectRef(), right.asObjectRef())); } - if (!eql) { - var lhs_formatter: JSC.ZigConsoleClient.Formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; - var rhs_formatter: JSC.ZigConsoleClient.Formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; - - if (comptime Environment.allow_assert) { - Output.prettyErrorln("\nJSType: {s}\nJSType: {s}\n\n", .{ @tagName(left.jsType()), @tagName(right.jsType()) }); - } - - globalObject.throw( - "Expected: {any}\n\tReceived: {any}", - .{ - left.toFmt(globalObject, &lhs_formatter), - right.toFmt(globalObject, &rhs_formatter), - }, - ); + if (not) pass = !pass; + if (pass) return thisValue; - return .zero; + // handle failure + var lhs_fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + var rhs_fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + if (comptime Environment.allow_assert) { + Output.prettyErrorln("\nJSType: {s}\nJSType: {s}\n\n", .{ @tagName(left.jsType()), @tagName(right.jsType()) }); } - return thisValue; + if (not) { + globalObject.throw("\n\tExpected: not {any}\n\tReceived: {any}", .{ left.toFmt(globalObject, &lhs_fmt), right.toFmt(globalObject, &rhs_fmt) }); + } else { + globalObject.throw("\n\tExpected: {any}\n\tReceived: {any}", .{ left.toFmt(globalObject, &lhs_fmt), right.toFmt(globalObject, &rhs_fmt) }); + } + return .zero; } pub fn toHaveLength( @@ -363,25 +362,304 @@ pub const Expect = struct { active_test_expectation_counter.actual += 1; - const expected = arguments[0].coerce(i32, globalObject); - const value = JSC.Jest.Expect.capturedValueGetCached(thisValue) orelse { + const expected: JSValue = arguments[0]; + const value: JSValue = JSC.Jest.Expect.capturedValueGetCached(thisValue) orelse { globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); return .zero; }; - const actual = value.getLengthOfArray(globalObject); - if (expected != actual) { - globalObject.throw("Expected length to equal {d} but received {d}\n Expected: {d}\n Actual: {d}\n", .{ - expected, - actual, - expected, - actual, - }); + value.ensureStillAlive(); + + if (!value.isObject() and !value.isString()) { + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + globalObject.throw("Received value does not have a length property: {any}", .{value.toFmt(globalObject, &fmt)}); return .zero; } - return thisValue; + if (!expected.isNumber()) { + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + globalObject.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(globalObject, &fmt)}); + return .zero; + } + + const expected_length: f64 = expected.asNumber(); + if (@round(expected_length) != expected_length or std.math.isInf(expected_length) or std.math.isNan(expected_length) or expected_length < 0) { + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + globalObject.throw("Expected value must be a non-negative integer: {any}", .{expected.toFmt(globalObject, &fmt)}); + return .zero; + } + + const not = this.op.contains(.not); + var pass = false; + + var actual_length: f64 = undefined; + if (value.isString()) { + actual_length = @intToFloat(f64, value.asString().length()); + if (actual_length == expected_length) pass = true; + } else { + const length_value: JSValue = value.getIfPropertyExistsImpl(globalObject, "length", "length".len); + + if (length_value.isEmpty()) { + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + globalObject.throw("Received value does not have a length property: {any}", .{value.toFmt(globalObject, &fmt)}); + return .zero; + } else if (!length_value.isNumber()) { + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + globalObject.throw("Received value has non-number length property: {any}", .{length_value.toFmt(globalObject, &fmt)}); + return .zero; + } + + actual_length = length_value.asNumber(); + if (@round(actual_length) == actual_length) { + if (actual_length == expected_length) pass = true; + } + } + + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + if (not) { + globalObject.throw("\n\tExpected: not {d}\n\tReceived: {d}", .{ expected_length, actual_length }); + } else { + globalObject.throw("\n\tExpected: {d}\n\tReceived: {d}", .{ expected_length, actual_length }); + } + return .zero; + } + + pub fn toContain( + this: *Expect, + globalObject: *JSC.JSGlobalObject, + callFrame: *JSC.CallFrame, + ) callconv(.C) JSC.JSValue { + const thisValue = callFrame.this(); + const arguments_ = callFrame.arguments(1); + const arguments = arguments_.ptr[0..arguments_.len]; + + if (arguments.len < 1) { + globalObject.throwInvalidArguments("toContain() takes 1 argument", .{}); + return .zero; + } + + if (this.scope.tests.items.len <= this.test_id) { + globalObject.throw("toContain() must be called in a test", .{}); + return .zero; + } + + active_test_expectation_counter.actual += 1; + + const expected = arguments[0]; + expected.ensureStillAlive(); + const value: JSValue = JSC.Jest.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(); + + const not = this.op.contains(.not); + var pass = false; + + if (value.isIterable(globalObject)) { + var itr = value.arrayIterator(globalObject); + while (itr.next()) |item| { + if (item.isSameValue(expected, globalObject)) { + pass = true; + break; + } + } + } else if (value.isString() and expected.isString()) { + const value_string = value.toString(globalObject).toSlice(globalObject, default_allocator).slice(); + const expected_string = expected.toString(globalObject).toSlice(globalObject, default_allocator).slice(); + if (strings.contains(value_string, expected_string)) { + pass = true; + } + } else { + globalObject.throw("Received value must be an array type, or both received and expected values must be strings.", .{}); + return .zero; + } + + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + if (not) { + globalObject.throw("Expected to not contain \"{any}\"", .{expected.toFmt(globalObject, &fmt)}); + } else { + globalObject.throw("Expected to contain \"{any}\"", .{expected.toFmt(globalObject, &fmt)}); + } + return .zero; } + pub fn toBeTruthy(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + 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("toBeTruthy() must be called in a test", .{}); + return .zero; + } + + active_test_expectation_counter.actual += 1; + + const not = this.op.contains(.not); + var pass = false; + + const truthy = value.toBooleanSlow(globalObject); + if (truthy) pass = true; + + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + if (not) { + globalObject.throw("Expected \"{any}\" to be not truthy.", .{value.toFmt(globalObject, &fmt)}); + } else { + globalObject.throw("Expected \"{any}\" to be truthy.", .{value.toFmt(globalObject, &fmt)}); + } + return .zero; + } + + pub fn toBeUndefined(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const thisValue = callFrame.this(); + const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { + globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + return .zero; + }; + value.ensureStillAlive(); + + active_test_expectation_counter.actual += 1; + + const not = this.op.contains(.not); + var pass = false; + if (value.isUndefined()) pass = true; + + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + if (not) { + globalObject.throw("Expected \"{any}\" to be not undefined.", .{value.toFmt(globalObject, &fmt)}); + } else { + globalObject.throw("Expected \"{any}\" to be undefined.", .{value.toFmt(globalObject, &fmt)}); + } + return .zero; + } + + pub fn toBeNaN(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const thisValue = callFrame.this(); + const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { + globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + return .zero; + }; + value.ensureStillAlive(); + + active_test_expectation_counter.actual += 1; + + const not = this.op.contains(.not); + var pass = false; + if (value.isNumber()) { + const number = value.asNumber(); + if (number != number) pass = true; + } + + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + if (not) { + globalObject.throw("Expected \"{any}\" to be not NaN.", .{value.toFmt(globalObject, &fmt)}); + } else { + globalObject.throw("Expected \"{any}\" to be NaN.", .{value.toFmt(globalObject, &fmt)}); + } + return .zero; + } + + pub fn toBeNull(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const thisValue = callFrame.this(); + const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { + globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + return .zero; + }; + value.ensureStillAlive(); + + active_test_expectation_counter.actual += 1; + + const not = this.op.contains(.not); + var pass = value.isNull(); + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + if (not) { + globalObject.throw("Expected \"{any}\" to be not null.", .{value.toFmt(globalObject, &fmt)}); + } else { + globalObject.throw("Expected \"{any}\" to be null.", .{value.toFmt(globalObject, &fmt)}); + } + return .zero; + } + + pub fn toBeDefined(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const thisValue = callFrame.this(); + const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { + globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + return .zero; + }; + value.ensureStillAlive(); + + active_test_expectation_counter.actual += 1; + + const not = this.op.contains(.not); + var pass = !value.isUndefined(); + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + if (not) { + globalObject.throw("Expected \"{any}\" to be not defined.", .{value.toFmt(globalObject, &fmt)}); + } else { + globalObject.throw("Expected \"{any}\" to be defined.", .{value.toFmt(globalObject, &fmt)}); + } + return .zero; + } + + pub fn toBeFalsy(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + 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(); + + const not = this.op.contains(.not); + var pass = false; + + const truthy = value.toBooleanSlow(globalObject); + if (!truthy) pass = true; + + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + if (not) { + globalObject.throw("Expected \"{any}\" to be not falsy.", .{value.toFmt(globalObject, &fmt)}); + } else { + globalObject.throw("Expected \"{any}\" to be falsy.", .{value.toFmt(globalObject, &fmt)}); + } + return .zero; + } + + pub const toHaveProperty = notImplementedJSCFn; pub const toHaveBeenCalledTimes = notImplementedJSCFn; pub const toHaveBeenCalledWith = notImplementedJSCFn; pub const toHaveBeenLastCalledWith = notImplementedJSCFn; @@ -390,14 +668,12 @@ pub const Expect = struct { pub const toHaveReturnedWith = notImplementedJSCFn; pub const toHaveLastReturnedWith = notImplementedJSCFn; pub const toHaveNthReturnedWith = notImplementedJSCFn; - pub const toHaveProperty = notImplementedJSCFn; pub const toBeCloseTo = notImplementedJSCFn; pub const toBeGreaterThan = notImplementedJSCFn; pub const toBeGreaterThanOrEqual = notImplementedJSCFn; pub const toBeLessThan = notImplementedJSCFn; pub const toBeLessThanOrEqual = notImplementedJSCFn; pub const toBeInstanceOf = notImplementedJSCFn; - pub const toContain = notImplementedJSCFn; pub const toContainEqual = notImplementedJSCFn; pub const toEqual = notImplementedJSCFn; pub const toMatch = notImplementedJSCFn; @@ -409,6 +685,35 @@ pub const Expect = struct { pub const toThrowErrorMatchingSnapshot = notImplementedJSCFn; pub const toThrowErrorMatchingInlineSnapshot = notImplementedJSCFn; + pub const getStaticNot = notImplementedStaticProp; + pub const getStaticResolves = notImplementedStaticProp; + pub const getStaticRejects = notImplementedStaticProp; + + pub fn getNot(this: *Expect, thisValue: JSValue, globalObject: *JSGlobalObject) callconv(.C) JSValue { + _ = Expect.capturedValueGetCached(thisValue) orelse { + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + return .zero; + }; + + this.op.toggle(.not); + + return thisValue; + } + + pub const getResolves = notImplementedJSCProp; + pub const getRejects = notImplementedJSCProp; + + pub const extend = notImplementedStaticFn; + pub const anything = notImplementedStaticFn; + pub const any = notImplementedStaticFn; + pub const arrayContaining = notImplementedStaticFn; + pub const assertions = notImplementedStaticFn; + pub const hasAssertions = notImplementedStaticFn; + pub const objectContaining = notImplementedStaticFn; + pub const stringContaining = notImplementedStaticFn; + pub const stringMatching = notImplementedStaticFn; + pub const addSnapshotSerializer = notImplementedStaticFn; + pub fn notImplementedJSCFn(_: *Expect, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { globalObject.throw("Not implemented", .{}); return .zero; @@ -428,24 +733,6 @@ pub const Expect = struct { globalObject.throw("Not implemented", .{}); return .zero; } - - pub const getStaticNot = notImplementedStaticProp; - pub const getStaticResolves = notImplementedStaticProp; - pub const getStaticRejects = notImplementedStaticProp; - pub const getNot = notImplementedJSCProp; - pub const getResolves = notImplementedJSCProp; - pub const getRejects = notImplementedJSCProp; - - pub const extend = notImplementedStaticFn; - pub const anything = notImplementedStaticFn; - pub const any = notImplementedStaticFn; - pub const arrayContaining = notImplementedStaticFn; - pub const assertions = notImplementedStaticFn; - pub const hasAssertions = notImplementedStaticFn; - pub const objectContaining = notImplementedStaticFn; - pub const stringContaining = notImplementedStaticFn; - pub const stringMatching = notImplementedStaticFn; - pub const addSnapshotSerializer = notImplementedStaticFn; }; pub const TestScope = struct { |