aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/test/jest.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/javascript/jsc/test/jest.zig')
-rw-r--r--src/javascript/jsc/test/jest.zig1059
1 files changed, 0 insertions, 1059 deletions
diff --git a/src/javascript/jsc/test/jest.zig b/src/javascript/jsc/test/jest.zig
deleted file mode 100644
index 5ca581d53..000000000
--- a/src/javascript/jsc/test/jest.zig
+++ /dev/null
@@ -1,1059 +0,0 @@
-const std = @import("std");
-const Api = @import("../../../api/schema.zig").Api;
-const RequestContext = @import("../../../http.zig").RequestContext;
-const MimeType = @import("../../../http.zig").MimeType;
-const ZigURL = @import("../../../url.zig").URL;
-const HTTPClient = @import("http");
-const NetworkThread = HTTPClient.NetworkThread;
-const Environment = @import("../../../env.zig");
-
-const JSC = @import("../../../jsc.zig");
-const js = JSC.C;
-
-const logger = @import("../../../logger.zig");
-const Method = @import("../../../http/method.zig").Method;
-
-const ObjectPool = @import("../../../pool.zig").ObjectPool;
-
-const Output = @import("../../../global.zig").Output;
-const MutableString = @import("../../../global.zig").MutableString;
-const strings = @import("../../../global.zig").strings;
-const string = @import("../../../global.zig").string;
-const default_allocator = @import("../../../global.zig").default_allocator;
-const FeatureFlags = @import("../../../global.zig").FeatureFlags;
-const ArrayBuffer = @import("../base.zig").ArrayBuffer;
-const Properties = @import("../base.zig").Properties;
-const NewClass = @import("../base.zig").NewClass;
-const d = @import("../base.zig").d;
-const castObj = @import("../base.zig").castObj;
-const getAllocator = @import("../base.zig").getAllocator;
-const JSPrivateDataPtr = @import("../base.zig").JSPrivateDataPtr;
-const GetJSPrivateData = @import("../base.zig").GetJSPrivateData;
-
-const ZigString = JSC.ZigString;
-const JSInternalPromise = JSC.JSInternalPromise;
-const JSPromise = JSC.JSPromise;
-const JSValue = JSC.JSValue;
-const JSError = JSC.JSError;
-const JSGlobalObject = JSC.JSGlobalObject;
-
-const VirtualMachine = @import("../javascript.zig").VirtualMachine;
-const Task = @import("../javascript.zig").Task;
-
-const Fs = @import("../../../fs.zig");
-const is_bindgen: bool = std.meta.globalOption("bindgen", bool) orelse false;
-
-fn notImplementedFn(_: *anyopaque, ctx: js.JSContextRef, _: js.JSObjectRef, _: js.JSObjectRef, _: []const js.JSValueRef, exception: js.ExceptionRef) js.JSValueRef {
- JSError(getAllocator(ctx), "Not implemented yet!", .{}, ctx, exception);
- return null;
-}
-
-fn notImplementedProp(
- _: anytype,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSStringRef,
- exception: js.ExceptionRef,
-) js.JSValueRef {
- JSError(getAllocator(ctx), "Property not implemented yet!", .{}, ctx, exception);
- return null;
-}
-
-const ArrayIdentityContext = @import("../../../identity_context.zig").ArrayIdentityContext;
-pub const TestRunner = struct {
- tests: TestRunner.Test.List = .{},
- log: *logger.Log,
- files: File.List = .{},
- index: File.Map = File.Map{},
- only: bool = false,
- last_file: u64 = 0,
-
- timeout_seconds: f64 = 5.0,
-
- allocator: std.mem.Allocator,
- callback: *Callback = undefined,
-
- pub fn setOnly(this: *TestRunner) void {
- if (this.only) {
- return;
- }
-
- this.only = true;
- this.tests.shrinkRetainingCapacity(0);
- this.callback.onUpdateCount(this.callback, 0, 0);
- }
-
- pub const Callback = struct {
- pub const OnUpdateCount = fn (this: *Callback, delta: u32, total: u32) void;
- pub const OnTestStart = fn (this: *Callback, test_id: Test.ID) void;
- pub const OnTestUpdate = fn (this: *Callback, test_id: Test.ID, file: string, label: string, expectations: u32, parent: ?*DescribeScope) void;
- onUpdateCount: OnUpdateCount,
- onTestStart: OnTestStart,
- onTestPass: OnTestUpdate,
- onTestFail: OnTestUpdate,
- };
-
- pub fn reportPass(this: *TestRunner, test_id: Test.ID, file: string, label: string, expectations: u32, parent: ?*DescribeScope) void {
- this.tests.items(.status)[test_id] = .pass;
- this.callback.onTestPass(this.callback, test_id, file, label, expectations, parent);
- }
- pub fn reportFailure(this: *TestRunner, test_id: Test.ID, file: string, label: string, expectations: u32, parent: ?*DescribeScope) void {
- this.tests.items(.status)[test_id] = .fail;
- this.callback.onTestFail(this.callback, test_id, file, label, expectations, parent);
- }
-
- pub fn addTestCount(this: *TestRunner, count: u32) u32 {
- this.tests.ensureUnusedCapacity(this.allocator, count) catch unreachable;
- const start = @truncate(Test.ID, this.tests.len);
- this.tests.len += count;
- var statuses = this.tests.items(.status)[start..][0..count];
- std.mem.set(Test.Status, statuses, Test.Status.pending);
- this.callback.onUpdateCount(this.callback, count, count + start);
- return start;
- }
-
- pub fn getOrPutFile(this: *TestRunner, file_path: string) *DescribeScope {
- var entry = this.index.getOrPut(this.allocator, @truncate(u32, std.hash.Wyhash.hash(0, file_path))) catch unreachable;
- if (entry.found_existing) {
- return this.files.items(.module_scope)[entry.value_ptr.*];
- }
- var scope = this.allocator.create(DescribeScope) catch unreachable;
- const file_id = @truncate(File.ID, this.files.len);
- scope.* = DescribeScope{
- .file_id = file_id,
- .test_id_start = @truncate(Test.ID, this.tests.len),
- };
- this.files.append(this.allocator, .{ .module_scope = scope, .source = logger.Source.initEmptyFile(file_path) }) catch unreachable;
- entry.value_ptr.* = file_id;
- return scope;
- }
-
- pub const File = struct {
- source: logger.Source = logger.Source.initEmptyFile(""),
- log: logger.Log = logger.Log.initComptime(default_allocator),
- module_scope: *DescribeScope = undefined,
-
- pub const List = std.MultiArrayList(File);
- pub const ID = u32;
- pub const Map = std.ArrayHashMapUnmanaged(u32, u32, ArrayIdentityContext, false);
- };
-
- pub const Test = struct {
- status: Status = Status.pending,
-
- pub const ID = u32;
- pub const List = std.MultiArrayList(Test);
-
- pub const Status = enum(u3) {
- pending,
- pass,
- fail,
- };
- };
-};
-
-pub const Jest = struct {
- pub var runner: ?*TestRunner = null;
-
- pub fn call(
- _: void,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {
- var runner_ = runner orelse {
- JSError(getAllocator(ctx), "Run bun test to run a test", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- };
-
- if (arguments.len < 1 or !js.JSValueIsString(ctx, arguments[0])) {
- JSError(getAllocator(ctx), "Bun.jest() expects a string filename", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- }
- var str = js.JSValueToStringCopy(ctx, arguments[0], exception);
- defer js.JSStringRelease(str);
- var ptr = js.JSStringGetCharacters8Ptr(str);
- const len = js.JSStringGetLength(str);
- if (len == 0 or ptr[0] != '/') {
- JSError(getAllocator(ctx), "Bun.jest() expects an absolute file path", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- }
- var str_value = ptr[0..len];
- var filepath = Fs.FileSystem.instance.filename_store.append([]const u8, str_value) catch unreachable;
-
- var scope = runner_.getOrPutFile(filepath);
- DescribeScope.active = scope;
-
- return DescribeScope.Class.make(ctx, scope);
- }
-};
-
-/// https://jestjs.io/docs/expect
-// To support async tests, we need to track the test ID
-pub const Expect = struct {
- test_id: TestRunner.Test.ID,
- scope: *DescribeScope,
- value: JSValue,
- op: Op.Set = Op.Set.init(.{}),
-
- pub const Op = enum(u3) {
- resolves,
- rejects,
- not,
- pub const Set = std.EnumSet(Op);
- };
-
- pub fn finalize(
- this: *Expect,
- ) void {
- this.value.unprotect();
- VirtualMachine.vm.allocator.destroy(this);
- }
-
- pub const Class = NewClass(
- Expect,
- .{ .name = "Expect" },
- .{
- .toBe = .{
- .rfn = Expect.toBe,
- .name = "toBe",
- },
- .toHaveBeenCalledTimes = .{
- .rfn = Expect.toHaveBeenCalledTimes,
- .name = "toHaveBeenCalledTimes",
- },
- .finalize = .{ .rfn = Expect.finalize, .name = "finalize" },
- .toHaveBeenCalledWith = .{
- .rfn = Expect.toHaveBeenCalledWith,
- .name = "toHaveBeenCalledWith",
- },
- .toHaveBeenLastCalledWith = .{
- .rfn = Expect.toHaveBeenLastCalledWith,
- .name = "toHaveBeenLastCalledWith",
- },
- .toHaveBeenNthCalledWith = .{
- .rfn = Expect.toHaveBeenNthCalledWith,
- .name = "toHaveBeenNthCalledWith",
- },
- .toHaveReturnedTimes = .{
- .rfn = Expect.toHaveReturnedTimes,
- .name = "toHaveReturnedTimes",
- },
- .toHaveReturnedWith = .{
- .rfn = Expect.toHaveReturnedWith,
- .name = "toHaveReturnedWith",
- },
- .toHaveLastReturnedWith = .{
- .rfn = Expect.toHaveLastReturnedWith,
- .name = "toHaveLastReturnedWith",
- },
- .toHaveNthReturnedWith = .{
- .rfn = Expect.toHaveNthReturnedWith,
- .name = "toHaveNthReturnedWith",
- },
- .toHaveLength = .{
- .rfn = Expect.toHaveLength,
- .name = "toHaveLength",
- },
- .toHaveProperty = .{
- .rfn = Expect.toHaveProperty,
- .name = "toHaveProperty",
- },
- .toBeCloseTo = .{
- .rfn = Expect.toBeCloseTo,
- .name = "toBeCloseTo",
- },
- .toBeGreaterThan = .{
- .rfn = Expect.toBeGreaterThan,
- .name = "toBeGreaterThan",
- },
- .toBeGreaterThanOrEqual = .{
- .rfn = Expect.toBeGreaterThanOrEqual,
- .name = "toBeGreaterThanOrEqual",
- },
- .toBeLessThan = .{
- .rfn = Expect.toBeLessThan,
- .name = "toBeLessThan",
- },
- .toBeLessThanOrEqual = .{
- .rfn = Expect.toBeLessThanOrEqual,
- .name = "toBeLessThanOrEqual",
- },
- .toBeInstanceOf = .{
- .rfn = Expect.toBeInstanceOf,
- .name = "toBeInstanceOf",
- },
- .toContain = .{
- .rfn = Expect.toContain,
- .name = "toContain",
- },
- .toContainEqual = .{
- .rfn = Expect.toContainEqual,
- .name = "toContainEqual",
- },
- .toEqual = .{
- .rfn = Expect.toEqual,
- .name = "toEqual",
- },
- .toMatch = .{
- .rfn = Expect.toMatch,
- .name = "toMatch",
- },
- .toMatchObject = .{
- .rfn = Expect.toMatchObject,
- .name = "toMatchObject",
- },
- .toMatchSnapshot = .{
- .rfn = Expect.toMatchSnapshot,
- .name = "toMatchSnapshot",
- },
- .toMatchInlineSnapshot = .{
- .rfn = Expect.toMatchInlineSnapshot,
- .name = "toMatchInlineSnapshot",
- },
- .toStrictEqual = .{
- .rfn = Expect.toStrictEqual,
- .name = "toStrictEqual",
- },
- .toThrow = .{
- .rfn = Expect.toThrow,
- .name = "toThrow",
- },
- .toThrowErrorMatchingSnapshot = .{
- .rfn = Expect.toThrowErrorMatchingSnapshot,
- .name = "toThrowErrorMatchingSnapshot",
- },
- .toThrowErrorMatchingInlineSnapshot = .{
- .rfn = Expect.toThrowErrorMatchingInlineSnapshot,
- .name = "toThrowErrorMatchingInlineSnapshot",
- },
- },
- .{
- .not = .{
- .get = Expect.not,
- .name = "not",
- },
- .resolves = .{
- .get = Expect.resolves,
- .name = "resolves",
- },
- .rejects = .{
- .get = Expect.rejects,
- .name = "rejects",
- },
- },
- );
-
- /// Object.is()
- pub fn toBe(
- this: *Expect,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- thisObject: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {
- if (arguments.len != 1) {
- JSC.JSError(
- getAllocator(ctx),
- ".toBe() takes 1 argument",
- .{},
- ctx,
- exception,
- );
- return js.JSValueMakeUndefined(ctx);
- }
- this.scope.tests.items[this.test_id].counter.actual += 1;
- const left = JSValue.fromRef(arguments[0]);
- left.ensureStillAlive();
- const right = this.value;
- right.ensureStillAlive();
- const eql = left.isSameValue(right, ctx.ptr());
- if (comptime Environment.allow_assert) {
- std.debug.assert(eql == JSC.C.JSValueIsStrictEqual(ctx, left.asObjectRef(), right.asObjectRef()));
- }
-
- if (!eql) {
- if (comptime Environment.allow_assert) {
- if (left.isString() and right.isString()) {
- var left_slice = left.toSlice(ctx, getAllocator(ctx));
- defer left_slice.deinit();
- var right_slice = right.toSlice(ctx, getAllocator(ctx));
- defer right_slice.deinit();
- std.debug.assert(!strings.eqlLong(left_slice.slice(), right_slice.slice(), true));
- }
- }
-
- var lhs_formatter: JSC.ZigConsoleClient.Formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = ctx.ptr() };
- var rhs_formatter: JSC.ZigConsoleClient.Formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = ctx.ptr() };
-
- if (comptime Environment.allow_assert) {
- Output.prettyErrorln("\nJSType: {s}\nJSType: {s}\n\n", .{ @tagName(left.jsType()), @tagName(right.jsType()) });
- }
-
- JSC.JSError(
- getAllocator(ctx),
- "Expected: {}\n\tReceived: {}",
- .{
- left.toFmt(ctx.ptr(), &lhs_formatter),
- right.toFmt(ctx.ptr(), &rhs_formatter),
- },
- ctx,
- exception,
- );
-
- return null;
- }
-
- return thisObject;
- }
-
- pub fn toHaveLength(
- this: *Expect,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- thisObject: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {
- if (arguments.len != 1) {
- JSC.JSError(
- getAllocator(ctx),
- ".toHaveLength() takes 1 argument",
- .{},
- ctx,
- exception,
- );
- return js.JSValueMakeUndefined(ctx);
- }
- this.scope.tests.items[this.test_id].counter.actual += 1;
-
- const expected = JSC.JSValue.fromRef(arguments[0]).toU32();
- const actual = this.value.getLengthOfArray(ctx.ptr());
- if (expected != actual) {
- JSC.JSError(
- getAllocator(ctx),
- "Expected length to equal {d} but received {d}\n Expected: {d}\n Actual: {d}\n",
- .{
- expected,
- actual,
- expected,
- actual,
- },
- ctx,
- exception,
- );
- return null;
- }
- return thisObject;
- }
-
- pub const toHaveBeenCalledTimes = notImplementedFn;
- pub const toHaveBeenCalledWith = notImplementedFn;
- pub const toHaveBeenLastCalledWith = notImplementedFn;
- pub const toHaveBeenNthCalledWith = notImplementedFn;
- pub const toHaveReturnedTimes = notImplementedFn;
- pub const toHaveReturnedWith = notImplementedFn;
- pub const toHaveLastReturnedWith = notImplementedFn;
- pub const toHaveNthReturnedWith = notImplementedFn;
- pub const toHaveProperty = notImplementedFn;
- pub const toBeCloseTo = notImplementedFn;
- pub const toBeGreaterThan = notImplementedFn;
- pub const toBeGreaterThanOrEqual = notImplementedFn;
- pub const toBeLessThan = notImplementedFn;
- pub const toBeLessThanOrEqual = notImplementedFn;
- pub const toBeInstanceOf = notImplementedFn;
- pub const toContain = notImplementedFn;
- pub const toContainEqual = notImplementedFn;
- pub const toEqual = notImplementedFn;
- pub const toMatch = notImplementedFn;
- pub const toMatchObject = notImplementedFn;
- pub const toMatchSnapshot = notImplementedFn;
- pub const toMatchInlineSnapshot = notImplementedFn;
- pub const toStrictEqual = notImplementedFn;
- pub const toThrow = notImplementedFn;
- pub const toThrowErrorMatchingSnapshot = notImplementedFn;
- pub const toThrowErrorMatchingInlineSnapshot = notImplementedFn;
-
- pub const not = notImplementedProp;
- pub const resolves = notImplementedProp;
- pub const rejects = notImplementedProp;
-};
-
-pub const ExpectPrototype = struct {
- scope: *DescribeScope,
- test_id: TestRunner.Test.ID,
- op: Expect.Op.Set = Expect.Op.Set.init(.{}),
-
- pub const Class = NewClass(
- ExpectPrototype,
- .{
- .name = "ExpectPrototype",
- .read_only = true,
- },
- .{
- .call = .{
- .rfn = ExpectPrototype.call,
- },
- .extend = .{
- .name = "extend",
- .rfn = ExpectPrototype.extend,
- },
- .anything = .{
- .name = "anything",
- .rfn = ExpectPrototype.anything,
- },
- .any = .{
- .name = "any",
- .rfn = ExpectPrototype.any,
- },
- .arrayContaining = .{
- .name = "arrayContaining",
- .rfn = ExpectPrototype.arrayContaining,
- },
- .assertions = .{
- .name = "assertions",
- .rfn = ExpectPrototype.assertions,
- },
- .hasAssertions = .{
- .name = "hasAssertions",
- .rfn = ExpectPrototype.hasAssertions,
- },
- .objectContaining = .{
- .name = "objectContaining",
- .rfn = ExpectPrototype.objectContaining,
- },
- .stringContaining = .{
- .name = "stringContaining",
- .rfn = ExpectPrototype.stringContaining,
- },
- .stringMatching = .{
- .name = "stringMatching",
- .rfn = ExpectPrototype.stringMatching,
- },
- .addSnapshotSerializer = .{
- .name = "addSnapshotSerializer",
- .rfn = ExpectPrototype.addSnapshotSerializer,
- },
- },
- .{
- .not = .{
- .name = "not",
- .get = ExpectPrototype.not,
- },
- .resolves = .{
- .name = "resolves",
- .get = ExpectPrototype.resolves,
- },
- .rejects = .{
- .name = "rejects",
- .get = ExpectPrototype.rejects,
- },
- },
- );
- pub const extend = notImplementedFn;
- pub const anything = notImplementedFn;
- pub const any = notImplementedFn;
- pub const arrayContaining = notImplementedFn;
- pub const assertions = notImplementedFn;
- pub const hasAssertions = notImplementedFn;
- pub const objectContaining = notImplementedFn;
- pub const stringContaining = notImplementedFn;
- pub const stringMatching = notImplementedFn;
- pub const addSnapshotSerializer = notImplementedFn;
- pub const not = notImplementedProp;
- pub const resolves = notImplementedProp;
- pub const rejects = notImplementedProp;
-
- pub fn call(
- _: *ExpectPrototype,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- if (arguments.len != 1) {
- JSError(getAllocator(ctx), "expect() requires one argument", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- }
- var expect_ = getAllocator(ctx).create(Expect) catch unreachable;
- const value = JSC.JSValue.c(arguments[0]);
- value.protect();
- expect_.* = .{
- .value = value,
- .scope = DescribeScope.active,
- .test_id = DescribeScope.active.current_test_id,
- };
- expect_.value.ensureStillAlive();
- return Expect.Class.make(ctx, expect_);
- }
-};
-
-pub const TestScope = struct {
- counter: Counter = Counter{},
- label: string = "",
- parent: *DescribeScope,
- callback: js.JSValueRef,
- id: TestRunner.Test.ID = 0,
- promise: ?*JSInternalPromise = null,
-
- pub const Class = NewClass(void, .{ .name = "test" }, .{ .call = call, .only = only }, .{});
-
- pub const Counter = struct {
- expected: u32 = 0,
- actual: u32 = 0,
- };
-
- pub fn only(
- // the DescribeScope here is the top of the file, not the real one
- _: void,
- ctx: js.JSContextRef,
- this: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return callMaybeOnly(this, ctx, arguments, exception, true);
- }
-
- pub fn call(
- // the DescribeScope here is the top of the file, not the real one
- _: void,
- ctx: js.JSContextRef,
- this: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return callMaybeOnly(this, ctx, arguments, exception, false);
- }
-
- fn callMaybeOnly(
- this: js.JSObjectRef,
- ctx: js.JSContextRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- is_only: bool,
- ) js.JSObjectRef {
- var args = arguments[0..@minimum(arguments.len, 2)];
- var label: string = "";
- if (args.len == 0) {
- return this;
- }
-
- if (js.JSValueIsString(ctx, args[0])) {
- label = (JSC.JSValue.fromRef(arguments[0]).toSlice(ctx, getAllocator(ctx)).cloneIfNeeded() catch unreachable).slice();
- args = args[1..];
- }
-
- var function = args[0];
- if (!js.JSValueIsObject(ctx, function) or !js.JSObjectIsFunction(ctx, function)) {
- JSError(getAllocator(ctx), "test() expects a function", .{}, ctx, exception);
- return this;
- }
-
- if (is_only) {
- Jest.runner.?.setOnly();
- }
-
- if (!is_only and Jest.runner.?.only)
- return this;
-
- js.JSValueProtect(ctx, function);
-
- DescribeScope.active.tests.append(getAllocator(ctx), TestScope{
- .label = label,
- .callback = function,
- .parent = DescribeScope.active,
- }) catch unreachable;
-
- return this;
- }
-
- pub const Result = union(TestRunner.Test.Status) {
- fail: u32,
- pass: u32, // assertion count
- pending: void,
- };
-
- pub fn run(
- this: *TestScope,
- ) Result {
- if (comptime is_bindgen) return undefined;
- var vm = VirtualMachine.vm;
- defer {
- js.JSValueUnprotect(vm.global.ref(), this.callback);
- this.callback = null;
- }
-
- const initial_value = js.JSObjectCallAsFunctionReturnValue(vm.global.ref(), this.callback, null, 0, null);
-
- if (initial_value.isException(vm.global.vm()) or initial_value.isError() or initial_value.isAggregateError(vm.global)) {
- vm.defaultErrorHandler(initial_value, null);
- return .{ .fail = this.counter.actual };
- }
-
- if (!initial_value.isEmptyOrUndefinedOrNull() and (initial_value.asPromise() != null or initial_value.asInternalPromise() != null)) {
- if (this.promise != null) {
- return .{ .pending = .{} };
- }
-
- this.promise = JSC.JSInternalPromise.resolvedPromise(vm.global, initial_value);
- defer {
- this.promise = null;
- }
-
- vm.waitForPromise(this.promise.?);
- switch (this.promise.?.status(vm.global.vm())) {
- .Rejected => {
- vm.defaultErrorHandler(this.promise.?.result(vm.global.vm()), null);
- return .{ .fail = this.counter.actual };
- },
- else => {
- if (this.promise != null)
- // don't care about the result
- _ = this.promise.?.result(vm.global.vm());
- },
- }
- }
-
- this.callback = null;
-
- if (this.counter.expected > 0 and this.counter.expected < this.counter.actual) {
- Output.prettyErrorln("Test fail: {d} / {d} expectations\n (make this better!)", .{
- this.counter.actual,
- this.counter.expected,
- });
- return .{ .fail = this.counter.actual };
- }
-
- return .{ .pass = this.counter.actual };
- }
-};
-
-pub const DescribeScope = struct {
- label: string = "",
- parent: ?*DescribeScope = null,
- beforeAll: std.ArrayListUnmanaged(JSC.JSValue) = .{},
- beforeEach: std.ArrayListUnmanaged(JSC.JSValue) = .{},
- afterEach: std.ArrayListUnmanaged(JSC.JSValue) = .{},
- afterAll: std.ArrayListUnmanaged(JSC.JSValue) = .{},
- test_id_start: TestRunner.Test.ID = 0,
- test_id_len: TestRunner.Test.ID = 0,
- tests: std.ArrayListUnmanaged(TestScope) = .{},
- file_id: TestRunner.File.ID,
- current_test_id: TestRunner.Test.ID = 0,
-
- pub const LifecycleHook = enum {
- beforeAll,
- beforeEach,
- afterEach,
- afterAll,
- };
-
- pub const TestEntry = struct {
- label: string,
- callback: js.JSValueRef,
-
- pub const List = std.MultiArrayList(TestEntry);
- };
-
- pub threadlocal var active: *DescribeScope = undefined;
-
- const CallbackFn = fn (
- this: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef;
- fn createCallback(comptime hook: LifecycleHook) CallbackFn {
- return struct {
- const this_hook = hook;
- pub fn run(
- this: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- if (arguments.len == 0 or !JSC.JSValue.c(arguments[0]).isObject() or !JSC.JSValue.c(arguments[0]).isCallable(ctx.vm())) {
- JSC.throwInvalidArguments("Expected callback", .{}, ctx, exception);
- return null;
- }
-
- JSC.JSValue.c(arguments[0]).protect();
- const name = comptime std.mem.span(@tagName(this_hook));
- @field(this, name).append(getAllocator(ctx), JSC.JSValue.c(arguments[0])) catch unreachable;
- return JSC.JSValue.jsBoolean(true).asObjectRef();
- }
- }.run;
- }
-
- pub const Class = NewClass(
- DescribeScope,
- .{
- .name = "describe",
- .read_only = true,
- },
- .{
- .call = describe,
- .afterAll = .{ .rfn = createCallback(.afterAll), .name = "afterAll" },
- .afterEach = .{ .rfn = createCallback(.afterEach), .name = "afterEach" },
- .beforeAll = .{ .rfn = createCallback(.beforeAll), .name = "beforeAll" },
- .beforeEach = .{ .rfn = createCallback(.beforeEach), .name = "beforeEach" },
- },
- .{
- .expect = .{ .get = createExpect, .name = "expect" },
- // kind of a mindfuck but
- // describe("foo", () => {}).describe("bar") will wrok
- .describe = .{ .get = createDescribe, .name = "describe" },
- .it = .{ .get = createTest, .name = "it" },
- .@"test" = .{ .get = createTest, .name = "test" },
- },
- );
-
- pub fn execCallback(this: *DescribeScope, ctx: js.JSContextRef, comptime hook: LifecycleHook) JSValue {
- const name = comptime std.mem.span(@tagName(hook));
- var hooks: []JSC.JSValue = @field(this, name).items;
- for (hooks) |cb, i| {
- if (cb.isEmpty()) continue;
-
- const err = cb.call(ctx, &.{});
- if (err.isAnyError(ctx)) {
- return err;
- }
-
- if (comptime hook == .beforeAll or hook == .afterAll) {
- hooks[i] = JSC.JSValue.zero;
- }
- }
-
- return JSValue.zero;
- }
- pub fn runCallback(this: *DescribeScope, ctx: js.JSContextRef, comptime hook: LifecycleHook) JSValue {
- var parent = this.parent;
- while (parent) |scope| {
- const ret = scope.execCallback(ctx, hook);
- if (!ret.isEmpty()) {
- return ret;
- }
- parent = scope.parent;
- }
-
- return this.execCallback(ctx, hook);
- }
-
- pub fn describe(
- this: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- if (arguments.len == 0 or arguments.len > 2) {
- JSError(getAllocator(ctx), "describe() requires 1-2 arguments", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- }
-
- var label = ZigString.init("");
- var args = arguments;
-
- if (js.JSValueIsString(ctx, arguments[0])) {
- JSC.JSValue.fromRef(arguments[0]).toZigString(&label, ctx.ptr());
- args = args[1..];
- }
-
- if (args.len == 0 or !js.JSObjectIsFunction(ctx, args[0])) {
- JSError(getAllocator(ctx), "describe() requires a callback function", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- }
-
- var callback = args[0];
-
- var scope = getAllocator(ctx).create(DescribeScope) catch unreachable;
- scope.* = .{
- .label = (label.toSlice(getAllocator(ctx)).cloneIfNeeded() catch unreachable).slice(),
- .parent = this,
- .file_id = this.file_id,
- };
- var new_this = DescribeScope.Class.make(ctx, scope);
-
- return scope.run(new_this, ctx, callback, exception);
- }
-
- pub fn run(this: *DescribeScope, thisObject: js.JSObjectRef, ctx: js.JSContextRef, callback: js.JSObjectRef, exception: js.ExceptionRef) js.JSObjectRef {
- if (comptime is_bindgen) return undefined;
- js.JSValueProtect(ctx, callback);
- defer js.JSValueUnprotect(ctx, callback);
- var original_active = active;
- defer active = original_active;
- active = this;
-
- {
- var result = js.JSObjectCallAsFunctionReturnValue(ctx, callback, thisObject, 0, null);
-
- if (result.asPromise() != null or result.asInternalPromise() != null) {
- var vm = JSC.VirtualMachine.vm;
-
- const promise = JSInternalPromise.resolvedPromise(ctx.ptr(), result);
- while (promise.status(ctx.ptr().vm()) == JSPromise.Status.Pending) {
- vm.tick();
- }
-
- switch (promise.status(ctx.ptr().vm())) {
- JSPromise.Status.Fulfilled => {},
- else => {
- exception.* = promise.result(ctx.ptr().vm()).asObjectRef();
- return null;
- },
- }
- } else if (result.isAnyError(ctx)) {
- exception.* = result.asObjectRef();
- return null;
- }
- }
-
- this.runTests(ctx);
- return js.JSValueMakeUndefined(ctx);
- }
-
- pub fn runTests(this: *DescribeScope, ctx: js.JSContextRef) void {
- // Step 1. Initialize the test block
-
- const file = this.file_id;
- const allocator = getAllocator(ctx);
- var tests: []TestScope = this.tests.items;
- const end = @truncate(TestRunner.Test.ID, tests.len);
-
- if (end == 0) return;
-
- // Step 2. Update the runner with the count of how many tests we have for this block
- this.test_id_start = Jest.runner.?.addTestCount(end);
-
- // Step 3. Run the beforeAll callbacks, in reverse order
- // TODO:
-
- const source: logger.Source = Jest.runner.?.files.items(.source)[file];
-
- var i: TestRunner.Test.ID = 0;
-
- const beforeAll = this.runCallback(ctx, .beforeAll);
- if (!beforeAll.isEmpty()) {
- while (i < end) {
- Jest.runner.?.reportFailure(i + this.test_id_start, source.path.text, tests[i].label, 0, this);
- i += 1;
- }
- this.tests.deinit(allocator);
- return;
- }
-
- while (i < end) {
- // the test array could resize in the middle of this loop
- this.current_test_id = i;
- var test_ = tests[i];
- const beforeEach = this.runCallback(ctx, .beforeEach);
-
- const test_id = i + this.test_id_start;
-
- if (!beforeEach.isEmpty()) {
- Jest.runner.?.reportFailure(test_id, source.path.text, tests[i].label, 0, this);
- ctx.bunVM().defaultErrorHandler(beforeEach, null);
- i += 1;
- continue;
- }
-
- const result = TestScope.run(&test_);
- tests[i] = test_;
-
- switch (result) {
- .pass => |count| Jest.runner.?.reportPass(test_id, source.path.text, tests[i].label, count, this),
- .fail => |count| Jest.runner.?.reportFailure(test_id, source.path.text, tests[i].label, count, this),
- .pending => @panic("Unexpected pending test"),
- }
-
- i += 1;
- }
-
- // invalidate it
- this.current_test_id = std.math.maxInt(TestRunner.Test.ID);
-
- const afterAll = this.execCallback(ctx, .afterAll);
- if (!afterAll.isEmpty()) {
- ctx.bunVM().defaultErrorHandler(afterAll, null);
- }
-
- this.tests.deinit(allocator);
- }
-
- const ScopeStack = ObjectPool(std.ArrayListUnmanaged(*DescribeScope), null, true, 16);
-
- // pub fn runBeforeAll(this: *DescribeScope, ctx: js.JSContextRef, exception: js.ExceptionRef) bool {
- // var scopes = ScopeStack.get(default_allocator);
- // defer scopes.release();
- // scopes.data.clearRetainingCapacity();
- // var cur: ?*DescribeScope = this;
- // while (cur) |scope| {
- // scopes.data.append(default_allocator, this) catch unreachable;
- // cur = scope.parent;
- // }
-
- // // while (scopes.data.popOrNull()) |scope| {
- // // scope.
- // // }
- // }
-
- pub fn runCallbacks(this: *DescribeScope, ctx: js.JSContextRef, callbacks: std.ArrayListUnmanaged(js.JSObjectRef), exception: js.ExceptionRef) bool {
- if (comptime is_bindgen) return undefined;
- var i: usize = 0;
- while (i < callbacks.items.len) : (i += 1) {
- var callback = callbacks.items[i];
- var result = js.JSObjectCallAsFunctionReturnValue(ctx, callback, this, 0);
- if (result.isException(ctx.ptr().vm())) {
- exception.* = result.asObjectRef();
- return false;
- }
- }
- }
-
- pub fn createExpect(
- _: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSValueRef,
- _: js.JSStringRef,
- _: js.ExceptionRef,
- ) js.JSObjectRef {
- var expect_ = getAllocator(ctx).create(ExpectPrototype) catch unreachable;
- expect_.* = .{
- .scope = DescribeScope.active,
- .test_id = DescribeScope.active.current_test_id,
- };
- return ExpectPrototype.Class.make(ctx, expect_);
- }
-
- pub fn createTest(
- _: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSValueRef,
- _: js.JSStringRef,
- _: js.ExceptionRef,
- ) js.JSObjectRef {
- return js.JSObjectMake(ctx, TestScope.Class.get().*, null);
- }
-
- pub fn createDescribe(
- this: *DescribeScope,
- ctx: js.JSContextRef,
- _: js.JSValueRef,
- _: js.JSStringRef,
- _: js.ExceptionRef,
- ) js.JSObjectRef {
- return DescribeScope.Class.make(ctx, this);
- }
-};