aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/test')
-rw-r--r--src/bun.js/test/jest.classes.ts24
-rw-r--r--src/bun.js/test/jest.zig387
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 {