Skip to content

Commit

Permalink
feat: add toBeEmptyObject (#7772)
Browse files Browse the repository at this point in the history
* feat: add toBeEmptyObject

* fix: feedbacks

* fix: feedbacks

* fix: feedbacks

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
coratgerl and autofix-ci[bot] authored Jan 3, 2024
1 parent 233622d commit 9f8ee7c
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 2 deletions.
8 changes: 8 additions & 0 deletions packages/bun-types/test.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,14 @@ declare module "bun:test" {
* expect(new Set()).toBeEmpty();
*/
toBeEmpty(): void;
/**
* Asserts that a value is an empty `object`.
*
* @example
* expect({}).toBeEmptyObject();
* expect({ a: 'hello' }).not.toBeEmptyObject();
*/
toBeEmptyObject(): void;
/**
* Asserts that a value is `null` or `undefined`.
*
Expand Down
13 changes: 13 additions & 0 deletions src/bun.js/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2473,12 +2473,25 @@ JSC__JSValue JSC__JSValue__fromEntries(JSC__JSGlobalObject* globalObject, ZigStr
return JSC::JSValue::encode(object);
}


JSC__JSValue JSC__JSValue__keys(JSC__JSGlobalObject* globalObject, JSC__JSValue objectValue) {
JSC::VM& vm = globalObject->vm();

auto scope = DECLARE_THROW_SCOPE(vm);

JSC::JSObject* object = JSC::JSValue::decode(objectValue).toObject(globalObject);
RETURN_IF_EXCEPTION(scope, encodedJSValue());

RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude)));
}

bool JSC__JSValue__hasOwnProperty(JSC__JSValue jsValue, JSC__JSGlobalObject* globalObject, ZigString key) {
JSC::VM& vm = globalObject->vm();


JSC::JSValue value = JSC::JSValue::decode(jsValue);
return value.toObject(globalObject)->hasOwnProperty(globalObject, JSC::PropertyName(JSC::Identifier::fromString(vm, Zig::toString(key))));

}

bool JSC__JSValue__asArrayBuffer_(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1,
Expand Down
23 changes: 21 additions & 2 deletions src/bun.js/bindings/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3980,16 +3980,24 @@ pub const JSValue = enum(JSValueReprInt) {
return String.init(buf.toOwnedSliceLeaky()).toJS(globalThis);
}

pub fn fromEntries(globalThis: *JSGlobalObject, keys: [*c]ZigString, values: [*c]ZigString, strings_count: usize, clone: bool) JSValue {
pub fn fromEntries(globalThis: *JSGlobalObject, keys_array: [*c]ZigString, values: [*c]ZigString, strings_count: usize, clone: bool) JSValue {
return cppFn("fromEntries", .{
globalThis,
keys,
keys_array,
values,
strings_count,
clone,
});
}


pub fn keys(globalThis: *JSGlobalObject, value: JSValue) JSValue {
return cppFn("keys", .{
globalThis,
value,
});
}

pub fn hasOwnProperty(this: JSValue, globalThis: *JSGlobalObject, key: ZigString) bool {
return cppFn("hasOwnProperty", .{ this, globalThis, key });
}
Expand Down Expand Up @@ -4197,6 +4205,16 @@ pub const JSValue = enum(JSValueReprInt) {
pub inline fn isObject(this: JSValue) bool {
return this.isCell() and this.jsType().isObject();
}
pub fn isObjectEmpty(this: JSValue, globalObject: *JSGlobalObject) bool {
const type_of_value = this.jsType();
// https://github.com/jestjs/jest/blob/main/packages/jest-get-type/src/index.ts#L26
// Map and Set are not considered as object in jest-extended
if (type_of_value.isMap() or type_of_value.isSet() or this.isRegExp() or this.isDate()) {
return false;
}

return this.jsType().isObject() and keys(globalObject, this).getLength(globalObject) == 0;
}

pub fn isClass(this: JSValue, global: *JSGlobalObject) bool {
return cppFn("isClass", .{ this, global });
Expand Down Expand Up @@ -5061,6 +5079,7 @@ pub const JSValue = enum(JSValueReprInt) {
"jsType",
"jsUndefined",
"jsonStringify",
"keys",
"kind_",
"makeWithNameAndPrototype",
"parseJSON",
Expand Down
1 change: 1 addition & 0 deletions src/bun.js/bindings/headers.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/bun.js/bindings/headers.zig

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions src/bun.js/test/expect.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2415,6 +2415,34 @@ pub const Expect = struct {
return .zero;
}

pub fn toBeEmptyObject(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue {
defer this.postMatch(globalThis);

const thisValue = callFrame.this();
const value: JSValue = this.getValue(globalThis, thisValue, "toBeEmptyObject", "") orelse return .zero;

incrementExpectCallCounter();

const not = this.flags.not;
var pass = value.isObjectEmpty(globalThis);

if (not) pass = !pass;
if (pass) return thisValue;

var formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalThis, .quote_strings = true };
const received = value.toFmt(globalThis, &formatter);

if (not) {
const fmt = comptime getSignature("toBeEmptyObject", "", true) ++ "\n\n" ++ "Received: <red>{any}<r>\n";
globalThis.throwPretty(fmt, .{received});
return .zero;
}

const fmt = comptime getSignature("toBeEmptyObject", "", false) ++ "\n\n" ++ "Received: <red>{any}<r>\n";
globalThis.throwPretty(fmt, .{received});
return .zero;
}

pub fn toBeNil(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) callconv(.C) JSValue {
defer this.postMatch(globalThis);

Expand Down
4 changes: 4 additions & 0 deletions src/bun.js/test/jest.classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@ export default [
fn: "toBeEmpty",
length: 0,
},
toBeEmptyObject: {
fn: "toBeEmptyObject",
length: 0,
},
toBeEven: {
fn: "toBeEven",
length: 0,
Expand Down
35 changes: 35 additions & 0 deletions test/js/bun/test/expect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3195,6 +3195,41 @@ describe("expect()", () => {
}
});

test("toBeEmptyObject()", () => {
// Map and Set are not considered as object in jest-extended
// https://github.com/jestjs/jest/blob/main/packages/jest-get-type/src/index.ts#L26
expect(new Map().set("a", 1)).not.toBeEmptyObject();
expect(new Map()).not.toBeEmptyObject();
expect(new Set()).not.toBeEmptyObject();
expect(new Set().add("1")).not.toBeEmptyObject();
expect([]).toBeEmptyObject();
expect({}).toBeEmptyObject();
expect([1, 2]).not.toBeEmptyObject();
expect({ a: "hello" }).not.toBeEmptyObject();
expect(true).not.toBeEmptyObject();
expect("notAnObject").not.toBeEmptyObject();

const object1 = {};

Object.defineProperty(object1, "property1", {
value: 42,
});

// Object.keys for non-enumerable properties returns an empty array
// jest-extended returns true for non enumerable object
expect(object1).toBeEmptyObject();

// jest-extended return false for Symbol
expect(Symbol("a")).not.toBeEmptyObject();
expect(Symbol()).not.toBeEmptyObject();

// jest-extended return false for Date
expect(new Date()).not.toBeEmptyObject();

// jest-extended return false for RegExp
expect(/(foo|bar)/g).not.toBeEmptyObject();
});

test("toBeNil()", () => {
expect(null).toBeNil();
expect(undefined).toBeNil();
Expand Down

0 comments on commit 9f8ee7c

Please sign in to comment.