aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar WingLim <winglims@gmail.com> 2023-09-18 17:59:09 +0800
committerGravatar GitHub <noreply@github.com> 2023-09-18 02:59:09 -0700
commitc7de270bbba05e0fc850290b27d617bda1df2206 (patch)
tree955759fa7a7d4211a11938cec5e8ca7dff65e52c
parentc66d4a724b675c6ebbd64ccf98e1ce9933f2ef00 (diff)
downloadbun-c7de270bbba05e0fc850290b27d617bda1df2206.tar.gz
bun-c7de270bbba05e0fc850290b27d617bda1df2206.tar.zst
bun-c7de270bbba05e0fc850290b27d617bda1df2206.zip
feat(test): Implement `arrayContaining` (#5572)
* feat(test): implement `arrayContaining` * feat: early return when expectedArray is empty * feat: add test for toEqual * chore: use `JSC::isArray` * chore: use getIndex for performance * fix: use deepEqual --------- Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h1
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h1
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h4
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h9
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp168
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.h56
-rw-r--r--src/bun.js/bindings/bindings.cpp43
-rw-r--r--src/bun.js/bindings/generated_classes.zig82
-rw-r--r--src/bun.js/bindings/generated_classes_list.zig1
-rw-r--r--src/bun.js/test/expect.zig42
-rw-r--r--src/bun.js/test/jest.classes.ts12
-rw-r--r--test/js/bun/test/expect.test.js16
12 files changed, 432 insertions, 3 deletions
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h
index a364c0a48..cef5c512a 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h
@@ -16,6 +16,7 @@ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForEndTag;
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_clientSubspaceForExpectArrayContaining;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExpectStringContaining;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExpectStringMatching;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFFI;
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h
index e98ea16c3..d06451eda 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h
@@ -16,6 +16,7 @@ std::unique_ptr<IsoSubspace> m_subspaceForEndTag;
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_subspaceForExpectArrayContaining;
std::unique_ptr<IsoSubspace> m_subspaceForExpectStringContaining;
std::unique_ptr<IsoSubspace> m_subspaceForExpectStringMatching;
std::unique_ptr<IsoSubspace> m_subspaceForFFI;
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h
index 386e1ea80..381378262 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h
@@ -70,6 +70,10 @@ JSC::Structure* JSExpectAnythingStructure() { return m_JSExpectAnything.getIniti
JSC::JSObject* JSExpectAnythingConstructor() { return m_JSExpectAnything.constructorInitializedOnMainThread(this); }
JSC::JSValue JSExpectAnythingPrototype() { return m_JSExpectAnything.prototypeInitializedOnMainThread(this); }
JSC::LazyClassStructure m_JSExpectAnything;
+JSC::Structure* JSExpectArrayContainingStructure() { return m_JSExpectArrayContaining.getInitializedOnMainThread(this); }
+ JSC::JSObject* JSExpectArrayContainingConstructor() { return m_JSExpectArrayContaining.constructorInitializedOnMainThread(this); }
+ JSC::JSValue JSExpectArrayContainingPrototype() { return m_JSExpectArrayContaining.prototypeInitializedOnMainThread(this); }
+ JSC::LazyClassStructure m_JSExpectArrayContaining;
JSC::Structure* JSExpectStringContainingStructure() { return m_JSExpectStringContaining.getInitializedOnMainThread(this); }
JSC::JSObject* JSExpectStringContainingConstructor() { return m_JSExpectStringContaining.constructorInitializedOnMainThread(this); }
JSC::JSValue JSExpectStringContainingPrototype() { return m_JSExpectStringContaining.prototypeInitializedOnMainThread(this); }
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h
index b51752180..84d3df7a0 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h
@@ -1,4 +1,4 @@
-void GlobalObject::initGeneratedLazyClasses() {
+ALWAYS_INLINE void GlobalObject::initGeneratedLazyClasses() {
m_JSAttributeIterator.initLater(
[](LazyClassStructure::Initializer& init) {
init.setPrototype(WebCore::JSAttributeIterator::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global)));
@@ -107,6 +107,12 @@ void GlobalObject::initGeneratedLazyClasses() {
init.setStructure(WebCore::JSExpectAnything::createStructure(init.vm, init.global, init.prototype));
});
+ m_JSExpectArrayContaining.initLater(
+ [](LazyClassStructure::Initializer& init) {
+ init.setPrototype(WebCore::JSExpectArrayContaining::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global)));
+ init.setStructure(WebCore::JSExpectArrayContaining::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)));
@@ -321,6 +327,7 @@ void GlobalObject::visitGeneratedLazyClasses(GlobalObject *thisObject, Visitor&
thisObject->m_JSExpect.visit(visitor);
thisObject->m_JSExpectAny.visit(visitor);
thisObject->m_JSExpectAnything.visit(visitor);
+ thisObject->m_JSExpectArrayContaining.visit(visitor);
thisObject->m_JSExpectStringContaining.visit(visitor);
thisObject->m_JSExpectStringMatching.visit(visitor);
thisObject->m_JSFFI.visit(visitor);
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index ec2add296..b84c1cd16 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -9712,6 +9712,174 @@ extern "C" EncodedJSValue ExpectAnything__create(Zig::GlobalObject* globalObject
return JSValue::encode(instance);
}
+class JSExpectArrayContainingPrototype final : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+
+ static JSExpectArrayContainingPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSExpectArrayContainingPrototype* ptr = new (NotNull, JSC::allocateCell<JSExpectArrayContainingPrototype>(vm)) JSExpectArrayContainingPrototype(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:
+ JSExpectArrayContainingPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
+};
+
+extern "C" void ExpectArrayContainingClass__finalize(void*);
+extern "C" JSC_DECLARE_HOST_FUNCTION(ExpectArrayContainingClass__call);
+
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSExpectArrayContainingPrototype, JSExpectArrayContainingPrototype::Base);
+
+static const HashTableValue JSExpectArrayContainingPrototypeTableValues[] = {};
+
+const ClassInfo JSExpectArrayContainingPrototype::s_info = { "ExpectArrayContaining"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectArrayContainingPrototype) };
+
+extern "C" void ExpectArrayContainingPrototype__arrayValueSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue value)
+{
+ auto& vm = globalObject->vm();
+ auto* thisObject = jsCast<JSExpectArrayContaining*>(JSValue::decode(thisValue));
+ thisObject->m_arrayValue.set(vm, thisObject, JSValue::decode(value));
+}
+
+extern "C" EncodedJSValue ExpectArrayContainingPrototype__arrayValueGetCachedValue(JSC::EncodedJSValue thisValue)
+{
+ auto* thisObject = jsCast<JSExpectArrayContaining*>(JSValue::decode(thisValue));
+ return JSValue::encode(thisObject->m_arrayValue.get());
+}
+
+void JSExpectArrayContainingPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
+{
+ Base::finishCreation(vm);
+
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+JSExpectArrayContaining::~JSExpectArrayContaining()
+{
+ if (m_ctx) {
+ ExpectArrayContainingClass__finalize(m_ctx);
+ }
+}
+void JSExpectArrayContaining::destroy(JSCell* cell)
+{
+ static_cast<JSExpectArrayContaining*>(cell)->JSExpectArrayContaining::~JSExpectArrayContaining();
+}
+
+const ClassInfo JSExpectArrayContaining::s_info = { "ExpectArrayContaining"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSExpectArrayContaining) };
+
+void JSExpectArrayContaining::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+}
+
+JSExpectArrayContaining* JSExpectArrayContaining::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx)
+{
+ JSExpectArrayContaining* ptr = new (NotNull, JSC::allocateCell<JSExpectArrayContaining>(vm)) JSExpectArrayContaining(vm, structure, ctx);
+ ptr->finishCreation(vm);
+ return ptr;
+}
+
+extern "C" void* ExpectArrayContaining__fromJS(JSC::EncodedJSValue value)
+{
+ JSC::JSValue decodedValue = JSC::JSValue::decode(value);
+ if (decodedValue.isEmpty() || !decodedValue.isCell())
+ return nullptr;
+
+ JSC::JSCell* cell = decodedValue.asCell();
+ JSExpectArrayContaining* object = JSC::jsDynamicCast<JSExpectArrayContaining*>(cell);
+
+ if (!object)
+ return nullptr;
+
+ return object->wrapped();
+}
+
+extern "C" bool ExpectArrayContaining__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr)
+{
+ JSExpectArrayContaining* object = JSC::jsDynamicCast<JSExpectArrayContaining*>(JSValue::decode(value));
+ if (!object)
+ return false;
+
+ object->m_ctx = ptr;
+ return true;
+}
+
+extern "C" const size_t ExpectArrayContaining__ptrOffset = JSExpectArrayContaining::offsetOfWrapped();
+
+void JSExpectArrayContaining::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
+{
+ auto* thisObject = jsCast<JSExpectArrayContaining*>(cell);
+ if (void* wrapped = thisObject->wrapped()) {
+ // if (thisObject->scriptExecutionContext())
+ // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string());
+ }
+ Base::analyzeHeap(cell, analyzer);
+}
+
+JSObject* JSExpectArrayContaining::createPrototype(VM& vm, JSDOMGlobalObject* globalObject)
+{
+ return JSExpectArrayContainingPrototype::create(vm, globalObject, JSExpectArrayContainingPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
+}
+
+extern "C" EncodedJSValue ExpectArrayContaining__create(Zig::GlobalObject* globalObject, void* ptr)
+{
+ auto& vm = globalObject->vm();
+ JSC::Structure* structure = globalObject->JSExpectArrayContainingStructure();
+ JSExpectArrayContaining* instance = JSExpectArrayContaining::create(vm, globalObject, structure, ptr);
+
+ return JSValue::encode(instance);
+}
+
+template<typename Visitor>
+void JSExpectArrayContaining::visitChildrenImpl(JSCell* cell, Visitor& visitor)
+{
+ JSExpectArrayContaining* thisObject = jsCast<JSExpectArrayContaining*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ Base::visitChildren(thisObject, visitor);
+
+ thisObject->visitAdditionalChildren<Visitor>(visitor);
+}
+
+DEFINE_VISIT_CHILDREN(JSExpectArrayContaining);
+
+template<typename Visitor>
+void JSExpectArrayContaining::visitAdditionalChildren(Visitor& visitor)
+{
+ JSExpectArrayContaining* thisObject = this;
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ visitor.append(thisObject->m_arrayValue);
+}
+
+DEFINE_VISIT_ADDITIONAL_CHILDREN(JSExpectArrayContaining);
+
+template<typename Visitor>
+void JSExpectArrayContaining::visitOutputConstraintsImpl(JSCell* cell, Visitor& visitor)
+{
+ JSExpectArrayContaining* thisObject = jsCast<JSExpectArrayContaining*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ thisObject->visitAdditionalChildren<Visitor>(visitor);
+}
+
+DEFINE_VISIT_OUTPUT_CONSTRAINTS(JSExpectArrayContaining);
class JSExpectStringContainingPrototype 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 142a96746..33aa58487 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses.h
@@ -1004,6 +1004,62 @@ public:
void finishCreation(JSC::VM&);
};
+class JSExpectArrayContaining final : public JSC::JSDestructibleObject {
+public:
+ using Base = JSC::JSDestructibleObject;
+ static JSExpectArrayContaining* 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<JSExpectArrayContaining, WebCore::UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForExpectArrayContaining.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForExpectArrayContaining = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForExpectArrayContaining.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForExpectArrayContaining = 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);
+ ;
+
+ ~JSExpectArrayContaining();
+
+ void* wrapped() const { return m_ctx; }
+
+ void detach()
+ {
+ m_ctx = nullptr;
+ }
+
+ static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
+ static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSExpectArrayContaining, m_ctx); }
+
+ void* m_ctx { nullptr };
+
+ JSExpectArrayContaining(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_arrayValue;
+};
+
class JSExpectStringContaining final : public JSC::JSDestructibleObject {
public:
using Base = JSC::JSDestructibleObject;
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index 173a454a1..47ef36aea 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -230,6 +230,47 @@ AsymmetricMatcherResult matchAsymmetricMatcher(JSGlobalObject* globalObject, JSC
}
return AsymmetricMatcherResult::FAIL;
+ } else if (auto* expectArrayContaining = jsDynamicCast<JSExpectArrayContaining*>(matcherPropCell)) {
+ JSValue expectedArrayValue = expectArrayContaining->m_arrayValue.get();
+
+ if (JSC::isArray(globalObject, otherProp)) {
+ if (JSC::isArray(globalObject, expectedArrayValue)) {
+ JSArray* expectedArray = jsDynamicCast<JSArray*>(expectedArrayValue);
+ JSArray* otherArray = jsDynamicCast<JSArray*>(otherProp);
+
+ unsigned expectedLength = expectedArray->length();
+ unsigned otherLength = otherArray->length();
+
+ // A empty array is all array's subset
+ if (expectedLength == 0) {
+ return AsymmetricMatcherResult::PASS;
+ }
+
+ // O(m*n) but works for now
+ for (unsigned m = 0; m < expectedLength; m++) {
+ JSValue expectedValue = expectedArray->getIndex(globalObject, m);
+ bool found = false;
+
+ for (unsigned n = 0; n < otherLength; n++) {
+ JSValue otherValue = otherArray->getIndex(globalObject, n);
+ ThrowScope scope = DECLARE_THROW_SCOPE(globalObject->vm());
+ Vector<std::pair<JSValue, JSValue>, 16> stack;
+ if (Bun__deepEquals<false, true>(globalObject, expectedValue, otherValue, stack, &scope, true)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ return AsymmetricMatcherResult::FAIL;
+ }
+ }
+
+ return AsymmetricMatcherResult::PASS;
+ }
+ }
+
+ return AsymmetricMatcherResult::FAIL;
}
return AsymmetricMatcherResult::NOT_MATCHER;
@@ -4702,4 +4743,4 @@ CPP_DECL void JSC__VM__setControlFlowProfiler(JSC__VM* vm, bool isEnabled)
} else {
vm->disableControlFlowProfiler();
}
-} \ No newline at end of file
+}
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index 329506718..581d4a5f3 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -2507,6 +2507,87 @@ pub const JSExpectAnything = struct {
}
}
};
+pub const JSExpectArrayContaining = struct {
+ const ExpectArrayContaining = Classes.ExpectArrayContaining;
+ const GetterType = fn (*ExpectArrayContaining, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const GetterTypeWithThisValue = fn (*ExpectArrayContaining, JSC.JSValue, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const SetterType = fn (*ExpectArrayContaining, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const SetterTypeWithThisValue = fn (*ExpectArrayContaining, JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const CallbackType = fn (*ExpectArrayContaining, *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) ?*ExpectArrayContaining {
+ JSC.markBinding(@src());
+ return ExpectArrayContaining__fromJS(value);
+ }
+
+ extern fn ExpectArrayContainingPrototype__arrayValueSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void;
+
+ extern fn ExpectArrayContainingPrototype__arrayValueGetCachedValue(JSC.JSValue) JSC.JSValue;
+
+ /// `ExpectArrayContaining.arrayValue` setter
+ /// This value will be visited by the garbage collector.
+ pub fn arrayValueSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ ExpectArrayContainingPrototype__arrayValueSetCachedValue(thisValue, globalObject, value);
+ }
+
+ /// `ExpectArrayContaining.arrayValue` getter
+ /// This value will be visited by the garbage collector.
+ pub fn arrayValueGetCached(thisValue: JSC.JSValue) ?JSC.JSValue {
+ JSC.markBinding(@src());
+ const result = ExpectArrayContainingPrototype__arrayValueGetCachedValue(thisValue);
+ if (result == .zero)
+ return null;
+
+ return result;
+ }
+
+ /// Create a new instance of ExpectArrayContaining
+ pub fn toJS(this: *ExpectArrayContaining, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
+ JSC.markBinding(@src());
+ if (comptime Environment.allow_assert) {
+ const value__ = ExpectArrayContaining__create(globalObject, this);
+ std.debug.assert(value__.as(ExpectArrayContaining).? == this); // If this fails, likely a C ABI issue.
+ return value__;
+ } else {
+ return ExpectArrayContaining__create(globalObject, this);
+ }
+ }
+
+ /// Modify the internal ptr to point to a new instance of ExpectArrayContaining.
+ pub fn dangerouslySetPtr(value: JSC.JSValue, ptr: ?*ExpectArrayContaining) bool {
+ JSC.markBinding(@src());
+ return ExpectArrayContaining__dangerouslySetPtr(value, ptr);
+ }
+
+ /// Detach the ptr from the thisValue
+ pub fn detachPtr(_: *ExpectArrayContaining, value: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ std.debug.assert(ExpectArrayContaining__dangerouslySetPtr(value, null));
+ }
+
+ extern fn ExpectArrayContaining__fromJS(JSC.JSValue) ?*ExpectArrayContaining;
+ extern fn ExpectArrayContaining__getConstructor(*JSC.JSGlobalObject) JSC.JSValue;
+
+ extern fn ExpectArrayContaining__create(globalObject: *JSC.JSGlobalObject, ptr: ?*ExpectArrayContaining) JSC.JSValue;
+
+ extern fn ExpectArrayContaining__dangerouslySetPtr(JSC.JSValue, ?*ExpectArrayContaining) bool;
+
+ comptime {
+ if (@TypeOf(ExpectArrayContaining.finalize) != (fn (*ExpectArrayContaining) callconv(.C) void)) {
+ @compileLog("ExpectArrayContaining.finalize is not a finalizer");
+ }
+
+ if (@TypeOf(ExpectArrayContaining.call) != StaticCallbackType)
+ @compileLog("Expected ExpectArrayContaining.call to be a static callback");
+ if (!JSC.is_bindgen) {
+ @export(ExpectArrayContaining.call, .{ .name = "ExpectArrayContainingClass__call" });
+ @export(ExpectArrayContaining.finalize, .{ .name = "ExpectArrayContainingClass__finalize" });
+ }
+ }
+};
pub const JSExpectStringContaining = struct {
const ExpectStringContaining = Classes.ExpectStringContaining;
const GetterType = fn (*ExpectStringContaining, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
@@ -7006,6 +7087,7 @@ comptime {
_ = JSExpect;
_ = JSExpectAny;
_ = JSExpectAnything;
+ _ = JSExpectArrayContaining;
_ = JSExpectStringContaining;
_ = JSExpectStringMatching;
_ = JSFFI;
diff --git a/src/bun.js/bindings/generated_classes_list.zig b/src/bun.js/bindings/generated_classes_list.zig
index a86f74dd3..3b45f33b8 100644
--- a/src/bun.js/bindings/generated_classes_list.zig
+++ b/src/bun.js/bindings/generated_classes_list.zig
@@ -17,6 +17,7 @@ pub const Classes = struct {
pub const ExpectAnything = JSC.Expect.ExpectAnything;
pub const ExpectStringContaining = JSC.Expect.ExpectStringContaining;
pub const ExpectStringMatching = JSC.Expect.ExpectStringMatching;
+ pub const ExpectArrayContaining = JSC.Expect.ExpectArrayContaining;
pub const FileSystemRouter = JSC.API.FileSystemRouter;
pub const Bundler = JSC.API.JSBundler;
pub const JSBundler = Bundler;
diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig
index 0d3ff663e..6434e3702 100644
--- a/src/bun.js/test/expect.zig
+++ b/src/bun.js/test/expect.zig
@@ -3437,8 +3437,11 @@ pub const Expect = struct {
return ExpectStringMatching.call(globalObject, callFrame);
}
+ pub fn arrayContaining(globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue {
+ return ExpectArrayContaining.call(globalObject, callFrame);
+ }
+
pub const extend = notImplementedStaticFn;
- pub const arrayContaining = notImplementedStaticFn;
pub const assertions = notImplementedStaticFn;
pub const hasAssertions = notImplementedStaticFn;
pub const objectContaining = notImplementedStaticFn;
@@ -3621,6 +3624,43 @@ pub const ExpectAny = struct {
}
};
+pub const ExpectArrayContaining = struct {
+ pub usingnamespace JSC.Codegen.JSExpectArrayContaining;
+
+ pub fn finalize(
+ this: *ExpectArrayContaining,
+ ) 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].jsType().isArray()) {
+ const fmt = "<d>expect.<r>arrayContaining<d>(<r>array<d>)<r>\n\nExpected a array\n";
+ globalObject.throwPretty(fmt, .{});
+ return .zero;
+ }
+
+ const array_value = args[0];
+ const array_containing = globalObject.bunVM().allocator.create(ExpectArrayContaining) catch unreachable;
+
+ if (Jest.runner.?.pending_test == null) {
+ const err = globalObject.createErrorInstance("expect.arrayContaining() must be called in a test", .{});
+ err.put(globalObject, ZigString.static("name"), ZigString.init("TestNotRunningError").toValueGC(globalObject));
+ globalObject.throwValue(err);
+ return .zero;
+ }
+
+ const array_containing_js_value = array_containing.toJS(globalObject);
+ ExpectArrayContaining.arrayValueSetCached(array_containing_js_value, globalObject, array_value);
+
+ var vm = globalObject.bunVM();
+ vm.autoGarbageCollect();
+ return array_containing_js_value;
+ }
+};
+
/// JSValue.zero is used to indicate it was not a JSMockFunction
/// If there were no calls, it returns an empty JSArray*
extern fn JSMockFunction__getCalls(JSValue) JSValue;
diff --git a/src/bun.js/test/jest.classes.ts b/src/bun.js/test/jest.classes.ts
index d40acbf07..186c8f9a8 100644
--- a/src/bun.js/test/jest.classes.ts
+++ b/src/bun.js/test/jest.classes.ts
@@ -49,6 +49,18 @@ export default [
proto: {},
}),
define({
+ name: "ExpectArrayContaining",
+ construct: false,
+ noConstructor: true,
+ call: true,
+ finalize: true,
+ JSType: "0b11101110",
+ values: ["arrayValue"],
+ configurable: false,
+ klass: {},
+ proto: {},
+ }),
+ define({
name: "Expect",
construct: true,
call: true,
diff --git a/test/js/bun/test/expect.test.js b/test/js/bun/test/expect.test.js
index 804a5339f..4b9e5ec22 100644
--- a/test/js/bun/test/expect.test.js
+++ b/test/js/bun/test/expect.test.js
@@ -1298,6 +1298,16 @@ describe("expect()", () => {
a = { a: 1, b: 2, c: 3 };
b = { a: 1, b: 2 };
expect(a).not.toEqual(b);
+
+ array1 = [1, 2, 3];
+ expect(array1).toEqual(expect.arrayContaining([]));
+ expect(array1).toEqual(expect.arrayContaining([1, 2]));
+ expect(array1).not.toEqual(expect.arrayContaining([1, 2, 4]));
+
+ array2 = [{ a: 1, b: 2 }, { a: { a: 1 } }];
+ expect(array2).toEqual(expect.arrayContaining([{ a: 1, b: 2 }]));
+ expect(array2).toEqual(expect.arrayContaining([{ a: { a: 1 } }]));
+ expect(array2).not.toEqual(expect.arrayContaining([{ a: 2, b: 3 }]));
});
test("symbol based keys in arrays are processed correctly", () => {
@@ -2624,6 +2634,12 @@ describe("expect()", () => {
expect({ a: [1, 2, 3] }).toMatchObject({ a: [1, 2, 3] });
expect({ a: [1, 2, 4] }).not.toMatchObject({ a: [1, 2, 3] });
+ expect({ a: [1, 2, 3] }).toMatchObject({ a: expect.arrayContaining([1, 2]) });
+ expect({ a: [1, 2, 3] }).not.toMatchObject({ a: expect.arrayContaining([4]) });
+ expect({ a: ['hello', 'world'] }).toMatchObject({ a: expect.arrayContaining([]) });
+ expect({ a: ['hello', 'world'] }).toMatchObject({ a: expect.arrayContaining(['world']) });
+ expect({ a: ['hello', 'world'] }).not.toMatchObject({ a: expect.arrayContaining(['hello', 'mars']) });
+
expect([]).toMatchObject([]);
expect([]).toMatchObject({});
expect({}).not.toMatchObject([]);