aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2023-06-09 19:26:36 -0700
committerGravatar GitHub <noreply@github.com> 2023-06-09 19:26:36 -0700
commit76cf465cc2e87c400b6bea56cad1f17f94b91da2 (patch)
treed72518cf461407fb3c0b88d8e108057f57c8b5b6
parent0f018ea2159f7bad499d8a2f50837a4d888b2344 (diff)
downloadbun-76cf465cc2e87c400b6bea56cad1f17f94b91da2.tar.gz
bun-76cf465cc2e87c400b6bea56cad1f17f94b91da2.tar.zst
bun-76cf465cc2e87c400b6bea56cad1f17f94b91da2.zip
`toMatchObject` and some asymmetric matchers (#3260)
* `toMatchObject` progress * add `expect.stringContaining()` * add `expect.stringMatching()` * print asymmetric matchers * cleanup * return before printing if constructor value isn't there * move matcher logic to cpp * pretty format and tests * fix formatting for snapshots * format `stringContaining` and `stringMatching` like jest * better test * remove commented tests * remove old property matcher code * add types * make sure all props are matched in arrays * add `Bun.deepMatch`
-rw-r--r--packages/bun-types/bun-test.d.ts13
-rw-r--r--packages/bun-types/bun.d.ts8
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h3
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h3
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h18
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h21
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp457
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.h162
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp35
-rw-r--r--src/bun.js/bindings/bindings.cpp181
-rw-r--r--src/bun.js/bindings/bindings.zig11
-rw-r--r--src/bun.js/bindings/exports.zig37
-rw-r--r--src/bun.js/bindings/generated_classes.zig224
-rw-r--r--src/bun.js/bindings/generated_classes_list.zig3
-rw-r--r--src/bun.js/bindings/headers-handwritten.h2
-rw-r--r--src/bun.js/bindings/headers.h1
-rw-r--r--src/bun.js/bindings/headers.zig1
-rw-r--r--src/bun.js/test/jest.classes.ts35
-rw-r--r--src/bun.js/test/jest.zig305
-rw-r--r--src/bun.js/test/pretty_format.zig46
-rw-r--r--src/http/websocket_http_client.zig1
-rw-r--r--test/js/bun/test/test-test.test.ts209
22 files changed, 1659 insertions, 117 deletions
diff --git a/packages/bun-types/bun-test.d.ts b/packages/bun-types/bun-test.d.ts
index 06d7b7c17..8c429d6fc 100644
--- a/packages/bun-types/bun-test.d.ts
+++ b/packages/bun-types/bun-test.d.ts
@@ -400,6 +400,9 @@ declare module "bun:test" {
any: (
constructor: ((..._: any[]) => any) | { new (..._: any[]): any },
) => Expect;
+ anything: () => Expect;
+ stringContaining: (str: string) => Expect;
+ stringMatching: (regex: RegExp | string) => Expect;
};
/**
* Asserts that a value matches some criteria.
@@ -690,6 +693,16 @@ declare module "bun:test" {
*/
toMatchSnapshot(propertyMatchers?: Object, hint?: string): void;
/**
+ * Asserts that an object matches a subset of properties.
+ *
+ * @example
+ * expect({ a: 1, b: 2 }).toMatchObject({ b: 2 });
+ * expect({ c: new Date(), d: 2 }).toMatchObject({ d: 2 });
+ *
+ * @param subset Subset of properties to match with.
+ */
+ toMatchObject(subset: Object): void;
+ /**
* Asserts that a value is empty.
*
* @example
diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts
index d7d9ffd09..6987a9226 100644
--- a/packages/bun-types/bun.d.ts
+++ b/packages/bun-types/bun.d.ts
@@ -745,6 +745,14 @@ declare module "bun" {
): boolean;
/**
+ * Returns true if all properties in the subset exist in the
+ * other and have equal values.
+ *
+ * This also powers expect().toMatchObject in `bun:test`
+ */
+ export function deepMatch(subset: unknown, a: unknown): boolean;
+
+ /**
* tsconfig.json options supported by Bun
*/
interface TSConfig {
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h
index f59e0e855..b16febcdb 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h
@@ -5,6 +5,9 @@ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForBuildMessageConstructo
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForCryptoHasherConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDirent;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForDirentConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExpect;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExpectConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExpectAny;
+std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExpectAnything;
+std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExpectStringContaining;
+std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExpectStringMatching;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFileSystemRouter;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFileSystemRouterConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForListener;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMD4;
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h
index 3e80b933b..59263e62c 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h
@@ -5,6 +5,9 @@ std::unique_ptr<IsoSubspace> m_subspaceForBuildMessageConstructor;std::unique_pt
std::unique_ptr<IsoSubspace> m_subspaceForCryptoHasherConstructor;std::unique_ptr<IsoSubspace> m_subspaceForDirent;
std::unique_ptr<IsoSubspace> m_subspaceForDirentConstructor;std::unique_ptr<IsoSubspace> m_subspaceForExpect;
std::unique_ptr<IsoSubspace> m_subspaceForExpectConstructor;std::unique_ptr<IsoSubspace> m_subspaceForExpectAny;
+std::unique_ptr<IsoSubspace> m_subspaceForExpectAnything;
+std::unique_ptr<IsoSubspace> m_subspaceForExpectStringContaining;
+std::unique_ptr<IsoSubspace> m_subspaceForExpectStringMatching;
std::unique_ptr<IsoSubspace> m_subspaceForFileSystemRouter;
std::unique_ptr<IsoSubspace> m_subspaceForFileSystemRouterConstructor;std::unique_ptr<IsoSubspace> m_subspaceForListener;
std::unique_ptr<IsoSubspace> m_subspaceForMD4;
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h
index 95a787f5e..4471fbab3 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h
@@ -40,6 +40,24 @@ JSC::Structure* JSExpectAnyStructure() { return m_JSExpectAny.getInitializedOnMa
JSC::LazyClassStructure m_JSExpectAny;
bool hasJSExpectAnySetterValue { false };
mutable JSC::WriteBarrier<JSC::Unknown> m_JSExpectAnySetterValue;
+JSC::Structure* JSExpectAnythingStructure() { return m_JSExpectAnything.getInitializedOnMainThread(this); }
+ JSC::JSObject* JSExpectAnythingConstructor() { return m_JSExpectAnything.constructorInitializedOnMainThread(this); }
+ JSC::JSValue JSExpectAnythingPrototype() { return m_JSExpectAnything.prototypeInitializedOnMainThread(this); }
+ JSC::LazyClassStructure m_JSExpectAnything;
+ bool hasJSExpectAnythingSetterValue { false };
+ mutable JSC::WriteBarrier<JSC::Unknown> m_JSExpectAnythingSetterValue;
+JSC::Structure* JSExpectStringContainingStructure() { return m_JSExpectStringContaining.getInitializedOnMainThread(this); }
+ JSC::JSObject* JSExpectStringContainingConstructor() { return m_JSExpectStringContaining.constructorInitializedOnMainThread(this); }
+ JSC::JSValue JSExpectStringContainingPrototype() { return m_JSExpectStringContaining.prototypeInitializedOnMainThread(this); }
+ JSC::LazyClassStructure m_JSExpectStringContaining;
+ bool hasJSExpectStringContainingSetterValue { false };
+ mutable JSC::WriteBarrier<JSC::Unknown> m_JSExpectStringContainingSetterValue;
+JSC::Structure* JSExpectStringMatchingStructure() { return m_JSExpectStringMatching.getInitializedOnMainThread(this); }
+ JSC::JSObject* JSExpectStringMatchingConstructor() { return m_JSExpectStringMatching.constructorInitializedOnMainThread(this); }
+ JSC::JSValue JSExpectStringMatchingPrototype() { return m_JSExpectStringMatching.prototypeInitializedOnMainThread(this); }
+ JSC::LazyClassStructure m_JSExpectStringMatching;
+ bool hasJSExpectStringMatchingSetterValue { false };
+ mutable JSC::WriteBarrier<JSC::Unknown> m_JSExpectStringMatchingSetterValue;
JSC::Structure* JSFileSystemRouterStructure() { return m_JSFileSystemRouter.getInitializedOnMainThread(this); }
JSC::JSObject* JSFileSystemRouterConstructor() { return m_JSFileSystemRouter.constructorInitializedOnMainThread(this); }
JSC::JSValue JSFileSystemRouterPrototype() { return m_JSFileSystemRouter.prototypeInitializedOnMainThread(this); }
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h
index 8756ed660..4e5a2c1fa 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h
@@ -41,6 +41,24 @@ void GlobalObject::initGeneratedLazyClasses() {
init.setStructure(WebCore::JSExpectAny::createStructure(init.vm, init.global, init.prototype));
});
+ m_JSExpectAnything.initLater(
+ [](LazyClassStructure::Initializer& init) {
+ init.setPrototype(WebCore::JSExpectAnything::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global)));
+ init.setStructure(WebCore::JSExpectAnything::createStructure(init.vm, init.global, init.prototype));
+
+ });
+ m_JSExpectStringContaining.initLater(
+ [](LazyClassStructure::Initializer& init) {
+ init.setPrototype(WebCore::JSExpectStringContaining::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global)));
+ init.setStructure(WebCore::JSExpectStringContaining::createStructure(init.vm, init.global, init.prototype));
+
+ });
+ m_JSExpectStringMatching.initLater(
+ [](LazyClassStructure::Initializer& init) {
+ init.setPrototype(WebCore::JSExpectStringMatching::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global)));
+ init.setStructure(WebCore::JSExpectStringMatching::createStructure(init.vm, init.global, init.prototype));
+
+ });
m_JSFileSystemRouter.initLater(
[](LazyClassStructure::Initializer& init) {
init.setPrototype(WebCore::JSFileSystemRouter::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global)));
@@ -190,6 +208,9 @@ void GlobalObject::visitGeneratedLazyClasses(GlobalObject *thisObject, Visitor&
thisObject->m_JSDirent.visit(visitor); visitor.append(thisObject->m_JSDirentSetterValue);
thisObject->m_JSExpect.visit(visitor); visitor.append(thisObject->m_JSExpectSetterValue);
thisObject->m_JSExpectAny.visit(visitor); visitor.append(thisObject->m_JSExpectAnySetterValue);
+ thisObject->m_JSExpectAnything.visit(visitor); visitor.append(thisObject->m_JSExpectAnythingSetterValue);
+ thisObject->m_JSExpectStringContaining.visit(visitor); visitor.append(thisObject->m_JSExpectStringContainingSetterValue);
+ thisObject->m_JSExpectStringMatching.visit(visitor); visitor.append(thisObject->m_JSExpectStringMatchingSetterValue);
thisObject->m_JSFileSystemRouter.visit(visitor); visitor.append(thisObject->m_JSFileSystemRouterSetterValue);
thisObject->m_JSListener.visit(visitor); visitor.append(thisObject->m_JSListenerSetterValue);
thisObject->m_JSMD4.visit(visitor); visitor.append(thisObject->m_JSMD4SetterValue);
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index 41d9cc888..b98c122ee 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -4831,6 +4831,463 @@ void JSExpectAny::visitOutputConstraintsImpl(JSCell* cell, Visitor& visitor)
}
DEFINE_VISIT_OUTPUT_CONSTRAINTS(JSExpectAny);
+class JSExpectAnythingPrototype final : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+
+ static JSExpectAnythingPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSExpectAnythingPrototype* ptr = new (NotNull, JSC::allocateCell<JSExpectAnythingPrototype>(vm)) JSExpectAnythingPrototype(vm, globalObject, structure);
+ ptr->finishCreation(vm, globalObject);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, JSC::SubspaceAccess>
+ static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ return &vm.plainObjectSpace();
+ }
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+
+private:
+ JSExpectAnythingPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
+};
+
+extern "C" void ExpectAnythingClass__finalize(void*);
+extern "C" JSC_DECLARE_HOST_FUNCTION(ExpectAnythingClass__call);
+
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSExpectAnythingPrototype, JSExpectAnythingPrototype::Base);
+
+static const HashTableValue JSExpectAnythingPrototypeTableValues[] = {};
+
+const ClassInfo JSExpectAnythingPrototype::s_info = { "ExpectAnything"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectAnythingPrototype) };
+
+void JSExpectAnythingPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
+{
+ Base::finishCreation(vm);
+
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+JSExpectAnything::~JSExpectAnything()
+{
+ if (m_ctx) {
+ ExpectAnythingClass__finalize(m_ctx);
+ }
+}
+void JSExpectAnything::destroy(JSCell* cell)
+{
+ static_cast<JSExpectAnything*>(cell)->JSExpectAnything::~JSExpectAnything();
+}
+
+const ClassInfo JSExpectAnything::s_info = { "ExpectAnything"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectAnything) };
+
+void JSExpectAnything::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+}
+
+JSExpectAnything* JSExpectAnything::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx)
+{
+ JSExpectAnything* ptr = new (NotNull, JSC::allocateCell<JSExpectAnything>(vm)) JSExpectAnything(vm, structure, ctx);
+ ptr->finishCreation(vm);
+ return ptr;
+}
+
+extern "C" void* ExpectAnything__fromJS(JSC::EncodedJSValue value)
+{
+ JSC::JSValue decodedValue = JSC::JSValue::decode(value);
+ if (decodedValue.isEmpty() || !decodedValue.isCell())
+ return nullptr;
+
+ JSC::JSCell* cell = decodedValue.asCell();
+ JSExpectAnything* object = JSC::jsDynamicCast<JSExpectAnything*>(cell);
+
+ if (!object)
+ return nullptr;
+
+ return object->wrapped();
+}
+
+extern "C" bool ExpectAnything__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr)
+{
+ JSExpectAnything* object = JSC::jsDynamicCast<JSExpectAnything*>(JSValue::decode(value));
+ if (!object)
+ return false;
+
+ object->m_ctx = ptr;
+ return true;
+}
+
+extern "C" const size_t ExpectAnything__ptrOffset = JSExpectAnything::offsetOfWrapped();
+
+void JSExpectAnything::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
+{
+ auto* thisObject = jsCast<JSExpectAnything*>(cell);
+ if (void* wrapped = thisObject->wrapped()) {
+ // if (thisObject->scriptExecutionContext())
+ // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string());
+ }
+ Base::analyzeHeap(cell, analyzer);
+}
+
+JSObject* JSExpectAnything::createPrototype(VM& vm, JSDOMGlobalObject* globalObject)
+{
+ return JSExpectAnythingPrototype::create(vm, globalObject, JSExpectAnythingPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
+}
+
+extern "C" EncodedJSValue ExpectAnything__create(Zig::GlobalObject* globalObject, void* ptr)
+{
+ auto& vm = globalObject->vm();
+ JSC::Structure* structure = globalObject->JSExpectAnythingStructure();
+ JSExpectAnything* instance = JSExpectAnything::create(vm, globalObject, structure, ptr);
+
+ return JSValue::encode(instance);
+}
+class JSExpectStringContainingPrototype final : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+
+ static JSExpectStringContainingPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSExpectStringContainingPrototype* ptr = new (NotNull, JSC::allocateCell<JSExpectStringContainingPrototype>(vm)) JSExpectStringContainingPrototype(vm, globalObject, structure);
+ ptr->finishCreation(vm, globalObject);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, JSC::SubspaceAccess>
+ static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ return &vm.plainObjectSpace();
+ }
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+
+private:
+ JSExpectStringContainingPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
+};
+
+extern "C" void ExpectStringContainingClass__finalize(void*);
+extern "C" JSC_DECLARE_HOST_FUNCTION(ExpectStringContainingClass__call);
+
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSExpectStringContainingPrototype, JSExpectStringContainingPrototype::Base);
+
+static const HashTableValue JSExpectStringContainingPrototypeTableValues[] = {};
+
+const ClassInfo JSExpectStringContainingPrototype::s_info = { "ExpectStringContaining"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectStringContainingPrototype) };
+
+extern "C" void ExpectStringContainingPrototype__stringValueSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value)
+{
+ auto& vm = globalObject->vm();
+ auto* thisObject = jsCast<JSExpectStringContaining*>(JSValue::decode(thisValue));
+ thisObject->m_stringValue.set(vm, thisObject, JSValue::decode(value));
+}
+
+extern "C" EncodedJSValue ExpectStringContainingPrototype__stringValueGetCachedValue(JSC::EncodedJSValue thisValue)
+{
+ auto* thisObject = jsCast<JSExpectStringContaining*>(JSValue::decode(thisValue));
+ return JSValue::encode(thisObject->m_stringValue.get());
+}
+
+void JSExpectStringContainingPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
+{
+ Base::finishCreation(vm);
+
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+JSExpectStringContaining::~JSExpectStringContaining()
+{
+ if (m_ctx) {
+ ExpectStringContainingClass__finalize(m_ctx);
+ }
+}
+void JSExpectStringContaining::destroy(JSCell* cell)
+{
+ static_cast<JSExpectStringContaining*>(cell)->JSExpectStringContaining::~JSExpectStringContaining();
+}
+
+const ClassInfo JSExpectStringContaining::s_info = { "ExpectStringContaining"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectStringContaining) };
+
+void JSExpectStringContaining::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+}
+
+JSExpectStringContaining* JSExpectStringContaining::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx)
+{
+ JSExpectStringContaining* ptr = new (NotNull, JSC::allocateCell<JSExpectStringContaining>(vm)) JSExpectStringContaining(vm, structure, ctx);
+ ptr->finishCreation(vm);
+ return ptr;
+}
+
+extern "C" void* ExpectStringContaining__fromJS(JSC::EncodedJSValue value)
+{
+ JSC::JSValue decodedValue = JSC::JSValue::decode(value);
+ if (decodedValue.isEmpty() || !decodedValue.isCell())
+ return nullptr;
+
+ JSC::JSCell* cell = decodedValue.asCell();
+ JSExpectStringContaining* object = JSC::jsDynamicCast<JSExpectStringContaining*>(cell);
+
+ if (!object)
+ return nullptr;
+
+ return object->wrapped();
+}
+
+extern "C" bool ExpectStringContaining__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr)
+{
+ JSExpectStringContaining* object = JSC::jsDynamicCast<JSExpectStringContaining*>(JSValue::decode(value));
+ if (!object)
+ return false;
+
+ object->m_ctx = ptr;
+ return true;
+}
+
+extern "C" const size_t ExpectStringContaining__ptrOffset = JSExpectStringContaining::offsetOfWrapped();
+
+void JSExpectStringContaining::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
+{
+ auto* thisObject = jsCast<JSExpectStringContaining*>(cell);
+ if (void* wrapped = thisObject->wrapped()) {
+ // if (thisObject->scriptExecutionContext())
+ // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string());
+ }
+ Base::analyzeHeap(cell, analyzer);
+}
+
+JSObject* JSExpectStringContaining::createPrototype(VM& vm, JSDOMGlobalObject* globalObject)
+{
+ return JSExpectStringContainingPrototype::create(vm, globalObject, JSExpectStringContainingPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
+}
+
+extern "C" EncodedJSValue ExpectStringContaining__create(Zig::GlobalObject* globalObject, void* ptr)
+{
+ auto& vm = globalObject->vm();
+ JSC::Structure* structure = globalObject->JSExpectStringContainingStructure();
+ JSExpectStringContaining* instance = JSExpectStringContaining::create(vm, globalObject, structure, ptr);
+
+ return JSValue::encode(instance);
+}
+
+template<typename Visitor>
+void JSExpectStringContaining::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+ JSExpectStringContaining* thisObject = jsCast<JSExpectStringContaining*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ Base::visitChildren(thisObject, visitor);
+ visitor.append(thisObject->m_stringValue);
+}
+
+DEFINE_VISIT_CHILDREN(JSExpectStringContaining);
+
+template<typename Visitor>
+void JSExpectStringContaining::visitAdditionalChildren(Visitor& visitor)
+{
+ JSExpectStringContaining* thisObject = this;
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ visitor.append(thisObject->m_stringValue);
+}
+
+DEFINE_VISIT_ADDITIONAL_CHILDREN(JSExpectStringContaining);
+
+template<typename Visitor>
+void JSExpectStringContaining::visitOutputConstraintsImpl(JSCell* cell, Visitor& visitor)
+{
+ JSExpectStringContaining* thisObject = jsCast<JSExpectStringContaining*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ thisObject->visitAdditionalChildren<Visitor>(visitor);
+}
+
+DEFINE_VISIT_OUTPUT_CONSTRAINTS(JSExpectStringContaining);
+class JSExpectStringMatchingPrototype final : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+
+ static JSExpectStringMatchingPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSExpectStringMatchingPrototype* ptr = new (NotNull, JSC::allocateCell<JSExpectStringMatchingPrototype>(vm)) JSExpectStringMatchingPrototype(vm, globalObject, structure);
+ ptr->finishCreation(vm, globalObject);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, JSC::SubspaceAccess>
+ static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ return &vm.plainObjectSpace();
+ }
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+
+private:
+ JSExpectStringMatchingPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
+};
+
+extern "C" void ExpectStringMatchingClass__finalize(void*);
+extern "C" JSC_DECLARE_HOST_FUNCTION(ExpectStringMatchingClass__call);
+
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSExpectStringMatchingPrototype, JSExpectStringMatchingPrototype::Base);
+
+static const HashTableValue JSExpectStringMatchingPrototypeTableValues[] = {};
+
+const ClassInfo JSExpectStringMatchingPrototype::s_info = { "ExpectStringMatching"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectStringMatchingPrototype) };
+
+extern "C" void ExpectStringMatchingPrototype__testValueSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value)
+{
+ auto& vm = globalObject->vm();
+ auto* thisObject = jsCast<JSExpectStringMatching*>(JSValue::decode(thisValue));
+ thisObject->m_testValue.set(vm, thisObject, JSValue::decode(value));
+}
+
+extern "C" EncodedJSValue ExpectStringMatchingPrototype__testValueGetCachedValue(JSC::EncodedJSValue thisValue)
+{
+ auto* thisObject = jsCast<JSExpectStringMatching*>(JSValue::decode(thisValue));
+ return JSValue::encode(thisObject->m_testValue.get());
+}
+
+void JSExpectStringMatchingPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
+{
+ Base::finishCreation(vm);
+
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+JSExpectStringMatching::~JSExpectStringMatching()
+{
+ if (m_ctx) {
+ ExpectStringMatchingClass__finalize(m_ctx);
+ }
+}
+void JSExpectStringMatching::destroy(JSCell* cell)
+{
+ static_cast<JSExpectStringMatching*>(cell)->JSExpectStringMatching::~JSExpectStringMatching();
+}
+
+const ClassInfo JSExpectStringMatching::s_info = { "ExpectStringMatching"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectStringMatching) };
+
+void JSExpectStringMatching::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+}
+
+JSExpectStringMatching* JSExpectStringMatching::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx)
+{
+ JSExpectStringMatching* ptr = new (NotNull, JSC::allocateCell<JSExpectStringMatching>(vm)) JSExpectStringMatching(vm, structure, ctx);
+ ptr->finishCreation(vm);
+ return ptr;
+}
+
+extern "C" void* ExpectStringMatching__fromJS(JSC::EncodedJSValue value)
+{
+ JSC::JSValue decodedValue = JSC::JSValue::decode(value);
+ if (decodedValue.isEmpty() || !decodedValue.isCell())
+ return nullptr;
+
+ JSC::JSCell* cell = decodedValue.asCell();
+ JSExpectStringMatching* object = JSC::jsDynamicCast<JSExpectStringMatching*>(cell);
+
+ if (!object)
+ return nullptr;
+
+ return object->wrapped();
+}
+
+extern "C" bool ExpectStringMatching__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr)
+{
+ JSExpectStringMatching* object = JSC::jsDynamicCast<JSExpectStringMatching*>(JSValue::decode(value));
+ if (!object)
+ return false;
+
+ object->m_ctx = ptr;
+ return true;
+}
+
+extern "C" const size_t ExpectStringMatching__ptrOffset = JSExpectStringMatching::offsetOfWrapped();
+
+void JSExpectStringMatching::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
+{
+ auto* thisObject = jsCast<JSExpectStringMatching*>(cell);
+ if (void* wrapped = thisObject->wrapped()) {
+ // if (thisObject->scriptExecutionContext())
+ // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string());
+ }
+ Base::analyzeHeap(cell, analyzer);
+}
+
+JSObject* JSExpectStringMatching::createPrototype(VM& vm, JSDOMGlobalObject* globalObject)
+{
+ return JSExpectStringMatchingPrototype::create(vm, globalObject, JSExpectStringMatchingPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
+}
+
+extern "C" EncodedJSValue ExpectStringMatching__create(Zig::GlobalObject* globalObject, void* ptr)
+{
+ auto& vm = globalObject->vm();
+ JSC::Structure* structure = globalObject->JSExpectStringMatchingStructure();
+ JSExpectStringMatching* instance = JSExpectStringMatching::create(vm, globalObject, structure, ptr);
+
+ return JSValue::encode(instance);
+}
+
+template<typename Visitor>
+void JSExpectStringMatching::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+ JSExpectStringMatching* thisObject = jsCast<JSExpectStringMatching*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ Base::visitChildren(thisObject, visitor);
+ visitor.append(thisObject->m_testValue);
+}
+
+DEFINE_VISIT_CHILDREN(JSExpectStringMatching);
+
+template<typename Visitor>
+void JSExpectStringMatching::visitAdditionalChildren(Visitor& visitor)
+{
+ JSExpectStringMatching* thisObject = this;
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ visitor.append(thisObject->m_testValue);
+}
+
+DEFINE_VISIT_ADDITIONAL_CHILDREN(JSExpectStringMatching);
+
+template<typename Visitor>
+void JSExpectStringMatching::visitOutputConstraintsImpl(JSCell* cell, Visitor& visitor)
+{
+ JSExpectStringMatching* thisObject = jsCast<JSExpectStringMatching*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ thisObject->visitAdditionalChildren<Visitor>(visitor);
+}
+
+DEFINE_VISIT_OUTPUT_CONSTRAINTS(JSExpectStringMatching);
class JSFileSystemRouterPrototype final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.h b/src/bun.js/bindings/ZigGeneratedClasses.h
index cf5446a1a..668cd3f6b 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses.h
@@ -416,6 +416,168 @@ public:
mutable JSC::WriteBarrier<JSC::Unknown> m_constructorValue;
};
+class JSExpectAnything final : public JSC::JSDestructibleObject {
+public:
+ using Base = JSC::JSDestructibleObject;
+ static JSExpectAnything* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx);
+
+ DECLARE_EXPORT_INFO;
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return WebCore::subspaceForImpl<JSExpectAnything, WebCore::UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForExpectAnything.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForExpectAnything = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForExpectAnything.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForExpectAnything = std::forward<decltype(space)>(space); });
+ }
+
+ static void destroy(JSC::JSCell*);
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), StructureFlags), info());
+ }
+
+ static JSObject* createPrototype(VM& vm, JSDOMGlobalObject* globalObject);
+ ;
+
+ ~JSExpectAnything();
+
+ void* wrapped() const { return m_ctx; }
+
+ void detach()
+ {
+ m_ctx = nullptr;
+ }
+
+ static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
+ static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSExpectAnything, m_ctx); }
+
+ void* m_ctx { nullptr };
+
+ JSExpectAnything(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
+ : Base(vm, structure)
+ {
+ m_ctx = sinkPtr;
+ }
+
+ void finishCreation(JSC::VM&);
+};
+
+class JSExpectStringContaining final : public JSC::JSDestructibleObject {
+public:
+ using Base = JSC::JSDestructibleObject;
+ static JSExpectStringContaining* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx);
+
+ DECLARE_EXPORT_INFO;
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return WebCore::subspaceForImpl<JSExpectStringContaining, WebCore::UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForExpectStringContaining.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForExpectStringContaining = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForExpectStringContaining.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForExpectStringContaining = std::forward<decltype(space)>(space); });
+ }
+
+ static void destroy(JSC::JSCell*);
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), StructureFlags), info());
+ }
+
+ static JSObject* createPrototype(VM& vm, JSDOMGlobalObject* globalObject);
+ ;
+
+ ~JSExpectStringContaining();
+
+ void* wrapped() const { return m_ctx; }
+
+ void detach()
+ {
+ m_ctx = nullptr;
+ }
+
+ static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
+ static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSExpectStringContaining, m_ctx); }
+
+ void* m_ctx { nullptr };
+
+ JSExpectStringContaining(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
+ : Base(vm, structure)
+ {
+ m_ctx = sinkPtr;
+ }
+
+ void finishCreation(JSC::VM&);
+
+ DECLARE_VISIT_CHILDREN;
+ template<typename Visitor> void visitAdditionalChildren(Visitor&);
+ DECLARE_VISIT_OUTPUT_CONSTRAINTS;
+
+ mutable JSC::WriteBarrier<JSC::Unknown> m_stringValue;
+};
+
+class JSExpectStringMatching final : public JSC::JSDestructibleObject {
+public:
+ using Base = JSC::JSDestructibleObject;
+ static JSExpectStringMatching* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx);
+
+ DECLARE_EXPORT_INFO;
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return WebCore::subspaceForImpl<JSExpectStringMatching, WebCore::UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForExpectStringMatching.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForExpectStringMatching = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForExpectStringMatching.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForExpectStringMatching = std::forward<decltype(space)>(space); });
+ }
+
+ static void destroy(JSC::JSCell*);
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), StructureFlags), info());
+ }
+
+ static JSObject* createPrototype(VM& vm, JSDOMGlobalObject* globalObject);
+ ;
+
+ ~JSExpectStringMatching();
+
+ void* wrapped() const { return m_ctx; }
+
+ void detach()
+ {
+ m_ctx = nullptr;
+ }
+
+ static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
+ static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSExpectStringMatching, m_ctx); }
+
+ void* m_ctx { nullptr };
+
+ JSExpectStringMatching(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
+ : Base(vm, structure)
+ {
+ m_ctx = sinkPtr;
+ }
+
+ void finishCreation(JSC::VM&);
+
+ DECLARE_VISIT_CHILDREN;
+ template<typename Visitor> void visitAdditionalChildren(Visitor&);
+ DECLARE_VISIT_OUTPUT_CONSTRAINTS;
+
+ mutable JSC::WriteBarrier<JSC::Unknown> m_testValue;
+};
+
class JSFileSystemRouter final : public JSC::JSDestructibleObject {
public:
using Base = JSC::JSDestructibleObject;
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index f2d0e248e..f31a3c1cc 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -2020,6 +2020,35 @@ JSC_DEFINE_HOST_FUNCTION(functionBunDeepEquals, (JSGlobalObject * globalObject,
}
}
+JSC_DECLARE_HOST_FUNCTION(functionBunDeepMatch);
+
+JSC_DEFINE_HOST_FUNCTION(functionBunDeepMatch, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ auto* global = reinterpret_cast<GlobalObject*>(globalObject);
+ JSC::VM& vm = global->vm();
+
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (callFrame->argumentCount() < 2) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ throwTypeError(globalObject, throwScope, "Expected 2 values to compare"_s);
+ return JSValue::encode(jsUndefined());
+ }
+
+ JSC::JSValue subset = callFrame->uncheckedArgument(0);
+ JSC::JSValue object = callFrame->uncheckedArgument(1);
+
+ if (!subset.isObject() || !object.isObject()) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ throwTypeError(globalObject, throwScope, "Expected 2 object to match"_s);
+ return JSValue::encode(jsUndefined());
+ }
+
+ bool isEqual = Bun__deepMatch(object, subset, globalObject, &scope, false);
+ RETURN_IF_EXCEPTION(scope, {});
+ return JSValue::encode(jsBoolean(isEqual));
+}
+
JSC_DECLARE_HOST_FUNCTION(functionBunNanoseconds);
JSC_DEFINE_HOST_FUNCTION(functionBunNanoseconds, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
@@ -3668,6 +3697,12 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm
}
{
+ JSC::Identifier identifier = JSC::Identifier::fromString(vm, "deepMatch"_s);
+ object->putDirectNativeFunction(vm, this, identifier, 2, functionBunDeepMatch, ImplementationVisibility::Public, NoIntrinsic,
+ JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0);
+ }
+
+ {
JSC::Identifier identifier = JSC::Identifier::fromString(vm, "version"_s);
object->putDirect(vm, PropertyName(identifier), JSC::jsOwnedString(vm, makeString(Bun__version + 1)),
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index 1e6da1e71..8aa01abb3 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -721,6 +721,175 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
return true;
}
+using namespace WebCore;
+
+enum class AsymmetricMatcherResult : uint8_t {
+ PASS,
+ FAIL,
+ NOT_MATCHER,
+};
+
+AsymmetricMatcherResult matchAsymmetricMatcher(JSGlobalObject* globalObject, JSCell* matcherPropCell, JSValue otherProp, ThrowScope* throwScope)
+{
+ if (auto* expectAnything = jsDynamicCast<JSExpectAnything*>(matcherPropCell)) {
+ if (otherProp.isUndefinedOrNull()) {
+ return AsymmetricMatcherResult::FAIL;
+ }
+
+ return AsymmetricMatcherResult::PASS;
+ } else if (auto* expectAny = jsDynamicCast<JSExpectAny*>(matcherPropCell)) {
+ JSValue constructorValue = expectAny->m_constructorValue.get();
+ JSObject* constructorObject = constructorValue.getObject();
+
+ if (constructorObject->hasInstance(globalObject, otherProp)) {
+ return AsymmetricMatcherResult::PASS;
+ }
+
+ // check for basic types
+ VM& vm = globalObject->vm();
+ ZigString name = {};
+ JSC__JSValue__getNameProperty(JSValue::encode(constructorValue), globalObject, &name);
+ StringView nameView(name.ptr, name.len);
+
+ if (otherProp.isNumber() && nameView == "Number"_s) {
+ return AsymmetricMatcherResult::PASS;
+ } else if (otherProp.isBoolean() && nameView == "Boolean"_s) {
+ return AsymmetricMatcherResult::PASS;
+ } else if (otherProp.isString() && nameView == "String"_s) {
+ return AsymmetricMatcherResult::PASS;
+ } else if (otherProp.isBigInt() && nameView == "BigInt"_s) {
+ return AsymmetricMatcherResult::PASS;
+ }
+
+ return AsymmetricMatcherResult::FAIL;
+ } else if (auto* expectStringContaining = jsDynamicCast<JSExpectStringContaining*>(matcherPropCell)) {
+ JSValue expectedSubstring = expectStringContaining->m_stringValue.get();
+
+ if (otherProp.isString()) {
+ String otherString = otherProp.toWTFString(globalObject);
+ RETURN_IF_EXCEPTION(*throwScope, AsymmetricMatcherResult::FAIL);
+
+ String substring = expectedSubstring.toWTFString(globalObject);
+ RETURN_IF_EXCEPTION(*throwScope, AsymmetricMatcherResult::FAIL);
+
+ if (otherString.find(substring) != WTF::notFound) {
+ return AsymmetricMatcherResult::PASS;
+ }
+ }
+
+ return AsymmetricMatcherResult::FAIL;
+ } else if (auto* expectStringMatching = jsDynamicCast<JSExpectStringMatching*>(matcherPropCell)) {
+ JSValue expectedTestValue = expectStringMatching->m_testValue.get();
+
+ if (otherProp.isString()) {
+ if (expectedTestValue.isString()) {
+ String otherString = otherProp.toWTFString(globalObject);
+ RETURN_IF_EXCEPTION(*throwScope, AsymmetricMatcherResult::FAIL);
+
+ String substring = expectedTestValue.toWTFString(globalObject);
+ RETURN_IF_EXCEPTION(*throwScope, AsymmetricMatcherResult::FAIL);
+
+ if (otherString.find(substring) != WTF::notFound) {
+ return AsymmetricMatcherResult::PASS;
+ }
+ } else if (expectedTestValue.isCell() and expectedTestValue.asCell()->type() == RegExpObjectType) {
+ if (auto* regex = jsDynamicCast<RegExpObject*>(expectedTestValue)) {
+ JSString* otherString = otherProp.toString(globalObject);
+ if (regex->match(globalObject, otherString)) {
+ return AsymmetricMatcherResult::PASS;
+ }
+ }
+ }
+ }
+
+ return AsymmetricMatcherResult::FAIL;
+ }
+
+ return AsymmetricMatcherResult::NOT_MATCHER;
+}
+
+bool Bun__deepMatch(JSValue objValue, JSValue subsetValue, JSGlobalObject* globalObject, ThrowScope* throwScope, bool replacePropsWithAsymmetricMatchers)
+{
+ VM& vm = globalObject->vm();
+ JSObject* obj = objValue.getObject();
+ JSObject* subsetObj = subsetValue.getObject();
+
+ PropertyNameArray subsetProps(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Include);
+ subsetObj->getPropertyNames(globalObject, subsetProps, DontEnumPropertiesMode::Exclude);
+
+ // TODO: add fast paths for:
+ // - two "simple" objects (using ->forEachProperty in both)
+ // - two "simple" arrays
+ // similar to what is done in deepEquals (canPerformFastPropertyEnumerationForIterationBun)
+
+ // arrays should match exactly
+ if (isArray(globalObject, objValue) && isArray(globalObject, subsetValue)) {
+ if (obj->getArrayLength() != subsetObj->getArrayLength()) {
+ return false;
+ }
+ PropertyNameArray objProps(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Include);
+ obj->getPropertyNames(globalObject, objProps, DontEnumPropertiesMode::Exclude);
+ if (objProps.size() != subsetProps.size()) {
+ return false;
+ }
+ }
+
+ for (size_t i = 0; i < subsetProps.size(); i++) {
+ JSValue prop = obj->getIfPropertyExists(globalObject, subsetProps[i]);
+ RETURN_IF_EXCEPTION(*throwScope, false);
+
+ if (prop.isEmpty()) {
+ return false;
+ }
+
+ JSValue subsetProp = subsetObj->get(globalObject, subsetProps[i]);
+ RETURN_IF_EXCEPTION(*throwScope, false);
+
+ JSCell* subsetPropCell = subsetProp.asCell();
+ JSCell* propCell = prop.asCell();
+
+ if (subsetProp.isCell() and subsetPropCell->type() == JSC::JSType(JSDOMWrapperType)) {
+ switch (matchAsymmetricMatcher(globalObject, subsetPropCell, prop, throwScope)) {
+ case AsymmetricMatcherResult::FAIL:
+ return false;
+ case AsymmetricMatcherResult::PASS:
+ if (replacePropsWithAsymmetricMatchers) {
+ obj->putDirect(vm, subsetProps[i], subsetProp);
+ }
+ // continue to next subset prop
+ continue;
+ case AsymmetricMatcherResult::NOT_MATCHER:
+ break;
+ }
+ } else if (prop.isCell() and propCell->type() == JSC::JSType(JSDOMWrapperType)) {
+ switch (matchAsymmetricMatcher(globalObject, propCell, subsetProp, throwScope)) {
+ case AsymmetricMatcherResult::FAIL:
+ return false;
+ case AsymmetricMatcherResult::PASS:
+ if (replacePropsWithAsymmetricMatchers) {
+ subsetObj->putDirect(vm, subsetProps[i], prop);
+ }
+ // continue to next subset prop
+ continue;
+ case AsymmetricMatcherResult::NOT_MATCHER:
+ break;
+ }
+ }
+
+ if (subsetProp.isObject() and prop.isObject()) {
+ if (!Bun__deepMatch(prop, subsetProp, globalObject, throwScope, replacePropsWithAsymmetricMatchers)) {
+ return false;
+ }
+ } else {
+ if (!sameValue(globalObject, prop, subsetProp)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
extern "C" {
bool WebCore__FetchHeaders__isEmpty(WebCore__FetchHeaders* arg0)
@@ -753,8 +922,6 @@ WebCore__FetchHeaders* WebCore__FetchHeaders__cast_(JSC__JSValue JSValue0, JSC__
return WebCoreCast<WebCore::JSFetchHeaders, WebCore__FetchHeaders>(JSValue0);
}
-using namespace WebCore;
-
WebCore__FetchHeaders* WebCore__FetchHeaders__createFromJS(JSC__JSGlobalObject* lexicalGlobalObject, JSC__JSValue argument0_)
{
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
@@ -1458,6 +1625,16 @@ bool JSC__JSValue__strictDeepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1
return Bun__deepEquals<true>(globalObject, v1, v2, stack, &scope, true);
}
+bool JSC__JSValue__deepMatch(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject, bool replacePropsWithAsymmetricMatchers)
+{
+ JSValue obj = JSValue::decode(JSValue0);
+ JSValue subset = JSValue::decode(JSValue1);
+
+ ThrowScope scope = DECLARE_THROW_SCOPE(globalObject->vm());
+
+ return Bun__deepMatch(obj, subset, globalObject, &scope, replacePropsWithAsymmetricMatchers);
+}
+
// This is the same as the C API version, except it returns a JSValue which may be a *Exception
// We want that so we can return stack traces.
JSC__JSValue JSObjectCallAsFunctionReturnValue(JSContextRef ctx, JSObjectRef object,
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index 634ec9f39..09902adb9 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -3970,6 +3970,8 @@ pub const JSValue = enum(JSValueReprInt) {
return cppFn("toZigString", .{ this, out, global });
}
+ /// this: RegExp value
+ /// other: string value
pub fn toMatch(this: JSValue, global: *JSGlobalObject, other: JSValue) bool {
return cppFn("toMatch", .{ this, global, other });
}
@@ -4338,6 +4340,10 @@ pub const JSValue = enum(JSValueReprInt) {
return cppFn("strictDeepEquals", .{ this, other, global });
}
+ pub fn deepMatch(this: JSValue, subset: JSValue, global: *JSGlobalObject, replace_props_with_asymmetric_matchers: bool) bool {
+ return cppFn("deepMatch", .{ this, subset, global, replace_props_with_asymmetric_matchers });
+ }
+
pub const DiffMethod = enum(u8) {
none,
character,
@@ -4697,6 +4703,7 @@ pub const JSValue = enum(JSValueReprInt) {
"isConstructor",
"isInstanceOf",
"stringIncludes",
+ "deepMatch",
};
};
@@ -5034,6 +5041,10 @@ pub const CallFrame = opaque {
.len = i,
};
}
+
+ pub inline fn slice(self: @This()) []const JSValue {
+ return self.ptr[0..self.len];
+ }
};
}
diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig
index dc56a07af..e6bc953da 100644
--- a/src/bun.js/bindings/exports.zig
+++ b/src/bun.js/bindings/exports.zig
@@ -2219,6 +2219,43 @@ pub const ZigConsoleClient = struct {
} else if (value.as(JSC.ResolveMessage)) |resolve_log| {
resolve_log.msg.writeFormat(writer_, enable_ansi_colors) catch {};
return;
+ } else if (value.as(JSC.Jest.ExpectAnything) != null) {
+ writer.writeAll("Anything");
+ return;
+ } else if (value.as(JSC.Jest.ExpectAny) != null) {
+ const constructor_value = JSC.Jest.ExpectAny.constructorValueGetCached(value) orelse return;
+
+ this.addForNewLine("Any<".len);
+ writer.writeAll("Any<");
+ var class_name = ZigString.init(&name_buf);
+
+ constructor_value.getClassName(this.globalThis, &class_name);
+ this.addForNewLine(class_name.len);
+ writer.print(comptime Output.prettyFmt("<cyan>{}<r>", enable_ansi_colors), .{class_name});
+ this.addForNewLine(1);
+ writer.writeAll(">");
+
+ return;
+ } else if (value.as(JSC.Jest.ExpectStringContaining) != null) {
+ const substring_value = JSC.Jest.ExpectStringContaining.stringValueGetCached(value) orelse return;
+
+ this.addForNewLine("StringContaining ".len);
+ writer.writeAll("StringContaining ");
+ this.printAs(.String, Writer, writer_, substring_value, .String, enable_ansi_colors);
+
+ return;
+ } else if (value.as(JSC.Jest.ExpectStringMatching) != null) {
+ const test_value = JSC.Jest.ExpectStringMatching.testValueGetCached(value) orelse return;
+
+ this.addForNewLine("StringMatching ".len);
+ writer.writeAll("StringMatching ");
+
+ const original_quote_strings = this.quote_strings;
+ if (test_value.isRegExp()) this.quote_strings = false;
+ this.printAs(.String, Writer, writer_, test_value, .String, enable_ansi_colors);
+ this.quote_strings = original_quote_strings;
+
+ return;
} else if (jsType != .DOMWrapper) {
if (value.isCallable(this.globalThis.vm())) {
return this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors);
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index a4bbd2cab..b98d59cd3 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -1176,6 +1176,227 @@ pub const JSExpectAny = struct {
}
}
};
+pub const JSExpectAnything = struct {
+ const ExpectAnything = Classes.ExpectAnything;
+ const GetterType = fn (*ExpectAnything, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const GetterTypeWithThisValue = fn (*ExpectAnything, JSC.JSValue, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const SetterType = fn (*ExpectAnything, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const SetterTypeWithThisValue = fn (*ExpectAnything, JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const CallbackType = fn (*ExpectAnything, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue;
+
+ /// Return the pointer to the wrapped object.
+ /// If the object does not match the type, return null.
+ pub fn fromJS(value: JSC.JSValue) ?*ExpectAnything {
+ JSC.markBinding(@src());
+ return ExpectAnything__fromJS(value);
+ }
+
+ /// Create a new instance of ExpectAnything
+ pub fn toJS(this: *ExpectAnything, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
+ JSC.markBinding(@src());
+ if (comptime Environment.allow_assert) {
+ const value__ = ExpectAnything__create(globalObject, this);
+ std.debug.assert(value__.as(ExpectAnything).? == this); // If this fails, likely a C ABI issue.
+ return value__;
+ } else {
+ return ExpectAnything__create(globalObject, this);
+ }
+ }
+
+ /// Modify the internal ptr to point to a new instance of ExpectAnything.
+ pub fn dangerouslySetPtr(value: JSC.JSValue, ptr: ?*ExpectAnything) bool {
+ JSC.markBinding(@src());
+ return ExpectAnything__dangerouslySetPtr(value, ptr);
+ }
+
+ /// Detach the ptr from the thisValue
+ pub fn detachPtr(_: *ExpectAnything, value: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ std.debug.assert(ExpectAnything__dangerouslySetPtr(value, null));
+ }
+
+ extern fn ExpectAnything__fromJS(JSC.JSValue) ?*ExpectAnything;
+ extern fn ExpectAnything__getConstructor(*JSC.JSGlobalObject) JSC.JSValue;
+
+ extern fn ExpectAnything__create(globalObject: *JSC.JSGlobalObject, ptr: ?*ExpectAnything) JSC.JSValue;
+
+ extern fn ExpectAnything__dangerouslySetPtr(JSC.JSValue, ?*ExpectAnything) bool;
+
+ comptime {
+ if (@TypeOf(ExpectAnything.finalize) != (fn (*ExpectAnything) callconv(.C) void)) {
+ @compileLog("ExpectAnything.finalize is not a finalizer");
+ }
+
+ if (@TypeOf(ExpectAnything.call) != StaticCallbackType)
+ @compileLog("Expected ExpectAnything.call to be a static callback");
+ if (!JSC.is_bindgen) {
+ @export(ExpectAnything.call, .{ .name = "ExpectAnythingClass__call" });
+ @export(ExpectAnything.finalize, .{ .name = "ExpectAnythingClass__finalize" });
+ }
+ }
+};
+pub const JSExpectStringContaining = struct {
+ const ExpectStringContaining = Classes.ExpectStringContaining;
+ const GetterType = fn (*ExpectStringContaining, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const GetterTypeWithThisValue = fn (*ExpectStringContaining, JSC.JSValue, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const SetterType = fn (*ExpectStringContaining, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const SetterTypeWithThisValue = fn (*ExpectStringContaining, JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const CallbackType = fn (*ExpectStringContaining, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue;
+
+ /// Return the pointer to the wrapped object.
+ /// If the object does not match the type, return null.
+ pub fn fromJS(value: JSC.JSValue) ?*ExpectStringContaining {
+ JSC.markBinding(@src());
+ return ExpectStringContaining__fromJS(value);
+ }
+
+ extern fn ExpectStringContainingPrototype__stringValueSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void;
+
+ extern fn ExpectStringContainingPrototype__stringValueGetCachedValue(JSC.JSValue) JSC.JSValue;
+
+ /// `ExpectStringContaining.stringValue` setter
+ /// This value will be visited by the garbage collector.
+ pub fn stringValueSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ ExpectStringContainingPrototype__stringValueSetCachedValue(thisValue, globalObject, value);
+ }
+
+ /// `ExpectStringContaining.stringValue` getter
+ /// This value will be visited by the garbage collector.
+ pub fn stringValueGetCached(thisValue: JSC.JSValue) ?JSC.JSValue {
+ JSC.markBinding(@src());
+ const result = ExpectStringContainingPrototype__stringValueGetCachedValue(thisValue);
+ if (result == .zero)
+ return null;
+
+ return result;
+ }
+
+ /// Create a new instance of ExpectStringContaining
+ pub fn toJS(this: *ExpectStringContaining, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
+ JSC.markBinding(@src());
+ if (comptime Environment.allow_assert) {
+ const value__ = ExpectStringContaining__create(globalObject, this);
+ std.debug.assert(value__.as(ExpectStringContaining).? == this); // If this fails, likely a C ABI issue.
+ return value__;
+ } else {
+ return ExpectStringContaining__create(globalObject, this);
+ }
+ }
+
+ /// Modify the internal ptr to point to a new instance of ExpectStringContaining.
+ pub fn dangerouslySetPtr(value: JSC.JSValue, ptr: ?*ExpectStringContaining) bool {
+ JSC.markBinding(@src());
+ return ExpectStringContaining__dangerouslySetPtr(value, ptr);
+ }
+
+ /// Detach the ptr from the thisValue
+ pub fn detachPtr(_: *ExpectStringContaining, value: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ std.debug.assert(ExpectStringContaining__dangerouslySetPtr(value, null));
+ }
+
+ extern fn ExpectStringContaining__fromJS(JSC.JSValue) ?*ExpectStringContaining;
+ extern fn ExpectStringContaining__getConstructor(*JSC.JSGlobalObject) JSC.JSValue;
+
+ extern fn ExpectStringContaining__create(globalObject: *JSC.JSGlobalObject, ptr: ?*ExpectStringContaining) JSC.JSValue;
+
+ extern fn ExpectStringContaining__dangerouslySetPtr(JSC.JSValue, ?*ExpectStringContaining) bool;
+
+ comptime {
+ if (@TypeOf(ExpectStringContaining.finalize) != (fn (*ExpectStringContaining) callconv(.C) void)) {
+ @compileLog("ExpectStringContaining.finalize is not a finalizer");
+ }
+
+ if (@TypeOf(ExpectStringContaining.call) != StaticCallbackType)
+ @compileLog("Expected ExpectStringContaining.call to be a static callback");
+ if (!JSC.is_bindgen) {
+ @export(ExpectStringContaining.call, .{ .name = "ExpectStringContainingClass__call" });
+ @export(ExpectStringContaining.finalize, .{ .name = "ExpectStringContainingClass__finalize" });
+ }
+ }
+};
+pub const JSExpectStringMatching = struct {
+ const ExpectStringMatching = Classes.ExpectStringMatching;
+ const GetterType = fn (*ExpectStringMatching, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const GetterTypeWithThisValue = fn (*ExpectStringMatching, JSC.JSValue, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const SetterType = fn (*ExpectStringMatching, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const SetterTypeWithThisValue = fn (*ExpectStringMatching, JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const CallbackType = fn (*ExpectStringMatching, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue;
+
+ /// Return the pointer to the wrapped object.
+ /// If the object does not match the type, return null.
+ pub fn fromJS(value: JSC.JSValue) ?*ExpectStringMatching {
+ JSC.markBinding(@src());
+ return ExpectStringMatching__fromJS(value);
+ }
+
+ extern fn ExpectStringMatchingPrototype__testValueSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void;
+
+ extern fn ExpectStringMatchingPrototype__testValueGetCachedValue(JSC.JSValue) JSC.JSValue;
+
+ /// `ExpectStringMatching.testValue` setter
+ /// This value will be visited by the garbage collector.
+ pub fn testValueSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ ExpectStringMatchingPrototype__testValueSetCachedValue(thisValue, globalObject, value);
+ }
+
+ /// `ExpectStringMatching.testValue` getter
+ /// This value will be visited by the garbage collector.
+ pub fn testValueGetCached(thisValue: JSC.JSValue) ?JSC.JSValue {
+ JSC.markBinding(@src());
+ const result = ExpectStringMatchingPrototype__testValueGetCachedValue(thisValue);
+ if (result == .zero)
+ return null;
+
+ return result;
+ }
+
+ /// Create a new instance of ExpectStringMatching
+ pub fn toJS(this: *ExpectStringMatching, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
+ JSC.markBinding(@src());
+ if (comptime Environment.allow_assert) {
+ const value__ = ExpectStringMatching__create(globalObject, this);
+ std.debug.assert(value__.as(ExpectStringMatching).? == this); // If this fails, likely a C ABI issue.
+ return value__;
+ } else {
+ return ExpectStringMatching__create(globalObject, this);
+ }
+ }
+
+ /// Modify the internal ptr to point to a new instance of ExpectStringMatching.
+ pub fn dangerouslySetPtr(value: JSC.JSValue, ptr: ?*ExpectStringMatching) bool {
+ JSC.markBinding(@src());
+ return ExpectStringMatching__dangerouslySetPtr(value, ptr);
+ }
+
+ /// Detach the ptr from the thisValue
+ pub fn detachPtr(_: *ExpectStringMatching, value: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ std.debug.assert(ExpectStringMatching__dangerouslySetPtr(value, null));
+ }
+
+ extern fn ExpectStringMatching__fromJS(JSC.JSValue) ?*ExpectStringMatching;
+ extern fn ExpectStringMatching__getConstructor(*JSC.JSGlobalObject) JSC.JSValue;
+
+ extern fn ExpectStringMatching__create(globalObject: *JSC.JSGlobalObject, ptr: ?*ExpectStringMatching) JSC.JSValue;
+
+ extern fn ExpectStringMatching__dangerouslySetPtr(JSC.JSValue, ?*ExpectStringMatching) bool;
+
+ comptime {
+ if (@TypeOf(ExpectStringMatching.finalize) != (fn (*ExpectStringMatching) callconv(.C) void)) {
+ @compileLog("ExpectStringMatching.finalize is not a finalizer");
+ }
+
+ if (@TypeOf(ExpectStringMatching.call) != StaticCallbackType)
+ @compileLog("Expected ExpectStringMatching.call to be a static callback");
+ if (!JSC.is_bindgen) {
+ @export(ExpectStringMatching.call, .{ .name = "ExpectStringMatchingClass__call" });
+ @export(ExpectStringMatching.finalize, .{ .name = "ExpectStringMatchingClass__finalize" });
+ }
+ }
+};
pub const JSFileSystemRouter = struct {
const FileSystemRouter = Classes.FileSystemRouter;
const GetterType = fn (*FileSystemRouter, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
@@ -4622,6 +4843,9 @@ comptime {
_ = JSDirent;
_ = JSExpect;
_ = JSExpectAny;
+ _ = JSExpectAnything;
+ _ = JSExpectStringContaining;
+ _ = JSExpectStringMatching;
_ = JSFileSystemRouter;
_ = JSListener;
_ = JSMD4;
diff --git a/src/bun.js/bindings/generated_classes_list.zig b/src/bun.js/bindings/generated_classes_list.zig
index 4acde31e8..d5d987dce 100644
--- a/src/bun.js/bindings/generated_classes_list.zig
+++ b/src/bun.js/bindings/generated_classes_list.zig
@@ -6,6 +6,9 @@ pub const Classes = struct {
pub const Dirent = JSC.Node.Dirent;
pub const Expect = JSC.Jest.Expect;
pub const ExpectAny = JSC.Jest.ExpectAny;
+ pub const ExpectAnything = JSC.Jest.ExpectAnything;
+ pub const ExpectStringContaining = JSC.Jest.ExpectStringContaining;
+ pub const ExpectStringMatching = JSC.Jest.ExpectStringMatching;
pub const FileSystemRouter = JSC.API.FileSystemRouter;
pub const Bundler = JSC.API.JSBundler;
pub const JSBundler = Bundler;
diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h
index d925fb4cd..650203653 100644
--- a/src/bun.js/bindings/headers-handwritten.h
+++ b/src/bun.js/bindings/headers-handwritten.h
@@ -284,6 +284,8 @@ extern "C" int64_t Bun__encoding__constructFromUTF16(void*, const UChar* ptr, si
template<bool isStrict>
bool Bun__deepEquals(JSC::JSGlobalObject* globalObject, JSC::JSValue v1, JSC::JSValue v2, Vector<std::pair<JSC::JSValue, JSC::JSValue>, 16>& stack, JSC::ThrowScope* scope, bool addToStack);
+bool Bun__deepMatch(JSC::JSValue object, JSC::JSValue subset, JSC::JSGlobalObject* globalObject, JSC::ThrowScope* throwScope, bool replacePropsWithAsymmetricMatchers);
+
namespace Inspector {
class ScriptArguments;
}
diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h
index 5034b7652..9e9254bb2 100644
--- a/src/bun.js/bindings/headers.h
+++ b/src/bun.js/bindings/headers.h
@@ -316,6 +316,7 @@ CPP_DECL JSC__JSValue JSC__JSValue__createStringArray(JSC__JSGlobalObject* arg0,
CPP_DECL JSC__JSValue JSC__JSValue__createTypeError(const ZigString* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2);
CPP_DECL JSC__JSValue JSC__JSValue__createUninitializedUint8Array(JSC__JSGlobalObject* arg0, size_t arg1);
CPP_DECL bool JSC__JSValue__deepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* arg2);
+CPP_DECL bool JSC__JSValue__deepMatch(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* arg2, bool arg3);
CPP_DECL bool JSC__JSValue__eqlCell(JSC__JSValue JSValue0, JSC__JSCell* arg1);
CPP_DECL bool JSC__JSValue__eqlValue(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
CPP_DECL JSC__JSValue JSC__JSValue__fastGet_(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, unsigned char arg2);
diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig
index b1f1a1974..7d075e9e6 100644
--- a/src/bun.js/bindings/headers.zig
+++ b/src/bun.js/bindings/headers.zig
@@ -217,6 +217,7 @@ pub extern fn JSC__JSValue__createStringArray(arg0: *bindings.JSGlobalObject, ar
pub extern fn JSC__JSValue__createTypeError(arg0: [*c]const ZigString, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) JSC__JSValue;
pub extern fn JSC__JSValue__createUninitializedUint8Array(arg0: *bindings.JSGlobalObject, arg1: usize) JSC__JSValue;
pub extern fn JSC__JSValue__deepEquals(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: *bindings.JSGlobalObject) bool;
+pub extern fn JSC__JSValue__deepMatch(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: *bindings.JSGlobalObject, arg3: bool) bool;
pub extern fn JSC__JSValue__eqlCell(JSValue0: JSC__JSValue, arg1: [*c]bindings.JSCell) bool;
pub extern fn JSC__JSValue__eqlValue(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) bool;
pub extern fn JSC__JSValue__fastGet_(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: u8) JSC__JSValue;
diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts
index de9f260d2..38eefe778 100644
--- a/src/bun.js/test/jest.classes.ts
+++ b/src/bun.js/test/jest.classes.ts
@@ -2,6 +2,17 @@ import { define } from "../scripts/class-definitions";
export default [
define({
+ name: "ExpectAnything",
+ construct: false,
+ noConstructor: true,
+ call: true,
+ finalize: true,
+ JSType: "0b11101110",
+ configurable: false,
+ klass: {},
+ proto: {},
+ }),
+ define({
name: "ExpectAny",
construct: false,
noConstructor: true,
@@ -14,6 +25,30 @@ export default [
proto: {},
}),
define({
+ name: "ExpectStringContaining",
+ construct: false,
+ noConstructor: true,
+ call: true,
+ finalize: true,
+ JSType: "0b11101110",
+ values: ["stringValue"],
+ configurable: false,
+ klass: {},
+ proto: {},
+ }),
+ define({
+ name: "ExpectStringMatching",
+ construct: false,
+ noConstructor: true,
+ call: true,
+ finalize: true,
+ JSType: "0b11101110",
+ values: ["testValue"],
+ configurable: false,
+ klass: {},
+ proto: {},
+ }),
+ define({
name: "Expect",
construct: true,
call: true,
diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig
index f2e832ff9..93edb8abd 100644
--- a/src/bun.js/test/jest.zig
+++ b/src/bun.js/test/jest.zig
@@ -36,6 +36,7 @@ const ZigString = JSC.ZigString;
const JSInternalPromise = JSC.JSInternalPromise;
const JSPromise = JSC.JSPromise;
const JSValue = JSC.JSValue;
+const JSType = JSValue.JSType;
const JSError = JSC.JSError;
const JSGlobalObject = JSC.JSGlobalObject;
const JSObject = JSC.JSObject;
@@ -772,6 +773,108 @@ pub const Jest = struct {
}
};
+pub const ExpectAnything = struct {
+ pub usingnamespace JSC.Codegen.JSExpectAnything;
+
+ pub fn finalize(
+ this: *ExpectAnything,
+ ) callconv(.C) void {
+ VirtualMachine.get().allocator.destroy(this);
+ }
+
+ pub fn call(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue {
+ const anything = globalObject.bunVM().allocator.create(ExpectAnything) catch unreachable;
+ if (Jest.runner.?.pending_test == null) {
+ const err = globalObject.createErrorInstance("expect.anything() must be called in a test", .{});
+ err.put(globalObject, ZigString.static("name"), ZigString.init("TestNotRunningError").toValueGC(globalObject));
+ globalObject.throwValue(err);
+ return .zero;
+ }
+
+ const anything_js_value = anything.toJS(globalObject);
+ anything_js_value.ensureStillAlive();
+
+ var vm = globalObject.bunVM();
+ vm.autoGarbageCollect();
+
+ return anything_js_value;
+ }
+};
+
+pub const ExpectStringMatching = struct {
+ pub usingnamespace JSC.Codegen.JSExpectStringMatching;
+
+ pub fn finalize(
+ this: *ExpectStringMatching,
+ ) callconv(.C) void {
+ VirtualMachine.get().allocator.destroy(this);
+ }
+
+ pub fn call(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue {
+ const args = callFrame.arguments(1).slice();
+
+ if (args.len == 0 or (!args[0].isString() and !args[0].isRegExp())) {
+ const fmt = "<d>expect.<r>stringContaining<d>(<r>string<d>)<r>\n\nExpected a string or regular expression\n";
+ globalObject.throwPretty(fmt, .{});
+ return .zero;
+ }
+
+ const test_value = args[0];
+ const string_matching = globalObject.bunVM().allocator.create(ExpectStringMatching) catch unreachable;
+
+ if (Jest.runner.?.pending_test == null) {
+ const err = globalObject.createErrorInstance("expect.stringContaining() must be called in a test", .{});
+ err.put(globalObject, ZigString.static("name"), ZigString.init("TestNotRunningError").toValueGC(globalObject));
+ globalObject.throwValue(err);
+ return .zero;
+ }
+
+ const string_matching_js_value = string_matching.toJS(globalObject);
+ ExpectStringMatching.testValueSetCached(string_matching_js_value, globalObject, test_value);
+
+ var vm = globalObject.bunVM();
+ vm.autoGarbageCollect();
+ return string_matching_js_value;
+ }
+};
+
+pub const ExpectStringContaining = struct {
+ pub usingnamespace JSC.Codegen.JSExpectStringContaining;
+
+ pub fn finalize(
+ this: *ExpectStringContaining,
+ ) callconv(.C) void {
+ VirtualMachine.get().allocator.destroy(this);
+ }
+
+ pub fn call(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue {
+ const args = callFrame.arguments(1).slice();
+
+ if (args.len == 0 or !args[0].isString()) {
+ const fmt = "<d>expect.<r>stringContaining<d>(<r>string<d>)<r>\n\nExpected a string\n";
+ globalObject.throwPretty(fmt, .{});
+ return .zero;
+ }
+
+ const string_value = args[0];
+
+ const string_containing = globalObject.bunVM().allocator.create(ExpectStringContaining) catch unreachable;
+
+ if (Jest.runner.?.pending_test == null) {
+ const err = globalObject.createErrorInstance("expect.stringContaining() must be called in a test", .{});
+ err.put(globalObject, ZigString.static("name"), ZigString.init("TestNotRunningError").toValueGC(globalObject));
+ globalObject.throwValue(err);
+ return .zero;
+ }
+
+ const string_containing_js_value = string_containing.toJS(globalObject);
+ ExpectStringContaining.stringValueSetCached(string_containing_js_value, globalObject, string_value);
+
+ var vm = globalObject.bunVM();
+ vm.autoGarbageCollect();
+ return string_containing_js_value;
+ }
+};
pub const ExpectAny = struct {
pub usingnamespace JSC.Codegen.JSExpectAny;
@@ -810,7 +913,7 @@ pub const ExpectAny = struct {
any.* = .{};
const any_js_value = any.toJS(globalObject);
any_js_value.ensureStillAlive();
- JSC.Jest.ExpectAny.constructorValueSetCached(any_js_value, globalObject, constructor);
+ ExpectAny.constructorValueSetCached(any_js_value, globalObject, constructor);
any_js_value.ensureStillAlive();
var vm = globalObject.bunVM();
@@ -888,7 +991,7 @@ pub const Expect = struct {
pub fn call(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
const arguments_ = callframe.arguments(1);
if (arguments_.len < 1) {
- globalObject.throw("expect() requires one argument", .{});
+ globalObject.throw("expect() requires one argument\n", .{});
return .zero;
}
const arguments = arguments_.ptr[0..arguments_.len];
@@ -1523,26 +1626,23 @@ pub const Expect = struct {
if (pass) return thisValue;
// handle failure
- const diff_formatter = DiffFormatter{ .received = value, .expected = expected, .globalObject = globalObject, .not = not };
+ const diff_formatter = DiffFormatter{
+ .received = value,
+ .expected = expected,
+ .globalObject = globalObject,
+ .not = not,
+ };
if (not) {
const signature = comptime getSignature("toEqual", "<green>expected<r>", true);
const fmt = signature ++ "\n\n{any}\n";
- if (Output.enable_ansi_colors) {
- globalObject.throw(Output.prettyFmt(fmt, true), .{diff_formatter});
- return .zero;
- }
- globalObject.throw(Output.prettyFmt(fmt, false), .{diff_formatter});
+ globalObject.throwPretty(fmt, .{diff_formatter});
return .zero;
}
const signature = comptime getSignature("toEqual", "<green>expected<r>", false);
const fmt = signature ++ "\n\n{any}\n";
- if (Output.enable_ansi_colors) {
- globalObject.throw(Output.prettyFmt(fmt, true), .{diff_formatter});
- return .zero;
- }
- globalObject.throw(Output.prettyFmt(fmt, false), .{diff_formatter});
+ globalObject.throwPretty(fmt, .{diff_formatter});
return .zero;
}
@@ -2728,14 +2828,7 @@ pub const Expect = struct {
if (property_matchers) |_prop_matchers| {
var prop_matchers = _prop_matchers;
- var itr = PropertyMatcherIterator{
- .received_object = value,
- .failed = false,
- };
-
- prop_matchers.forEachProperty(globalObject, &itr, PropertyMatcherIterator.forEach);
-
- if (itr.failed) {
+ if (!value.deepMatch(prop_matchers, globalObject, true)) {
// TODO: print diff with properties from propertyMatchers
const signature = comptime getSignature("toMatchSnapshot", "<green>propertyMatchers<r>", false);
const fmt = signature ++ "\n\nExpected <green>propertyMatchers<r> to match properties from received object" ++
@@ -3627,88 +3720,6 @@ pub const Expect = struct {
return .zero;
}
- pub const PropertyMatcherIterator = struct {
- received_object: JSValue,
- failed: bool,
- i: usize = 0,
-
- pub fn forEach(
- globalObject: *JSGlobalObject,
- ctx_ptr: ?*anyopaque,
- key_: [*c]ZigString,
- value: JSValue,
- _: bool,
- ) callconv(.C) void {
- const key: ZigString = key_.?[0];
- if (key.eqlComptime("constructor")) return;
- if (key.eqlComptime("call")) return;
-
- var ctx: *@This() = bun.cast(*@This(), ctx_ptr orelse return);
- defer ctx.i += 1;
- var received_object: JSValue = ctx.received_object;
-
- if (received_object.get(globalObject, key.slice())) |received_value| {
- if (JSC.Jest.ExpectAny.fromJS(value)) |_| {
- var constructor_value = JSC.Jest.ExpectAny.constructorValueGetCached(value) orelse {
- globalObject.throw("Internal consistency error: the expect.any(constructor value) was garbage collected but it should not have been!", .{});
- ctx.failed = true;
- return;
- };
-
- if (received_value.isCell() and received_value.isInstanceOf(globalObject, constructor_value)) {
- received_object.put(globalObject, &key, value);
- return;
- }
-
- // check primitives
- // TODO: check the constructor for primitives by reading it from JSGlobalObject through a binding.
- var constructor_name = ZigString.Empty;
- constructor_value.getNameProperty(globalObject, &constructor_name);
- if (received_value.isNumber() and constructor_name.eqlComptime("Number")) {
- received_object.put(globalObject, &key, value);
- return;
- }
- if (received_value.isBoolean() and constructor_name.eqlComptime("Boolean")) {
- received_object.put(globalObject, &key, value);
- return;
- }
- if (received_value.isString() and constructor_name.eqlComptime("String")) {
- received_object.put(globalObject, &key, value);
- return;
- }
- if (received_value.isBigInt() and constructor_name.eqlComptime("BigInt")) {
- received_object.put(globalObject, &key, value);
- return;
- }
-
- ctx.failed = true;
- return;
- }
-
- if (value.isObject()) {
- if (received_object.get(globalObject, key.slice())) |new_object| {
- var itr = PropertyMatcherIterator{
- .received_object = new_object,
- .failed = false,
- };
- value.forEachProperty(globalObject, &itr, PropertyMatcherIterator.forEach);
- if (itr.failed) {
- ctx.failed = true;
- }
- } else {
- ctx.failed = true;
- }
-
- return;
- }
-
- if (value.isSameValue(received_value, globalObject)) return;
- }
-
- ctx.failed = true;
- }
- };
-
pub fn toBeInstanceOf(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue {
defer this.postMatch(globalObject);
@@ -3945,6 +3956,78 @@ pub const Expect = struct {
unreachable;
}
+ pub fn toMatchObject(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue {
+ defer this.postMatch(globalObject);
+ const thisValue = callFrame.this();
+ const args = callFrame.arguments(1).slice();
+
+ if (this.scope.tests.items.len <= this.test_id) {
+ globalObject.throw("toMatchObject() must be called in a test", .{});
+ return .zero;
+ }
+
+ active_test_expectation_counter.actual += 1;
+
+ const not = this.op.contains(.not);
+
+ const received_object = Expect.capturedValueGetCached(thisValue) orelse {
+ globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{});
+ return .zero;
+ };
+
+ if (!received_object.isObject()) {
+ const matcher_error = "\n\n<b>Matcher error<r>: <red>received<r> value must be a non-null object\n";
+ if (not) {
+ const fmt = comptime getSignature("toMatchObject", "<green>expected<r>", true) ++ matcher_error;
+ globalObject.throwPretty(fmt, .{});
+ return .zero;
+ }
+
+ const fmt = comptime getSignature("toMatchObject", "<green>expected<r>", false) ++ matcher_error;
+ globalObject.throwPretty(fmt, .{});
+ return .zero;
+ }
+
+ if (args.len < 1 or !args[0].isObject()) {
+ const matcher_error = "\n\n<b>Matcher error<r>: <green>expected<r> value must be a non-null object\n";
+ if (not) {
+ const fmt = comptime getSignature("toMatchObject", "", true) ++ matcher_error;
+ globalObject.throwPretty(fmt, .{});
+ return .zero;
+ }
+ const fmt = comptime getSignature("toMatchObject", "", false) ++ matcher_error;
+ globalObject.throwPretty(fmt, .{});
+ return .zero;
+ }
+
+ const property_matchers = args[0];
+
+ var pass = received_object.deepMatch(property_matchers, globalObject, true);
+
+ if (not) pass = !pass;
+ if (pass) return thisValue;
+
+ // handle failure
+ const diff_formatter = DiffFormatter{
+ .received = received_object,
+ .expected = property_matchers,
+ .globalObject = globalObject,
+ .not = not,
+ };
+
+ if (not) {
+ const signature = comptime getSignature("toMatchObject", "<green>expected<r>", true);
+ const fmt = signature ++ "\n\n{any}\n";
+ globalObject.throwPretty(fmt, .{diff_formatter});
+ return .zero;
+ }
+
+ const signature = comptime getSignature("toMatchObject", "<green>expected<r>", false);
+ const fmt = signature ++ "\n\n{any}\n";
+ globalObject.throwPretty(fmt, .{diff_formatter});
+ return .zero;
+ }
+
pub const toHaveBeenCalledWith = notImplementedJSCFn;
pub const toHaveBeenLastCalledWith = notImplementedJSCFn;
pub const toHaveBeenNthCalledWith = notImplementedJSCFn;
@@ -3953,7 +4036,6 @@ pub const Expect = struct {
pub const toHaveLastReturnedWith = notImplementedJSCFn;
pub const toHaveNthReturnedWith = notImplementedJSCFn;
pub const toContainEqual = notImplementedJSCFn;
- pub const toMatchObject = notImplementedJSCFn;
pub const toMatchInlineSnapshot = notImplementedJSCFn;
pub const toThrowErrorMatchingSnapshot = notImplementedJSCFn;
pub const toThrowErrorMatchingInlineSnapshot = notImplementedJSCFn;
@@ -3980,14 +4062,23 @@ pub const Expect = struct {
return ExpectAny.call(globalObject, callFrame);
}
+ pub fn anything(globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue {
+ return ExpectAnything.call(globalObject, callFrame);
+ }
+
+ pub fn stringContaining(globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue {
+ return ExpectStringContaining.call(globalObject, callFrame);
+ }
+
+ pub fn stringMatching(globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue {
+ return ExpectStringMatching.call(globalObject, callFrame);
+ }
+
pub const extend = notImplementedStaticFn;
- pub const anything = 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 {
diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig
index 0431b2e10..15ab88799 100644
--- a/src/bun.js/test/pretty_format.zig
+++ b/src/bun.js/test/pretty_format.zig
@@ -1264,6 +1264,43 @@ pub const JestPrettyFormat = struct {
} else if (value.as(JSC.ResolveMessage)) |resolve_log| {
resolve_log.msg.writeFormat(writer_, enable_ansi_colors) catch {};
return;
+ } else if (value.as(JSC.Jest.ExpectAnything) != null) {
+ this.addForNewLine("Anything".len);
+ writer.writeAll("Anything");
+ return;
+ } else if (value.as(JSC.Jest.ExpectAny) != null) {
+ const constructor_value = JSC.Jest.ExpectAny.constructorValueGetCached(value) orelse return;
+
+ this.addForNewLine("Any<".len);
+ writer.writeAll("Any<");
+
+ var class_name = ZigString.init(&name_buf);
+ constructor_value.getClassName(this.globalThis, &class_name);
+ this.addForNewLine(class_name.len);
+ writer.print(comptime Output.prettyFmt("<cyan>{}<r>", enable_ansi_colors), .{class_name});
+ writer.writeAll(">");
+
+ return;
+ } else if (value.as(JSC.Jest.ExpectStringContaining) != null) {
+ const substring_value = JSC.Jest.ExpectStringContaining.stringValueGetCached(value) orelse return;
+
+ this.addForNewLine("StringContaining ".len);
+ writer.writeAll("StringContaining ");
+ this.printAs(.String, Writer, writer_, substring_value, .String, enable_ansi_colors);
+
+ return;
+ } else if (value.as(JSC.Jest.ExpectStringMatching) != null) {
+ const test_value = JSC.Jest.ExpectStringMatching.testValueGetCached(value) orelse return;
+
+ this.addForNewLine("StringMatching ".len);
+ writer.writeAll("StringMatching ");
+
+ const original_quote_strings = this.quote_strings;
+ if (test_value.isRegExp()) this.quote_strings = false;
+ this.printAs(.String, Writer, writer_, test_value, .String, enable_ansi_colors);
+ this.quote_strings = original_quote_strings;
+
+ return;
} else if (jsType != .DOMWrapper) {
if (value.isCallable(this.globalThis.vm())) {
return this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors);
@@ -1701,14 +1738,7 @@ pub const JestPrettyFormat = struct {
value.getClassName(this.globalThis, &object_name);
if (!strings.eqlComptime(object_name.slice(), "Object")) {
- if (value.as(JSC.Jest.ExpectAny)) |_| {
- var constructor = JSC.Jest.ExpectAny.constructorValueGetCached(value) orelse unreachable;
- var constructor_name = ZigString.Empty;
- constructor.getNameProperty(this.globalThis, &constructor_name);
- writer.print("Any<{s}>", .{constructor_name});
- } else {
- writer.print("{s} {{}}", .{object_name});
- }
+ writer.print("{s} {{}}", .{object_name});
} else {
// don't write "Object"
writer.writeAll("{}");
diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig
index 9f83e550e..e1bd42984 100644
--- a/src/http/websocket_http_client.zig
+++ b/src/http/websocket_http_client.zig
@@ -933,6 +933,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type {
pub fn handleHandshake(this: *WebSocket, socket: Socket, success: i32, ssl_error: uws.us_bun_verify_error_t) void {
_ = socket;
_ = ssl_error;
+ JSC.markBinding(@src());
log("WebSocket.onHandshake({d})", .{success});
JSC.markBinding(@src());
if (success == 0) {
diff --git a/test/js/bun/test/test-test.test.ts b/test/js/bun/test/test-test.test.ts
index ed356aa50..ebb6ecfab 100644
--- a/test/js/bun/test/test-test.test.ts
+++ b/test/js/bun/test/test-test.test.ts
@@ -2522,6 +2522,215 @@ test("toBeOdd()", () => {
// FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:true}, 43).value).toBeOdd();
});
+describe("toMatchObject", () => {
+ test("with Bun.deepMatch", () => {
+ expect(Bun.deepMatch({ a: 1, b: 2 }, { a: 1 })).toBe(false);
+ expect(Bun.deepMatch({ a: 1 }, { a: 1, b: 2 })).toBe(true);
+ });
+ test("with expect matcher", () => {
+ const f = Symbol.for("foo");
+ const b = Symbol.for("bar");
+
+ class Number2 extends Number {
+ constructor(value) {
+ super(value);
+ }
+ }
+ class Number3 extends Number2 {
+ constructor(value) {
+ super(value);
+ }
+ }
+
+ class Boolean2 extends Boolean {
+ constructor(value) {
+ super(value);
+ }
+ }
+ expect({ [f]: 2 }).toMatchObject({ [f]: 2 });
+ expect({ [f]: 2 }).toMatchObject({ [f]: expect.anything() });
+ expect({ [f]: new Date() }).toMatchObject({ [f]: expect.any(Date) });
+ expect({ [f]: new Date() }).not.toMatchObject({ [f]: expect.any(RegExp) });
+ expect({ [f]: 3 }).not.toMatchObject({ [f]: 5 });
+ expect({ [f]: 3 }).not.toMatchObject({ [b]: 3 });
+ expect({}).toMatchObject({});
+ expect([5]).toMatchObject([5]);
+ expect([5]).not.toMatchObject([4]);
+ expect(() => {
+ expect({}).toMatchObject();
+ }).toThrow();
+ expect(() => {
+ expect(true).toMatchObject(true);
+ }).toThrow();
+ expect(() => {
+ expect(true).toMatchObject(true);
+ }).toThrow();
+ expect(() => {
+ expect(1).toMatchObject(1);
+ }).toThrow();
+ expect(() => {
+ expect("a").toMatchObject("a");
+ }).toThrow();
+ expect(() => {
+ expect(null).toMatchObject(null);
+ }).toThrow();
+ expect(() => {
+ expect(undefined).toMatchObject(undefined);
+ }).toThrow();
+ expect(() => {
+ expect(Symbol()).toMatchObject(Symbol());
+ }).toThrow();
+ expect(() => {
+ expect(BigInt(1)).toMatchObject(BigInt(1));
+ }).toThrow();
+ expect([]).toMatchObject([]);
+ expect([1]).toMatchObject([1]);
+ expect([1, 2]).toMatchObject([1, 2]);
+ expect(() => {
+ expect([1]).toMatchObject([1, 2]);
+ }).toThrow();
+ expect(() => {
+ expect([1, 2]).toMatchObject([1]);
+ }).toThrow();
+ expect([]).toMatchObject({});
+ expect([1]).toMatchObject({});
+ expect([1, 2]).toMatchObject({ 0: 1, 1: 2 });
+ expect([1, 2]).not.toMatchObject({ 0: 2 });
+ expect(() => {
+ expect({}).toMatchObject([]);
+ }).toThrow();
+ expect({ a: 1 }).toMatchObject({});
+ expect({ a: 1 }).toMatchObject({ a: expect.anything() });
+ expect({ a: 1, b: 2 }).toMatchObject({ a: 1 });
+ expect({ a: 1, b: 2 }).toMatchObject({ a: 1, b: 2 });
+ expect({ a: 1, b: 2 }).toMatchObject({ b: 2 });
+ expect({ a: 1, b: 2 }).toMatchObject({ b: 2, a: 1 });
+ expect({ a: 1, b: 2 }).toMatchObject({ a: 1, b: 2 });
+ expect({}).not.toMatchObject({ a: 1 });
+ expect({ a: 89 }).not.toMatchObject({ b: 90 });
+ expect({ a: 1, b: 2 }).not.toMatchObject({ a: 1, b: 3 });
+ expect({ a: 1, b: 2 }).not.toMatchObject({ a: 1, b: 2, c: 4 });
+ expect({ a: new Date(), b: "jj" }).not.toMatchObject({ b: expect.any(Number) });
+ expect({ a: "123" }).not.toMatchObject({ a: expect.stringContaining("4") });
+ class DString extends String {
+ constructor(str) {
+ super(str);
+ }
+ }
+ expect({ a: "hello world" }).toMatchObject({ a: expect.stringContaining("wor") });
+ expect({ a: "hello world" }).not.toMatchObject({ a: expect.stringContaining("wol") });
+ expect({ a: "hello String" }).toMatchObject({ a: expect.stringContaining(new String("Str")) });
+ expect({ a: "hello String" }).not.toMatchObject({ a: expect.stringContaining(new String("Strs")) });
+ expect({ a: "hello derived String" }).toMatchObject({ a: expect.stringContaining(new DString("riv")) });
+ expect({ a: "hello derived String" }).not.toMatchObject({ a: expect.stringContaining(new DString("rivd")) });
+ expect({ a: "hello world" }).toMatchObject({ a: expect.stringMatching("wor") });
+ expect({ a: "hello world" }).not.toMatchObject({ a: expect.stringMatching("word") });
+ expect({ a: "hello world" }).toMatchObject({ a: "hello world" });
+ expect({ a: "hello world" }).toMatchObject({ a: expect.stringMatching(/wor/) });
+ expect({ a: "hello world" }).not.toMatchObject({ a: expect.stringMatching(/word/) });
+ expect({ a: expect.stringMatching("wor") }).toMatchObject({ a: "hello world" });
+ expect({ a: expect.stringMatching("word") }).not.toMatchObject({ a: "hello world" });
+ expect({ a: expect.stringMatching(/wor/) }).toMatchObject({ a: "hello world" });
+ expect({ a: expect.stringMatching(/word/) }).not.toMatchObject({ a: "hello world" });
+ expect({ a: expect.stringMatching(/word/) }).toMatchObject({ a: "hello word" });
+ expect({ a: [1, 2, 3] }).toMatchObject({ a: [1, 2, 3] });
+ expect({ a: [1, 2, 3] }).toMatchObject({ a: [1, 2, 3] });
+ expect({ a: [1, 2, 4] }).not.toMatchObject({ a: [1, 2, 3] });
+
+ expect([]).toMatchObject([]);
+ expect([]).toMatchObject({});
+ expect({}).not.toMatchObject([]);
+ expect({ a: 1 }).toMatchObject({});
+ expect({ a: 1 }).toMatchObject({ a: 1 });
+
+ expect({ a: 1 }).toMatchObject({ a: expect.anything() });
+ expect({ a: null }).not.toMatchObject({ a: expect.anything() });
+ expect({ a: undefined }).not.toMatchObject({ a: expect.anything() });
+
+ expect({ a: new Date() }).toMatchObject({ a: expect.any(Date) });
+ expect({ a: new Date() }).not.toMatchObject({ a: expect.any(RegExp) });
+ expect({ a: new RegExp("a", "g") }).toMatchObject({ a: expect.any(RegExp) });
+ expect({ a: /a/g }).toMatchObject({ a: expect.any(RegExp) });
+
+ expect({
+ first: new Boolean2(false),
+ a: {
+ 4: [3, 2, 2],
+ j: new Date(),
+ b: {
+ c: {
+ num: 1,
+ d: {
+ e: {
+ bigint: 123n,
+ f: {
+ g: {
+ h: {
+ i: new Number3(2),
+ bool: true,
+ },
+ compare: "compare",
+ },
+ },
+ ignore1: 234,
+ ignore2: {
+ ignore3: 23421,
+ ignore4: {
+ ignore5: {
+ ignore6: "hello",
+ ignore7: "done",
+ },
+ },
+ },
+ },
+ },
+ string1: "hello",
+ string2: "hello",
+ string3: "hello",
+ },
+ },
+ },
+ }).toMatchObject({
+ first: expect.any(Boolean2),
+ a: {
+ 4: [3, 2, expect.any(Number)],
+
+ j: expect.any(Date),
+ b: {
+ c: {
+ num: expect.any(Number),
+ string1: expect.anything(),
+ string2: expect.stringContaining("ll"),
+ string3: expect.stringMatching(/ll/),
+ d: {
+ e: {
+ bigint: expect.any(BigInt),
+ f: {
+ g: {
+ compare: "compare",
+ h: {
+ i: expect.any(Number3),
+ bool: expect.any(Boolean),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ });
+
+ var a1 = [1];
+ a1[f] = 99;
+ expect(a1).not.toMatchObject([1]);
+ expect([1]).not.toMatchObject(a1);
+ expect({ 1: 1 }).not.toMatchObject(a1);
+ expect(a1).not.toMatchObject({ 1: 1 });
+ expect(a1).toMatchObject(a1);
+ });
+});
+
try {
test("test this doesnt crash");
} catch (e) {}