diff options
author | 2023-09-18 17:59:09 +0800 | |
---|---|---|
committer | 2023-09-18 02:59:09 -0700 | |
commit | c7de270bbba05e0fc850290b27d617bda1df2206 (patch) | |
tree | 955759fa7a7d4211a11938cec5e8ca7dff65e52c /src/bun.js | |
parent | c66d4a724b675c6ebbd64ccf98e1ce9933f2ef00 (diff) | |
download | bun-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>
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h | 4 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h | 9 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses.cpp | 168 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses.h | 56 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 43 | ||||
-rw-r--r-- | src/bun.js/bindings/generated_classes.zig | 82 | ||||
-rw-r--r-- | src/bun.js/bindings/generated_classes_list.zig | 1 | ||||
-rw-r--r-- | src/bun.js/test/expect.zig | 42 | ||||
-rw-r--r-- | src/bun.js/test/jest.classes.ts | 12 |
11 files changed, 416 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, |