aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Tiramify (A.K. Daniel) <94789999+TiranexDev@users.noreply.github.com> 2023-07-29 00:46:44 +0200
committerGravatar GitHub <noreply@github.com> 2023-07-28 15:46:44 -0700
commit242d8655d854c1b818c38d9b021a31d673638e1e (patch)
tree701763879bc87d96aca21aedc78294cbaf89ed0e
parent9b91e3c1a25548217d846932c14e3ccdd0942a99 (diff)
downloadbun-242d8655d854c1b818c38d9b021a31d673638e1e.tar.gz
bun-242d8655d854c1b818c38d9b021a31d673638e1e.tar.zst
bun-242d8655d854c1b818c38d9b021a31d673638e1e.zip
feat(bun/test): Impl. expect().pass() & expect().fail() (#3843)
* Impl. pass & fail * fix * fix 2 * smol
-rw-r--r--packages/bun-types/bun-test.d.ts24
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp62
-rw-r--r--src/bun.js/bindings/generated_classes.zig6
-rw-r--r--src/bun.js/test/expect.zig102
-rw-r--r--src/bun.js/test/jest.classes.ts8
-rw-r--r--test/js/bun/test/jest-extended.test.js15
6 files changed, 212 insertions, 5 deletions
diff --git a/packages/bun-types/bun-test.d.ts b/packages/bun-types/bun-test.d.ts
index 5182f7e93..6877744f2 100644
--- a/packages/bun-types/bun-test.d.ts
+++ b/packages/bun-types/bun-test.d.ts
@@ -469,6 +469,30 @@ declare module "bun:test" {
*/
rejects: Expect<unknown>;
/**
+ * Assertion which passes.
+ *
+ * @link https://jest-extended.jestcommunity.dev/docs/matchers/pass
+ * @example
+ * expect().pass();
+ * expect().pass("message is optional");
+ * expect().not.pass();
+ * expect().not.pass("hi");
+ *
+ * @param message the message to display if the test fails (optional)
+ */
+ pass: (message?: string) => void;
+ /**
+ * Assertion which fails.
+ *
+ * @link https://jest-extended.jestcommunity.dev/docs/matchers/fail
+ * @example
+ * expect().fail();
+ * expect().fail("message is optional");
+ * expect().not.fail();
+ * expect().not.fail("hi");
+ */
+ fail: (message?: string) => void;
+ /**
* Asserts that a value equals what is expected.
*
* - For non-primitive values, like objects and arrays,
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index 245010158..25aca16c4 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -2693,9 +2693,15 @@ JSC_DECLARE_CUSTOM_GETTER(jsExpectConstructor);
extern "C" void ExpectClass__finalize(void*);
extern "C" JSC_DECLARE_HOST_FUNCTION(ExpectClass__call);
+extern "C" EncodedJSValue ExpectPrototype__fail(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__failCallback);
+
extern "C" JSC::EncodedJSValue ExpectPrototype__getNot(void* ptr, JSC::EncodedJSValue thisValue, JSC::JSGlobalObject* lexicalGlobalObject);
JSC_DECLARE_CUSTOM_GETTER(ExpectPrototype__notGetterWrap);
+extern "C" EncodedJSValue ExpectPrototype___pass(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__passCallback);
+
extern "C" JSC::EncodedJSValue ExpectPrototype__getRejects(void* ptr, JSC::EncodedJSValue thisValue, JSC::JSGlobalObject* lexicalGlobalObject);
JSC_DECLARE_CUSTOM_GETTER(ExpectPrototype__rejectsGetterWrap);
@@ -2879,7 +2885,9 @@ JSC_DECLARE_HOST_FUNCTION(ExpectPrototype__toThrowErrorMatchingSnapshotCallback)
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSExpectPrototype, JSExpectPrototype::Base);
static const HashTableValue JSExpectPrototypeTableValues[] = {
+ { "fail"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__failCallback, 1 } },
{ "not"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ExpectPrototype__notGetterWrap, 0 } },
+ { "pass"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__passCallback, 1 } },
{ "rejects"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ExpectPrototype__rejectsGetterWrap, 0 } },
{ "resolves"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ExpectPrototype__resolvesGetterWrap, 0 } },
{ "toBe"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ExpectPrototype__toBeCallback, 1 } },
@@ -2956,6 +2964,33 @@ JSC_DEFINE_CUSTOM_GETTER(jsExpectConstructor, (JSGlobalObject * lexicalGlobalObj
return JSValue::encode(globalObject->JSExpectConstructor());
}
+JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__failCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSExpect* thisObject = jsDynamicCast<JSExpect*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
+ return ExpectPrototype__fail(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_CUSTOM_GETTER(ExpectPrototype__notGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
{
auto& vm = lexicalGlobalObject->vm();
@@ -2968,6 +3003,33 @@ JSC_DEFINE_CUSTOM_GETTER(ExpectPrototype__notGetterWrap, (JSGlobalObject * lexic
RELEASE_AND_RETURN(throwScope, result);
}
+JSC_DEFINE_HOST_FUNCTION(ExpectPrototype__passCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSExpect* thisObject = jsDynamicCast<JSExpect*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(lexicalGlobalObject, throwScope);
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
+ return ExpectPrototype___pass(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_CUSTOM_GETTER(ExpectPrototype__rejectsGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
{
auto& vm = lexicalGlobalObject->vm();
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index 947be7a51..e8cc701a3 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -883,8 +883,12 @@ pub const JSExpect = struct {
@compileLog("Expect.finalize is not a finalizer");
}
+ if (@TypeOf(Expect.fail) != CallbackType)
+ @compileLog("Expected Expect.fail to be a callback but received " ++ @typeName(@TypeOf(Expect.fail)));
if (@TypeOf(Expect.getNot) != GetterTypeWithThisValue)
@compileLog("Expected Expect.getNot to be a getter with thisValue");
+ if (@TypeOf(Expect._pass) != CallbackType)
+ @compileLog("Expected Expect._pass to be a callback but received " ++ @typeName(@TypeOf(Expect._pass)));
if (@TypeOf(Expect.getRejects) != GetterTypeWithThisValue)
@compileLog("Expected Expect.getRejects to be a getter with thisValue");
if (@TypeOf(Expect.getResolves) != GetterTypeWithThisValue)
@@ -1037,6 +1041,7 @@ pub const JSExpect = struct {
if (@TypeOf(Expect.call) != StaticCallbackType)
@compileLog("Expected Expect.call to be a static callback");
if (!JSC.is_bindgen) {
+ @export(Expect._pass, .{ .name = "ExpectPrototype___pass" });
@export(Expect.addSnapshotSerializer, .{ .name = "ExpectClass__addSnapshotSerializer" });
@export(Expect.any, .{ .name = "ExpectClass__any" });
@export(Expect.anything, .{ .name = "ExpectClass__anything" });
@@ -1045,6 +1050,7 @@ pub const JSExpect = struct {
@export(Expect.call, .{ .name = "ExpectClass__call" });
@export(Expect.constructor, .{ .name = "ExpectClass__construct" });
@export(Expect.extend, .{ .name = "ExpectClass__extend" });
+ @export(Expect.fail, .{ .name = "ExpectPrototype__fail" });
@export(Expect.finalize, .{ .name = "ExpectClass__finalize" });
@export(Expect.getNot, .{ .name = "ExpectPrototype__getNot" });
@export(Expect.getRejects, .{ .name = "ExpectPrototype__getRejects" });
diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig
index d6f8ebb12..07fbca03c 100644
--- a/src/bun.js/test/expect.zig
+++ b/src/bun.js/test/expect.zig
@@ -289,6 +289,108 @@ pub const Expect = struct {
return null;
}
+ // pass here has a leading underscore to avoid name collision with the pass variable in other functions
+ pub fn _pass(
+ this: *Expect,
+ globalObject: *JSC.JSGlobalObject,
+ callFrame: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ defer this.postMatch(globalObject);
+
+ const thisValue = callFrame.this();
+ const arguments_ = callFrame.arguments(1);
+ const arguments = arguments_.ptr[0..arguments_.len];
+
+ var _msg: ZigString = ZigString.Empty;
+
+ if (arguments.len > 0) {
+ const value = arguments[0];
+ value.ensureStillAlive();
+
+ if (!value.isString()) {
+ globalObject.throwInvalidArgumentType("pass", "message", "string");
+ return .zero;
+ }
+
+ value.toZigString(&_msg, globalObject);
+ } else {
+ _msg = ZigString.fromBytes("passes by .pass() assertion");
+ }
+
+ active_test_expectation_counter.actual += 1;
+
+ const not = this.flags.not;
+ var pass = true;
+
+ if (not) pass = !pass;
+ if (pass) return thisValue;
+
+ var msg = _msg.toSlice(default_allocator);
+ defer msg.deinit();
+
+ if (not) {
+ const signature = comptime getSignature("pass", "", true);
+ const fmt = signature ++ "\n\n{s}\n";
+ if (Output.enable_ansi_colors) {
+ globalObject.throw(Output.prettyFmt(fmt, true), .{msg.slice()});
+ return .zero;
+ }
+ globalObject.throw(Output.prettyFmt(fmt, false), .{msg.slice()});
+ return .zero;
+ }
+
+ // should never reach here
+ return .zero;
+ }
+
+ pub fn fail(
+ this: *Expect,
+ globalObject: *JSC.JSGlobalObject,
+ callFrame: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ defer this.postMatch(globalObject);
+
+ const thisValue = callFrame.this();
+ const arguments_ = callFrame.arguments(1);
+ const arguments = arguments_.ptr[0..arguments_.len];
+
+ var _msg: ZigString = ZigString.Empty;
+
+ if (arguments.len > 0) {
+ const value = arguments[0];
+ value.ensureStillAlive();
+
+ if (!value.isString()) {
+ globalObject.throwInvalidArgumentType("fail", "message", "string");
+ return .zero;
+ }
+
+ value.toZigString(&_msg, globalObject);
+ } else {
+ _msg = ZigString.fromBytes("fails by .fail() assertion");
+ }
+
+ active_test_expectation_counter.actual += 1;
+
+ const not = this.flags.not;
+ var pass = false;
+
+ if (not) pass = !pass;
+ if (pass) return thisValue;
+
+ var msg = _msg.toSlice(default_allocator);
+ defer msg.deinit();
+
+ const signature = comptime getSignature("fail", "", true);
+ const fmt = signature ++ "\n\n{s}\n";
+ if (Output.enable_ansi_colors) {
+ globalObject.throw(Output.prettyFmt(fmt, true), .{msg.slice()});
+ return .zero;
+ }
+ globalObject.throw(Output.prettyFmt(fmt, false), .{msg.slice()});
+ return .zero;
+ }
+
/// Object.is()
pub fn toBe(
this: *Expect,
diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts
index e5bf567a9..c337ab4ec 100644
--- a/src/bun.js/test/jest.classes.ts
+++ b/src/bun.js/test/jest.classes.ts
@@ -108,6 +108,14 @@ export default [
},
},
proto: {
+ pass: {
+ fn: "_pass",
+ length: 1,
+ },
+ fail: {
+ fn: "fail",
+ length: 1,
+ },
toBe: {
fn: "toBe",
length: 1,
diff --git a/test/js/bun/test/jest-extended.test.js b/test/js/bun/test/jest-extended.test.js
index b03e0828f..5f84914f1 100644
--- a/test/js/bun/test/jest-extended.test.js
+++ b/test/js/bun/test/jest-extended.test.js
@@ -15,17 +15,22 @@ const inspect = isBun ? Bun.inspect : require("util").inspect;
// https://jest-extended.jestcommunity.dev/docs/matchers/
describe("jest-extended", () => {
- test.todo("pass()", () => {
- expect(typeof expect().pass).toBe("function");
- expect(() => expect().not.pass()).toThrow();
+ test("pass()", () => {
+ expect(expect().pass).toBeTypeOf("function");
+ expect(() => expect("ignored value").not.pass()).toThrow("passes by .pass() assertion");
+ expect(() => expect().not.pass("message here")).toThrow("message here");
+ expect(() => expect().pass(1)).toThrow("Expected message to be a string for 'pass'.");
expect().pass();
expect().pass("message ignored");
});
- test.todo("fail()", () => {
- expect(typeof expect().fail).toBe("function");
+ test("fail()", () => {
+ expect(expect().fail).toBeTypeOf("function");
expect(() => expect("ignored value").fail("message here")).toThrow("message here");
+ expect(() => expect().fail()).toThrow("fails by .fail() assertion");
+ expect(() => expect().fail(1)).toThrow("Expected message to be a string for 'fail'.");
expect().not.fail();
+ expect().not.fail("message ignored");
});
describe("toBeEmpty()", () => {