diff options
30 files changed, 4662 insertions, 3719 deletions
diff --git a/.github/workflows/bun-linux-build.yml b/.github/workflows/bun-linux-build.yml index f7665ab9a..603c0ad3d 100644 --- a/.github/workflows/bun-linux-build.yml +++ b/.github/workflows/bun-linux-build.yml @@ -45,7 +45,7 @@ jobs: tag: linux-x64 arch: x86_64 build_arch: amd64 - runner: ubuntu-latest-xl + runner: big-ubuntu webkit_url: "https://github.com/oven-sh/WebKit/releases/download/may20/bun-webkit-linux-amd64-lto.tar.gz" webkit_basename: "bun-webkit-linux-amd64-lto" build_machine_arch: x86_64 @@ -53,7 +53,7 @@ jobs: tag: linux-x64-baseline arch: x86_64 build_arch: amd64 - runner: ubuntu-latest-xl + runner: big-ubuntu webkit_url: "https://github.com/oven-sh/WebKit/releases/download/may20/bun-webkit-linux-amd64-lto.tar.gz" webkit_basename: "bun-webkit-linux-amd64-lto" build_machine_arch: x86_64 diff --git a/.github/workflows/bun-mac-aarch64.yml b/.github/workflows/bun-mac-aarch64.yml index 038bf741f..6ac6b958e 100644 --- a/.github/workflows/bun-mac-aarch64.yml +++ b/.github/workflows/bun-mac-aarch64.yml @@ -32,7 +32,7 @@ on: jobs: macos-object-files: name: macOS Object - runs-on: ubuntu-latest-xl + runs-on: med-ubuntu if: github.repository_owner == 'oven-sh' strategy: matrix: diff --git a/.github/workflows/bun-mac-x64-baseline.yml b/.github/workflows/bun-mac-x64-baseline.yml index 7effc5642..c7a021c66 100644 --- a/.github/workflows/bun-mac-x64-baseline.yml +++ b/.github/workflows/bun-mac-x64-baseline.yml @@ -32,7 +32,7 @@ on: jobs: macos-object-files: name: macOS Object - runs-on: ubuntu-latest-xl + runs-on: med-ubuntu if: github.repository_owner == 'oven-sh' strategy: matrix: diff --git a/.github/workflows/bun-mac-x64.yml b/.github/workflows/bun-mac-x64.yml index b9522344c..d8402d95d 100644 --- a/.github/workflows/bun-mac-x64.yml +++ b/.github/workflows/bun-mac-x64.yml @@ -32,7 +32,7 @@ on: jobs: macos-object-files: name: macOS Object - runs-on: ubuntu-latest-xl + runs-on: med-ubuntu if: github.repository_owner == 'oven-sh' strategy: matrix: Binary files differdiff --git a/docs/runtime/modules.md b/docs/runtime/modules.md index 32bd78cc3..42f2a08e6 100644 --- a/docs/runtime/modules.md +++ b/docs/runtime/modules.md @@ -108,8 +108,8 @@ Once it finds the `foo` package, Bun reads the `package.json` to determine how t "worker": "./index.js", "module": "./index.js", "node": "./index.js", - "browser": "./index.js", - "default": "./index.js" // lowest priority + "default": "./index.js", + "browser": "./index.js" // lowest priority } } ``` diff --git a/packages/bun-types/bun-test.d.ts b/packages/bun-types/bun-test.d.ts index c26ece304..ba59966ad 100644 --- a/packages/bun-types/bun-test.d.ts +++ b/packages/bun-types/bun-test.d.ts @@ -442,7 +442,7 @@ declare module "bun:test" { toBe(expected: T): void; /** * Asserts that a number is odd. - * + * * @link https://jest-extended.jestcommunity.dev/docs/matchers/number/#tobeodd * @example * expect(1).toBeOdd(); @@ -451,7 +451,7 @@ declare module "bun:test" { toBeOdd(): void; /** * Asserts that a number is even. - * + * * @link https://jest-extended.jestcommunity.dev/docs/matchers/number/#tobeeven * @example * expect(2).toBeEven(); @@ -740,7 +740,7 @@ declare module "bun:test" { toBeNil(): void; /** * Asserts that a value is a `array`. - * + * * @link https://jest-extended.jestcommunity.dev/docs/matchers/array/#tobearray * @example * expect([1]).toBeArray(); @@ -750,9 +750,9 @@ declare module "bun:test" { toBeArray(): void; /** * Asserts that a value is a `array` of a certain length. - * + * * @link https://jest-extended.jestcommunity.dev/docs/matchers/array/#tobearrayofsize - * @example + * @example * expect([]).toBeArrayOfSize(0); * expect([1]).toBeArrayOfSize(1); * expect(new Array(1)).toBeArrayOfSize(1); @@ -780,14 +780,24 @@ declare module "bun:test" { toBeTrue(): void; /** * Asserts that a value matches a specific type. - * + * * @link https://vitest.dev/api/expect.html#tobetypeof * @example * expect(1).toBeTypeOf("number"); * expect("hello").toBeTypeOf("string"); * expect([]).not.toBeTypeOf("boolean"); */ - toBeTypeOf(type: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined'): void; + toBeTypeOf( + type: + | "bigint" + | "boolean" + | "function" + | "number" + | "object" + | "string" + | "symbol" + | "undefined", + ): void; /** * Asserts that a value is `false`. * @@ -1089,12 +1099,12 @@ declare namespace JestMock { * List of the call order indexes of the mock. Jest is indexing the order of * invocations of all mocks in a test file. The index is starting with `1`. */ - // invocationCallOrder: Array<number>; + invocationCallOrder: Array<number>; /** * List of the call arguments of the last call that was made to the mock. * If the function was not called, it will return `undefined`. */ - // lastCall?: Parameters<T>; + lastCall?: Parameters<T>; /** * List of the results of all calls that have been made to the mock. */ diff --git a/src/bun.js/bindings/JSBufferList.cpp b/src/bun.js/bindings/JSBufferList.cpp index 409d50df6..a8cefa710 100644 --- a/src/bun.js/bindings/JSBufferList.cpp +++ b/src/bun.js/bindings/JSBufferList.cpp @@ -445,4 +445,4 @@ void JSBufferListConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* const ClassInfo JSBufferListConstructor::s_info = { "BufferList"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBufferListConstructor) }; -} // namespace Zig
\ No newline at end of file +} // namespace Zig diff --git a/src/bun.js/bindings/JSMockFunction.cpp b/src/bun.js/bindings/JSMockFunction.cpp index fcd80102b..b7c2659b4 100644 --- a/src/bun.js/bindings/JSMockFunction.cpp +++ b/src/bun.js/bindings/JSMockFunction.cpp @@ -22,9 +22,31 @@ namespace Bun { +/** + * intended to be used in an if statement as an abstraction over this double if statement + * + * if(jsValue) { + * if(auto value = jsDynamicCast(jsValue)) { + * ... + * } + * } + * + * the reason this is needed is because jsDynamicCast will segfault if given a zero JSValue + */ +template<typename To> +inline To tryJSDynamicCast(JSValue from) +{ + if (UNLIKELY(!from)) + return nullptr; + if (UNLIKELY(!from.isCell())) + return nullptr; + return jsDynamicCast<To>(from.asCell()); +} + JSC_DECLARE_HOST_FUNCTION(jsMockFunctionCall); JSC_DECLARE_CUSTOM_GETTER(jsMockFunctionGetter_protoImpl); JSC_DECLARE_CUSTOM_GETTER(jsMockFunctionGetter_mock); +JSC_DECLARE_HOST_FUNCTION(jsMockFunctionGetter_mockGetLastCall); JSC_DECLARE_HOST_FUNCTION(jsMockFunctionGetMockImplementation); JSC_DECLARE_HOST_FUNCTION(jsMockFunctionGetMockName); JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockClear); @@ -40,8 +62,10 @@ JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockResolvedValue); JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockResolvedValueOnce); JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockRejectedValue); JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockRejectedValueOnce); +JSC_DECLARE_HOST_FUNCTION(jsMockFunctionWithImplementationCleanup); JSC_DECLARE_HOST_FUNCTION(jsMockFunctionWithImplementation); -JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockImplementationOnce); + +uint64_t JSMockModule::s_nextInvocationId = 0; // This is taken from JSWeakSet // We only want to hold onto the list of active spies which haven't already been collected @@ -79,9 +103,7 @@ class JSMockImplementation final : public JSNonFinalObject { public: enum class Kind : uint8_t { Call, - Promise, ReturnValue, - ThrowValue, ReturnThis, }; @@ -112,7 +134,14 @@ public: static constexpr unsigned numberOfInternalFields = 2; - mutable JSC::WriteBarrier<Unknown> internalFields[2]; + // either a function or a return value, depends on kind + mutable JSC::WriteBarrier<Unknown> underlyingValue; + + // a combination of a pointer to the next implementation and a flag indicating if this is a once implementation + // - undefined - no next value + // - jsNumber(1) - no next value + is a once implementation + // - JSMockImplementation - next value + is a once implementation + mutable JSC::WriteBarrier<Unknown> nextValueOrSentinel; DECLARE_EXPORT_INFO; DECLARE_VISIT_CHILDREN; @@ -121,11 +150,7 @@ public: bool isOnce() { - auto secondField = internalFields[1].get(); - if (secondField.isNumber() && secondField.asInt32() == 1) { - return true; - } - return jsDynamicCast<JSMockImplementation*>(secondField.asCell()); + return !nextValueOrSentinel.get().isUndefined(); } JSMockImplementation(JSC::VM& vm, JSC::Structure* structure, Kind kind) @@ -137,8 +162,8 @@ public: void finishCreation(JSC::VM& vm, JSC::JSValue first, JSC::JSValue second) { Base::finishCreation(vm); - this->internalFields[0].set(vm, this, first); - this->internalFields[1].set(vm, this, second); + this->underlyingValue.set(vm, this, first); + this->nextValueOrSentinel.set(vm, this, second); } }; @@ -149,32 +174,17 @@ void JSMockImplementation::visitChildrenImpl(JSCell* cell, Visitor& visitor) ASSERT_GC_OBJECT_INHERITS(fn, info()); Base::visitChildren(fn, visitor); - visitor.append(fn->internalFields[0]); - visitor.append(fn->internalFields[1]); + visitor.append(fn->underlyingValue); + visitor.append(fn->nextValueOrSentinel); } DEFINE_VISIT_CHILDREN(JSMockImplementation); enum class CallbackKind : uint8_t { - Wrapper, Call, GetterSetter, }; -static NativeFunction jsMockFunctionForCallbackKind(CallbackKind kind) -{ - switch (kind) { - // return jsMockFunctionGetterSetter; - case CallbackKind::Wrapper: - return jsMockFunctionMockImplementation; - case CallbackKind::GetterSetter: - case CallbackKind::Call: - return jsMockFunctionCall; - default: - RELEASE_ASSERT_NOT_REACHED(); - } -} - const ClassInfo JSMockImplementation::s_info = { "MockImpl"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMockImplementation) }; class JSMockFunction : public JSC::InternalFunction { @@ -197,12 +207,20 @@ public: DECLARE_VISIT_CHILDREN; JSC::LazyProperty<JSMockFunction, JSObject> mock; + // three pointers to implementation objects + // head of the list, this one is run next mutable JSC::WriteBarrier<JSC::Unknown> implementation; + // this contains the non-once implementation. there is only ever one of these + mutable JSC::WriteBarrier<JSC::Unknown> fallbackImplmentation; + // the last once implementation + mutable JSC::WriteBarrier<JSC::Unknown> tail; + // original implementation from spy. separate from `implementation` so restoration always works + mutable JSC::WriteBarrier<JSC::Unknown> spyOriginal; mutable JSC::WriteBarrier<JSC::JSArray> calls; mutable JSC::WriteBarrier<JSC::JSArray> contexts; + mutable JSC::WriteBarrier<JSC::JSArray> invocationCallOrder; mutable JSC::WriteBarrier<JSC::JSArray> instances; mutable JSC::WriteBarrier<JSC::JSArray> returnValues; - mutable JSC::WriteBarrier<JSC::Unknown> tail; JSC::Weak<JSObject> spyTarget; JSC::Identifier spyIdentifier; @@ -214,6 +232,28 @@ public: this->putDirect(vm, vm.propertyNames->name, jsString(vm, name), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly); } + void copyNameAndLength(JSC::VM& vm, JSGlobalObject* global, JSC::JSValue value) + { + auto catcher = DECLARE_CATCH_SCOPE(vm); + WTF::String nameToUse; + if (auto* fn = jsDynamicCast<JSFunction*>(value)) { + nameToUse = fn->name(vm); + JSValue lengthJSValue = fn->get(global, vm.propertyNames->length); + if (lengthJSValue.isNumber()) { + this->putDirect(vm, vm.propertyNames->length, (lengthJSValue), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly); + } + } else if (auto* fn = jsDynamicCast<InternalFunction*>(value)) { + nameToUse = fn->name(); + } else { + nameToUse = "mockConstructor"_s; + } + this->setName(nameToUse); + + if (catcher.exception()) { + catcher.clearException(); + } + } + void initMock() { mock.initLater( @@ -226,11 +266,12 @@ public: object->putDirectOffset(init.vm, 1, mock->getContexts()); object->putDirectOffset(init.vm, 2, mock->getInstances()); object->putDirectOffset(init.vm, 3, mock->getReturnValues()); + object->putDirectOffset(init.vm, 4, mock->getInvocationCallOrder()); init.set(object); }); } - void reset() + void clear() { this->calls.clear(); this->instances.clear(); @@ -242,12 +283,22 @@ public: } } + void reset() + { + this->clear(); + this->implementation.clear(); + this->fallbackImplmentation.clear(); + this->tail.clear(); + } + void clearSpy() { + this->reset(); + if (auto* target = this->spyTarget.get()) { - JSValue implValue = jsUndefined(); - if (auto* impl = jsDynamicCast<JSMockImplementation*>(this->implementation.get())) { - implValue = impl->internalFields[0].get(); + JSValue implValue = this->spyOriginal.get(); + if (!implValue) { + implValue = jsUndefined(); } // Reset the spy back to the original value. @@ -295,6 +346,15 @@ public: } return val; } + JSArray* getInvocationCallOrder() const + { + JSArray* val = invocationCallOrder.get(); + if (!val) { + val = JSC::constructEmptyArray(globalObject(), nullptr, 0); + this->invocationCallOrder.set(vm(), this, val); + } + return val; + } template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) @@ -310,7 +370,7 @@ public: } JSMockFunction(JSC::VM& vm, JSC::Structure* structure, CallbackKind wrapKind) - : Base(vm, structure, jsMockFunctionForCallbackKind(wrapKind), jsMockFunctionForCallbackKind(wrapKind)) + : Base(vm, structure, jsMockFunctionCall, jsMockFunctionCall) { initMock(); } @@ -324,41 +384,56 @@ void JSMockFunction::visitChildrenImpl(JSCell* cell, Visitor& visitor) Base::visitChildren(fn, visitor); visitor.append(fn->implementation); + visitor.append(fn->tail); + visitor.append(fn->fallbackImplmentation); visitor.append(fn->calls); visitor.append(fn->contexts); visitor.append(fn->instances); visitor.append(fn->returnValues); - visitor.append(fn->tail); + visitor.append(fn->invocationCallOrder); fn->mock.visit(visitor); } DEFINE_VISIT_CHILDREN(JSMockFunction); -static void pushImplInternal(JSMockFunction* fn, JSGlobalObject* jsGlobalObject, JSMockImplementation::Kind kind, JSValue value, bool isOnce) +static void pushImpl(JSMockFunction* fn, JSGlobalObject* jsGlobalObject, JSMockImplementation::Kind kind, JSValue value) { Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(jsGlobalObject); auto& vm = globalObject->vm(); - JSMockImplementation* impl = JSMockImplementation::create(globalObject, globalObject->mockModule.mockImplementationStructure.getInitializedOnMainThread(globalObject), kind, value, isOnce); - JSValue currentTail = fn->tail.get(); - JSValue currentImpl = fn->implementation.get(); - if (currentTail) { - if (auto* current = jsDynamicCast<JSMockImplementation*>(currentTail)) { - current->internalFields[1].set(vm, current, impl); - } + + if (auto* current = tryJSDynamicCast<JSMockImplementation*>(fn->fallbackImplmentation.get())) { + current->underlyingValue.set(vm, current, value); + current->kind = kind; + return; } - fn->tail.set(vm, fn, impl); - if (!currentImpl || !currentImpl.inherits<JSMockImplementation>()) { + + JSMockImplementation* impl = JSMockImplementation::create(globalObject, globalObject->mockModule.mockImplementationStructure.getInitializedOnMainThread(globalObject), kind, value, false); + fn->fallbackImplmentation.set(vm, fn, impl); + if (auto* tail = tryJSDynamicCast<JSMockImplementation*>(fn->tail.get())) { + tail->nextValueOrSentinel.set(vm, tail, impl); + } else { fn->implementation.set(vm, fn, impl); } } -static void pushImpl(JSMockFunction* fn, JSGlobalObject* globalObject, JSMockImplementation::Kind kind, JSValue value) +static void pushImplOnce(JSMockFunction* fn, JSGlobalObject* jsGlobalObject, JSMockImplementation::Kind kind, JSValue value) { - pushImplInternal(fn, globalObject, kind, value, false); -} + Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(jsGlobalObject); + auto& vm = globalObject->vm(); -static void pushImplOnce(JSMockFunction* fn, JSGlobalObject* globalObject, JSMockImplementation::Kind kind, JSValue value) -{ - pushImplInternal(fn, globalObject, kind, value, true); + JSMockImplementation* impl = JSMockImplementation::create(globalObject, globalObject->mockModule.mockImplementationStructure.getInitializedOnMainThread(globalObject), kind, value, true); + + if (!fn->implementation.get()) { + fn->implementation.set(vm, fn, impl); + } + if (auto* tail = tryJSDynamicCast<JSMockImplementation*>(fn->tail.get())) { + tail->nextValueOrSentinel.set(vm, tail, impl); + } else { + fn->implementation.set(vm, fn, impl); + } + if (auto fallback = fn->fallbackImplmentation.get()) { + impl->nextValueOrSentinel.set(vm, impl, fallback); + } + fn->tail.set(vm, fn, impl); } class JSMockFunctionPrototype final : public JSC::JSNonFinalObject { @@ -409,8 +484,8 @@ static const HashTableValue JSMockFunctionPrototypeTableValues[] = { { "mockReturnValueOnce"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockReturnValueOnce, 1 } }, { "mockResolvedValue"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockResolvedValue, 1 } }, { "mockResolvedValueOnce"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockResolvedValueOnce, 1 } }, - { "mockRejectedValueOnce"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockRejectedValue, 1 } }, - { "mockRejectedValue"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockRejectedValueOnce, 1 } }, + { "mockRejectedValue"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockRejectedValue, 1 } }, + { "mockRejectedValueOnce"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), NoIntrinsic, { HashTableValue::NativeFunctionType, jsMockFunctionMockRejectedValueOnce, 1 } }, }; const ClassInfo JSMockFunction::s_info = { "Mock"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSMockFunction) }; @@ -446,7 +521,6 @@ extern "C" void JSMock__resetSpies(Zig::GlobalObject* globalObject) continue; auto* spyObject = jsCast<JSMockFunction*>(spy); - spyObject->reset(); spyObject->clearSpy(); } globalObject->mockModule.activeSpies.clear(); @@ -508,20 +582,7 @@ extern "C" EncodedJSValue JSMock__spyOn(JSC::JSGlobalObject* lexicalGlobalObject if (hasValue) attributes = slot.attributes(); - { - auto catcher = DECLARE_CATCH_SCOPE(vm); - WTF::String nameToUse; - if (auto* fn = jsDynamicCast<JSFunction*>(value)) { - nameToUse = fn->name(vm); - } else if (auto* fn = jsDynamicCast<InternalFunction*>(value)) { - nameToUse = fn->name(); - } - if (nameToUse.length()) { - mock->setName(nameToUse); - } - if (catcher.exception()) - catcher.clearException(); - } + mock->copyNameAndLength(vm, globalObject, value); attributes |= PropertyAttribute::Function; object->putDirect(vm, propertyKey, mock, attributes); @@ -534,11 +595,14 @@ extern "C" EncodedJSValue JSMock__spyOn(JSC::JSGlobalObject* lexicalGlobalObject attributes |= PropertyAttribute::Accessor; object->putDirect(vm, propertyKey, JSC::GetterSetter::create(vm, globalObject, mock, mock), attributes); + // mock->setName(propertyKey.publicName()); RETURN_IF_EXCEPTION(scope, {}); pushImpl(mock, globalObject, JSMockImplementation::Kind::ReturnValue, value); } + mock->spyOriginal.set(vm, mock, value); + if (!globalObject->mockModule.activeSpies) { ActiveSpySet* activeSpies = ActiveSpySet::create(vm, globalObject->mockModule.activeSpySetStructure.getInitializedOnMainThread(globalObject)); globalObject->mockModule.activeSpies.set(vm, activeSpies); @@ -561,7 +625,6 @@ JSMockModule JSMockModule::create(JSC::JSGlobalObject* globalObject) mock.mockFunctionStructure.initLater( [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) { auto* prototype = JSMockFunctionPrototype::create(init.vm, init.owner, JSMockFunctionPrototype::createStructure(init.vm, init.owner, init.owner->functionPrototype())); - init.set(JSMockFunction::createStructure(init.vm, init.owner, prototype)); }); mock.mockResultStructure.initLater( @@ -602,10 +665,25 @@ JSMockModule JSMockModule::create(JSC::JSGlobalObject* globalObject) mock.mockObjectStructure.initLater( [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::Structure>::Initializer& init) { Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(init.owner); - JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype( + + auto* prototype = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype()); + // `putDirectCustomAccessor` doesn't pass the `this` value as expected. unfortunatly we + // need to use a JSFunction for the getter and assign it via `putDirectAccessor` instead. + prototype->putDirectAccessor( globalObject, - globalObject->objectPrototype(), - 4); + JSC::Identifier::fromString(init.vm, "lastCall"_s), + JSC::GetterSetter::create( + init.vm, + globalObject, + JSC::JSFunction::create(init.vm, init.owner, 0, "lastCall"_s, jsMockFunctionGetter_mockGetLastCall, ImplementationVisibility::Public), + jsUndefined()), + JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); + + JSC::Structure* structure + = globalObject->structureCache().emptyObjectStructureForPrototype( + globalObject, + prototype, + 5); JSC::PropertyOffset offset; structure = structure->addPropertyTransition( init.vm, @@ -631,9 +709,23 @@ JSMockModule JSMockModule::create(JSC::JSGlobalObject* globalObject) JSC::Identifier::fromString(init.vm, "results"_s), JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly, offset); + structure = structure->addPropertyTransition( + init.vm, + structure, + JSC::Identifier::fromString(init.vm, "invocationCallOrder"_s), + JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly, + offset); init.set(structure); }); + mock.withImplementationCleanupFunction.initLater( + [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSFunction>::Initializer& init) { + init.set(JSC::JSFunction::create(init.vm, init.owner, 2, String(), jsMockFunctionWithImplementationCleanup, ImplementationVisibility::Public)); + }); + mock.mockWithImplementationCleanupDataStructure.initLater( + [](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) { + init.set(Bun::MockWithImplementationCleanupData::createStructure(init.vm, init.owner, init.owner->objectPrototype())); + }); return mock; } @@ -705,8 +797,8 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), 1); calls->initializeIndex(object, 0, argumentsArray); + fn->calls.set(vm, fn, calls); } - fn->calls.set(vm, fn, calls); JSC::JSArray* contexts = fn->contexts.get(); if (contexts) { @@ -718,39 +810,51 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), 1); contexts->initializeIndex(object, 0, thisValue); + fn->contexts.set(vm, fn, contexts); } - fn->contexts.set(vm, fn, contexts); - JSValue implementationValue = fn->implementation.get(); - if (!implementationValue) - implementationValue = jsUndefined(); + auto invocationId = JSMockModule::nextInvocationId(); + JSC::JSArray* invocationCallOrder = fn->invocationCallOrder.get(); + if (invocationCallOrder) { + invocationCallOrder->push(globalObject, jsNumber(invocationId)); + } else { + JSC::ObjectInitializationScope object(vm); + invocationCallOrder = JSC::JSArray::tryCreateUninitializedRestricted( + object, + globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), + 1); + invocationCallOrder->initializeIndex(object, 0, jsNumber(invocationId)); + fn->invocationCallOrder.set(vm, fn, invocationCallOrder); + } - if (auto* impl = jsDynamicCast<JSMockImplementation*>(implementationValue)) { - if (JSValue nextValue = impl->internalFields[1].get()) { - if (nextValue.inherits<JSMockImplementation>() || (nextValue.isInt32() && nextValue.asInt32() == 1)) { - fn->implementation.set(vm, fn, nextValue); - } + unsigned int returnValueIndex = 0; + auto setReturnValue = [&](JSC::JSValue value) -> void { + if (auto* returnValuesArray = fn->returnValues.get()) { + returnValuesArray->push(globalObject, value); + returnValueIndex = returnValuesArray->length() - 1; + } else { + JSC::ObjectInitializationScope object(vm); + returnValuesArray = JSC::JSArray::tryCreateUninitializedRestricted( + object, + globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), + 1); + returnValuesArray->initializeIndex(object, 0, value); + fn->returnValues.set(vm, fn, returnValuesArray); } + }; - unsigned int returnValueIndex = 0; - auto setReturnValue = [&](JSC::JSValue value) -> void { - if (auto* returnValuesArray = fn->returnValues.get()) { - returnValuesArray->push(globalObject, value); - returnValueIndex = returnValuesArray->length() - 1; - } else { - JSC::ObjectInitializationScope object(vm); - returnValuesArray = JSC::JSArray::tryCreateUninitializedRestricted( - object, - globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), - 1); - returnValuesArray->initializeIndex(object, 0, value); - fn->returnValues.set(vm, fn, returnValuesArray); + if (auto* impl = tryJSDynamicCast<JSMockImplementation*>(fn->implementation.get())) { + if (impl->isOnce()) { + auto next = impl->nextValueOrSentinel.get(); + fn->implementation.set(vm, fn, next); + if (next.isNumber() || !jsDynamicCast<JSMockImplementation*>(next)->isOnce()) { + fn->tail.clear(); } - }; + } switch (impl->kind) { case JSMockImplementation::Kind::Call: { - JSValue result = impl->internalFields[0].get(); + JSValue result = impl->underlyingValue.get(); JSC::CallData callData = JSC::getCallData(result); if (UNLIKELY(callData.type == JSC::CallData::Type::None)) { throwTypeError(globalObject, scope, "Expected mock implementation to be callable"_s); @@ -783,9 +887,8 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje return JSValue::encode(returnValue); } - case JSMockImplementation::Kind::ReturnValue: - case JSMockImplementation::Kind::Promise: { - JSValue returnValue = impl->internalFields[0].get(); + case JSMockImplementation::Kind::ReturnValue: { + JSValue returnValue = impl->underlyingValue.get(); setReturnValue(createMockResult(vm, globalObject, "return"_s, returnValue)); return JSValue::encode(returnValue); } @@ -799,6 +902,7 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje } } + setReturnValue(createMockResult(vm, globalObject, "return"_s, jsUndefined())); return JSValue::encode(jsUndefined()); } @@ -820,10 +924,9 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetMockImplementation, (JSC::JSGlobalObje throwTypeError(globalObject, scope, "Expected Mock"_s); } - JSValue impl = thisObject->implementation.get(); - if (auto* implementation = jsDynamicCast<JSMockImplementation*>(impl)) { + if (auto* implementation = tryJSDynamicCast<JSMockImplementation*>(thisObject->implementation.get())) { if (implementation->kind == JSMockImplementation::Kind::Call) { - RELEASE_AND_RETURN(scope, JSValue::encode(implementation->internalFields[0].get())); + RELEASE_AND_RETURN(scope, JSValue::encode(implementation->underlyingValue.get())); } } @@ -851,41 +954,59 @@ JSC_DEFINE_CUSTOM_GETTER(jsMockFunctionGetter_protoImpl, (JSC::JSGlobalObject * return {}; } - if (auto implValue = thisObject->implementation.get()) { - if (auto* impl = jsDynamicCast<JSMockImplementation*>(implValue)) { - if (impl->kind == JSMockImplementation::Kind::Call) { - return JSValue::encode(impl->internalFields[0].get()); - } - - return JSValue::encode(jsUndefined()); + if (auto* impl = tryJSDynamicCast<JSMockImplementation*>(thisObject->implementation.get())) { + if (impl->kind == JSMockImplementation::Kind::Call) { + return JSValue::encode(impl->underlyingValue.get()); } } return JSValue::encode(jsUndefined()); } -#define HANDLE_STATIC_CALL \ - if (!thisObject->implementation.get()) { \ - thisObject = JSMockFunction::create( \ - vm, \ - globalObject, \ - reinterpret_cast<Zig::GlobalObject*>(globalObject)->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject), \ - CallbackKind::Call); \ +JSC_DEFINE_HOST_FUNCTION(jsMockFunctionConstructor, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callframe)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto thisObject_ = callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict()); + JSMockFunction* thisObject = JSMockFunction::create( + vm, + globalObject, + globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject)); + + if (UNLIKELY(!thisObject)) { + throwOutOfMemoryError(globalObject, scope); + return {}; + } + + if (callframe->argumentCount() > 0) { + JSValue value = callframe->argument(0); + if (value.isCallable()) { + thisObject->copyNameAndLength(vm, lexicalGlobalObject, value); + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, value); + } else { + // jest doesn't support doing `jest.fn(10)`, but we support it. + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, value); + thisObject->setName("mockConstructor"_s); + } + } else { + thisObject->setName("mockConstructor"_s); } + return JSValue::encode(thisObject); +} + extern "C" EncodedJSValue JSMockFunction__createObject(Zig::GlobalObject* globalObject) { - return JSValue::encode( - JSMockFunction::create(globalObject->vm(), globalObject, globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject), CallbackKind::Wrapper)); + auto& vm = globalObject->vm(); + return JSValue::encode(JSC::JSFunction::create(vm, globalObject, 0, "mock"_s, jsMockFunctionConstructor, ImplementationVisibility::Public)); } - extern "C" EncodedJSValue JSMockFunction__getCalls(EncodedJSValue encodedValue) { JSValue value = JSValue::decode(encodedValue); - if (value) { - if (auto* mock = jsDynamicCast<JSMockFunction*>(value)) { - return JSValue::encode(mock->getCalls()); - } + if (auto* mock = tryJSDynamicCast<JSMockFunction*>(value)) { + return JSValue::encode(mock->getCalls()); } return JSValue::encode({}); @@ -893,10 +1014,8 @@ extern "C" EncodedJSValue JSMockFunction__getCalls(EncodedJSValue encodedValue) extern "C" EncodedJSValue JSMockFunction__getReturns(EncodedJSValue encodedValue) { JSValue value = JSValue::decode(encodedValue); - if (value) { - if (auto* mock = jsDynamicCast<JSMockFunction*>(value)) { - return JSValue::encode(mock->getReturnValues()); - } + if (auto* mock = tryJSDynamicCast<JSMockFunction*>(value)) { + return JSValue::encode(mock->getReturnValues()); } return JSValue::encode({}); @@ -911,19 +1030,16 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetMockName, (JSC::JSGlobalObject * globa throwTypeError(globalObject, scope, "Expected Mock"_s); } - JSValue implValue = thisObject->implementation.get(); - if (!implValue) { - implValue = jsUndefined(); - } - - if (auto* impl = jsDynamicCast<JSMockImplementation*>(implValue)) { + if (auto* impl = tryJSDynamicCast<JSMockImplementation*>(thisObject->implementation.get())) { if (impl->kind == JSMockImplementation::Kind::Call) { - JSObject* object = impl->internalFields[0].get().asCell()->getObject(); - if (auto nameValue = object->getIfPropertyExists(globalObject, PropertyName(vm.propertyNames->name))) { - RELEASE_AND_RETURN(scope, JSValue::encode(nameValue)); - } + if (JSValue underlyingValue = impl->underlyingValue.get()) { + JSObject* object = underlyingValue.asCell()->getObject(); + if (auto nameValue = object->getIfPropertyExists(globalObject, PropertyName(vm.propertyNames->name))) { + RELEASE_AND_RETURN(scope, JSValue::encode(nameValue)); + } - RETURN_IF_EXCEPTION(scope, {}); + RETURN_IF_EXCEPTION(scope, {}); + } } } @@ -938,7 +1054,7 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockClear, (JSC::JSGlobalObject * globalO throwTypeError(globalObject, scope, "Expected Mock"_s); } - thisObject->reset(); + thisObject->clear(); RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); } @@ -964,6 +1080,8 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRestore, (JSC::JSGlobalObject * globa throwTypeError(globalObject, scope, "Expected Mock"_s); } + thisObject->clearSpy(); + RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementation, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callframe)) @@ -971,37 +1089,20 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementation, (JSC::JSGlobalObject auto& vm = lexicalGlobalObject->vm(); auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject); auto scope = DECLARE_THROW_SCOPE(vm); - JSMockFunction* thisObject = JSMockFunction::create( - vm, - globalObject, - globalObject->mockModule.mockFunctionStructure.getInitializedOnMainThread(globalObject)); + JSMockFunction* thisObject = jsDynamicCast<JSMockFunction*>(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); if (UNLIKELY(!thisObject)) { throwOutOfMemoryError(globalObject, scope); return {}; } - if (callframe->argumentCount() > 0) { - JSValue value = callframe->argument(0); - if (value.isCallable()) { - { - auto catcher = DECLARE_CATCH_SCOPE(vm); - WTF::String nameToUse; - if (auto* fn = jsDynamicCast<JSFunction*>(value)) { - nameToUse = fn->name(vm); - } else if (auto* fn = jsDynamicCast<InternalFunction*>(value)) { - nameToUse = fn->name(); - } - if (nameToUse.length()) { - thisObject->setName(nameToUse); - } - if (catcher.exception()) - catcher.clearException(); - } - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, value); - } else { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, value); - } + JSValue value = callframe->argument(0); + + // This check is for a jest edge case, truthy values will throw but not immediatly, and falsy values return undefined. + if (value.toBoolean(globalObject)) { + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, value); + } else { + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, jsUndefined()); } return JSValue::encode(thisObject); @@ -1018,59 +1119,16 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockImplementationOnce, (JSC::JSGlobalObj return {}; } - HANDLE_STATIC_CALL; - - if (callframe->argumentCount() > 0) { - JSValue value = callframe->argument(0); - if (value.isCallable()) { - if (!thisObject->implementation) { - auto catcher = DECLARE_CATCH_SCOPE(vm); - WTF::String nameToUse; - if (auto* fn = jsDynamicCast<JSFunction*>(value)) { - nameToUse = fn->name(vm); - } else if (auto* fn = jsDynamicCast<InternalFunction*>(value)) { - nameToUse = fn->name(); - } - if (nameToUse.length()) { - thisObject->setName(nameToUse); - } - if (catcher.exception()) - catcher.clearException(); - } - - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, value); - } else { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, value); - } - } - - return JSValue::encode(thisObject); -} - -JSC_DEFINE_HOST_FUNCTION(jsMockFunctionWithImplementation, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) -{ - JSMockFunction* thisObject = jsDynamicCast<JSMockFunction*>(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); - auto& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - if (UNLIKELY(!thisObject)) { - throwTypeError(globalObject, scope, "Expected Mock"_s); - RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined())); - } - - HANDLE_STATIC_CALL; - - JSValue arg = callframe->argument(0); + JSValue value = callframe->argument(0); - if (callframe->argumentCount() < 1 || arg.isEmpty() || arg.isUndefined()) { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, jsUndefined()); - } else if (arg.isCallable()) { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Call, arg); + // This check is for a jest edge case, truthy values will throw but not immediatly, and falsy values return undefined. + if (value.toBoolean(globalObject)) { + pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Call, value); } else { - throwTypeError(globalObject, scope, "Expected a function or undefined"_s); - RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined())); + pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, jsUndefined()); } - RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); + return JSValue::encode(thisObject); } JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockName, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { @@ -1082,8 +1140,6 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockName, (JSC::JSGlobalObject * globalOb return {}; } - HANDLE_STATIC_CALL; - if (callframe->argumentCount() > 0) { auto* newName = callframe->argument(0).toStringOrNull(globalObject); if (UNLIKELY(!newName)) { @@ -1105,7 +1161,6 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnThis, (JSC::JSGlobalObject * gl throwTypeError(globalObject, scope, "Expected Mock"_s); } - HANDLE_STATIC_CALL; pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnThis, jsUndefined()); RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); @@ -1117,13 +1172,10 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValue, (JSC::JSGlobalObject * g auto scope = DECLARE_THROW_SCOPE(vm); if (UNLIKELY(!thisObject)) { throwTypeError(globalObject, scope, "Expected Mock"_s); + return {}; } - if (callframe->argumentCount() < 1) { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, jsUndefined()); - } else { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, callframe->argument(0)); - } + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, callframe->argument(0)); RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); } @@ -1134,35 +1186,24 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockReturnValueOnce, (JSC::JSGlobalObject auto scope = DECLARE_THROW_SCOPE(vm); if (UNLIKELY(!thisObject)) { throwTypeError(globalObject, scope, "Expected Mock"_s); + return {}; } - HANDLE_STATIC_CALL; - if (callframe->argumentCount() < 1) { - pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, jsUndefined()); - } else { - pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, callframe->argument(0)); - } + pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, callframe->argument(0)); RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); } -JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callframe)) +JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) { - auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject); JSMockFunction* thisObject = jsDynamicCast<JSMockFunction*>(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); auto& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); if (UNLIKELY(!thisObject)) { throwTypeError(globalObject, scope, "Expected Mock"_s); + return {}; } - HANDLE_STATIC_CALL; - - if (callframe->argumentCount() < 1) { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, jsUndefined())); - } else { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0))); - } + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0))); RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); } @@ -1173,15 +1214,10 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockResolvedValueOnce, (JSC::JSGlobalObje auto scope = DECLARE_THROW_SCOPE(vm); if (UNLIKELY(!thisObject)) { throwTypeError(globalObject, scope, "Expected Mock"_s); + return {}; } - HANDLE_STATIC_CALL; - - if (callframe->argumentCount() < 1) { - pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, jsUndefined())); - } else { - pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0))); - } + pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0))); RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); } @@ -1192,15 +1228,10 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValue, (JSC::JSGlobalObject * auto scope = DECLARE_THROW_SCOPE(vm); if (UNLIKELY(!thisObject)) { throwTypeError(globalObject, scope, "Expected Mock"_s); + return {}; } - HANDLE_STATIC_CALL; - - if (callframe->argumentCount() < 1) { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::rejectedPromise(globalObject, jsUndefined())); - } else { - pushImpl(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::rejectedPromise(globalObject, callframe->argument(0))); - } + pushImpl(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, JSC::JSPromise::rejectedPromise(globalObject, callframe->argument(0))); RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); } @@ -1211,16 +1242,178 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionMockRejectedValueOnce, (JSC::JSGlobalObje auto scope = DECLARE_THROW_SCOPE(vm); if (UNLIKELY(!thisObject)) { throwTypeError(globalObject, scope, "Expected Mock"_s); + return {}; } - HANDLE_STATIC_CALL; + pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::ReturnValue, JSC::JSPromise::rejectedPromise(globalObject, callframe->argument(0))); - if (callframe->argumentCount() < 1) { - pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::rejectedPromise(globalObject, jsUndefined())); - } else { - pushImplOnce(thisObject, globalObject, JSMockImplementation::Kind::Promise, JSC::JSPromise::resolvedPromise(globalObject, callframe->argument(0))); + RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); +} +JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetter_mockGetLastCall, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + JSValue thisObject = callframe->thisValue(); + if (UNLIKELY(!thisObject.isObject())) { + return JSValue::encode(jsUndefined()); } + JSValue callsValue = thisObject.get(globalObject, Identifier::fromString(globalObject->vm(), "calls"_s)); - RELEASE_AND_RETURN(scope, JSValue::encode(thisObject)); + if (auto callsArray = jsDynamicCast<JSC::JSArray*>(callsValue)) { + auto len = callsArray->length(); + if (len > 0) { + return JSValue::encode(callsArray->getIndex(globalObject, len - 1)); + } + } + return JSValue::encode(jsUndefined()); +} + +const JSC::ClassInfo MockWithImplementationCleanupData::s_info = { "MockWithImplementationCleanupData"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(MockWithImplementationCleanupData) }; + +template<typename, JSC::SubspaceAccess mode> +JSC::GCClient::IsoSubspace* MockWithImplementationCleanupData::subspaceFor(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<MockWithImplementationCleanupData, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForMockWithImplementationCleanupData.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForMockWithImplementationCleanupData = std::forward<decltype(space)>(space); }, + [](auto& spaces) { return spaces.m_subspaceForMockWithImplementationCleanupData.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForMockWithImplementationCleanupData = std::forward<decltype(space)>(space); }); +} + +MockWithImplementationCleanupData* MockWithImplementationCleanupData::create(VM& vm, Structure* structure) +{ + MockWithImplementationCleanupData* mod = new (NotNull, allocateCell<MockWithImplementationCleanupData>(vm)) MockWithImplementationCleanupData(vm, structure); + return mod; +} +Structure* MockWithImplementationCleanupData::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); +} + +MockWithImplementationCleanupData::MockWithImplementationCleanupData(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +void MockWithImplementationCleanupData::finishCreation(VM& vm, JSMockFunction* fn, JSValue impl, JSValue tail, JSValue fallback) +{ + Base::finishCreation(vm); + this->internalField(0).set(vm, this, fn); + this->internalField(1).set(vm, this, impl); + this->internalField(2).set(vm, this, tail); + this->internalField(3).set(vm, this, fallback); +} + +template<typename Visitor> +void MockWithImplementationCleanupData::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<MockWithImplementationCleanupData*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); +} + +DEFINE_VISIT_CHILDREN(MockWithImplementationCleanupData); + +MockWithImplementationCleanupData* MockWithImplementationCleanupData::create(JSC::JSGlobalObject* globalObject, JSMockFunction* fn, JSValue impl, JSValue tail, JSValue fallback) +{ + auto* obj = create(globalObject->vm(), reinterpret_cast<Zig::GlobalObject*>(globalObject)->mockModule.mockWithImplementationCleanupDataStructure.getInitializedOnMainThread(globalObject)); + obj->finishCreation(globalObject->vm(), fn, impl, tail, fallback); + return obj; +} + +JSC_DEFINE_HOST_FUNCTION(jsMockFunctionWithImplementationCleanup, (JSC::JSGlobalObject * jsGlobalObject, JSC::CallFrame* callframe)) +{ + auto& vm = jsGlobalObject->vm(); + auto count = callframe->argumentCount(); + auto ctx = jsDynamicCast<MockWithImplementationCleanupData*>(callframe->argument(1)); + if (!ctx) { + return JSValue::encode(jsUndefined()); + } + + auto fn = jsDynamicCast<JSMockFunction*>(ctx->internalField(0).get()); + fn->implementation.set(vm, fn, ctx->internalField(1).get()); + fn->tail.set(vm, fn, ctx->internalField(2).get()); + fn->fallbackImplmentation.set(vm, fn, ctx->internalField(3).get()); + + return JSValue::encode(jsUndefined()); +} +JSC_DEFINE_HOST_FUNCTION(jsMockFunctionWithImplementation, (JSC::JSGlobalObject * jsGlobalObject, JSC::CallFrame* callframe)) +{ + Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(jsGlobalObject); + + JSMockFunction* thisObject = jsDynamicCast<JSMockFunction*>(callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict())); + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (UNLIKELY(!thisObject)) { + throwTypeError(globalObject, scope, "Expected Mock"_s); + return {}; + } + + JSValue tempImplValue = callframe->argument(0); + JSValue callback = callframe->argument(1); + JSC::CallData callData = JSC::getCallData(callback); + if (UNLIKELY(callData.type == JSC::CallData::Type::None)) { + throwTypeError(globalObject, scope, "Expected mock implementation to be callable"_s); + return {}; + } + + auto lastImpl = thisObject->implementation.get(); + auto lastTail = thisObject->tail.get(); + auto lastFallback = thisObject->fallbackImplmentation.get(); + + JSMockImplementation* impl = JSMockImplementation::create( + globalObject, + globalObject->mockModule.mockImplementationStructure.getInitializedOnMainThread(globalObject), + JSMockImplementation::Kind::Call, + tempImplValue, + false); + + thisObject->implementation.set(vm, thisObject, impl); + thisObject->fallbackImplmentation.clear(); + thisObject->tail.clear(); + + MarkedArgumentBuffer args; + NakedPtr<Exception> exception; + JSValue returnValue = call(globalObject, callback, callData, jsUndefined(), args, exception); + + if (auto promise = tryJSDynamicCast<JSC::JSPromise*>(returnValue)) { + auto capability = JSC::JSPromise::createNewPromiseCapability(globalObject, globalObject->promiseConstructor()); + auto ctx = MockWithImplementationCleanupData::create(globalObject, thisObject, lastImpl, lastTail, lastFallback); + + JSFunction* cleanup = globalObject->mockModule.withImplementationCleanupFunction.getInitializedOnMainThread(globalObject); + JSFunction* performPromiseThenFunction = globalObject->performPromiseThenFunction(); + auto callData = JSC::getCallData(performPromiseThenFunction); + MarkedArgumentBuffer arguments; + arguments.append(promise); + arguments.append(cleanup); + arguments.append(cleanup); + arguments.append(capability); + arguments.append(ctx); + ASSERT(!arguments.hasOverflowed()); + call(globalObject, performPromiseThenFunction, callData, jsUndefined(), arguments); + + return JSC::JSValue::encode(promise); + } + + thisObject->implementation.set(vm, thisObject, lastImpl); + thisObject->tail.set(vm, thisObject, lastImpl); + thisObject->fallbackImplmentation.set(vm, thisObject, lastFallback); + + return JSC::JSValue::encode(jsUndefined()); +} +} // namespace Bun + +namespace JSC { + +template<unsigned passedNumberOfInternalFields> +template<typename Visitor> +void JSInternalFieldObjectImpl<passedNumberOfInternalFields>::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSInternalFieldObjectImpl*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.appendValues(thisObject->m_internalFields, numberOfInternalFields); } -}
\ No newline at end of file + +DEFINE_VISIT_CHILDREN_WITH_MODIFIER(template<unsigned passedNumberOfInternalFields>, JSInternalFieldObjectImpl<passedNumberOfInternalFields>); + +} // namespace JSC diff --git a/src/bun.js/bindings/JSMockFunction.h b/src/bun.js/bindings/JSMockFunction.h index 288ca2e89..93c8bb015 100644 --- a/src/bun.js/bindings/JSMockFunction.h +++ b/src/bun.js/bindings/JSMockFunction.h @@ -16,15 +16,47 @@ class JSMockFunction; class JSMockModule final { public: + static uint64_t s_nextInvocationId; + static uint64_t nextInvocationId() { return ++s_nextInvocationId; } + LazyProperty<JSC::JSGlobalObject, Structure> mockFunctionStructure; LazyProperty<JSC::JSGlobalObject, Structure> mockResultStructure; LazyProperty<JSC::JSGlobalObject, Structure> mockImplementationStructure; LazyProperty<JSC::JSGlobalObject, Structure> mockObjectStructure; LazyProperty<JSC::JSGlobalObject, Structure> activeSpySetStructure; + LazyProperty<JSC::JSGlobalObject, JSFunction> withImplementationCleanupFunction; + LazyProperty<JSC::JSGlobalObject, JSC::Structure> mockWithImplementationCleanupDataStructure; static JSMockModule create(JSC::JSGlobalObject*); JSC::Strong<Unknown> activeSpies; }; -}
\ No newline at end of file +class MockWithImplementationCleanupData : public JSC::JSInternalFieldObjectImpl<4> { +public: + using Base = JSC::JSInternalFieldObjectImpl<4>; + + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm); + + JS_EXPORT_PRIVATE static MockWithImplementationCleanupData* create(VM&, Structure*); + static MockWithImplementationCleanupData* create(JSC::JSGlobalObject* globalObject, JSMockFunction* fn, JSValue impl, JSValue tail, JSValue fallback); + static MockWithImplementationCleanupData* createWithInitialValues(VM&, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + static std::array<JSValue, numberOfInternalFields> initialValues() + { + return { { + jsUndefined(), + jsUndefined(), + jsUndefined(), + jsUndefined(), + } }; + } + + DECLARE_EXPORT_INFO; + DECLARE_VISIT_CHILDREN; + + MockWithImplementationCleanupData(JSC::VM&, JSC::Structure*); + void finishCreation(JSC::VM&, JSMockFunction* fn, JSValue impl, JSValue tail, JSValue fallback); +}; +} diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 5589d2add..e49b94687 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -4042,6 +4042,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->mockModule.mockImplementationStructure.visit(visitor); thisObject->mockModule.mockObjectStructure.visit(visitor); thisObject->mockModule.activeSpySetStructure.visit(visitor); + thisObject->mockModule.mockWithImplementationCleanupDataStructure.visit(visitor); + thisObject->mockModule.withImplementationCleanupFunction.visit(visitor); for (auto& barrier : thisObject->m_thenables) { visitor.append(barrier); diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h index d595dc866..3997c1d88 100644 --- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h @@ -36,6 +36,7 @@ public: std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForCommonJSModuleRecord; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSMockImplementation; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSMockFunction; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMockWithImplementationCleanupData; #include "ZigGeneratedClasses+DOMClientIsoSubspaces.h" /* --- bun --- */ diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h index b4a5e9d55..4feca1754 100644 --- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h @@ -36,6 +36,7 @@ public: std::unique_ptr<IsoSubspace> m_subspaceForCommonJSModuleRecord; std::unique_ptr<IsoSubspace> m_subspaceForJSMockImplementation; std::unique_ptr<IsoSubspace> m_subspaceForJSMockFunction; + std::unique_ptr<IsoSubspace> m_subspaceForMockWithImplementationCleanupData; #include "ZigGeneratedClasses+DOMIsoSubspaces.h" /*-- BUN --*/ diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 6c77d7aaa..00cc954ad 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -698,22 +698,22 @@ pub const Jest = struct { Expect.getConstructor(globalObject), ); - const mock_object = JSMockFunction__createObject(globalObject); + const mock_fn = JSMockFunction__createObject(globalObject); const spyOn = JSC.NewFunction(globalObject, ZigString.static("spyOn"), 2, JSMock__spyOn, false); const restoreAllMocks = JSC.NewFunction(globalObject, ZigString.static("restoreAllMocks"), 2, jsFunctionResetSpies, false); - module.put( - globalObject, - ZigString.static("mock"), - mock_object, - ); + module.put(globalObject, ZigString.static("mock"), mock_fn); const jest = JSValue.createEmptyObject(globalObject, 3); - jest.put(globalObject, ZigString.static("fn"), mock_object); + jest.put(globalObject, ZigString.static("fn"), mock_fn); jest.put(globalObject, ZigString.static("spyOn"), spyOn); jest.put(globalObject, ZigString.static("restoreAllMocks"), restoreAllMocks); module.put(globalObject, ZigString.static("jest"), jest); module.put(globalObject, ZigString.static("spyOn"), spyOn); + const vi = JSValue.createEmptyObject(globalObject, 1); + vi.put(globalObject, ZigString.static("fn"), mock_fn); + module.put(globalObject, ZigString.static("vi"), vi); + return module; } @@ -989,15 +989,10 @@ 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\n", .{}); - return .zero; - } - const arguments = arguments_.ptr[0..arguments_.len]; + const arguments = callframe.arguments(1); + const value = if (arguments.len < 1) JSC.JSValue.jsUndefined() else arguments.ptr[0]; var expect = globalObject.bunVM().allocator.create(Expect) catch unreachable; - const value = arguments[0]; if (Jest.runner.?.pending_test == null) { const err = globalObject.createErrorInstance("expect() must be called in a test", .{}); diff --git a/src/js/out/WebCoreJSBuiltins.cpp b/src/js/out/WebCoreJSBuiltins.cpp index 55238274b..1fecb34e3 100644 --- a/src/js/out/WebCoreJSBuiltins.cpp +++ b/src/js/out/WebCoreJSBuiltins.cpp @@ -2916,9 +2916,9 @@ WEBCORE_FOREACH_WRITABLESTREAMDEFAULTCONTROLLER_BUILTIN_CODE(DEFINE_BUILTIN_GENE const JSC::ConstructAbility s_eventSourceGetEventSourceCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_eventSourceGetEventSourceCodeConstructorKind = JSC::ConstructorKind::None; const JSC::ImplementationVisibility s_eventSourceGetEventSourceCodeImplementationVisibility = JSC::ImplementationVisibility::Public; -const int s_eventSourceGetEventSourceCodeLength = 5476; +const int s_eventSourceGetEventSourceCodeLength = 5477; static const JSC::Intrinsic s_eventSourceGetEventSourceCodeIntrinsic = JSC::NoIntrinsic; -const char* const s_eventSourceGetEventSourceCode = "(function (){\"use strict\";class j extends EventTarget{#$;#j;#w;#A;#B;#F=!1;#G=null;#J=\"\";#K=\"\";#L=\"\";#M=!0;#O=0;#Q=0;#U=0;#V=null;static#W(w){w.#H()}static#X(w,A){const B=w.data,F=B.#L\?`Last-Event-ID: ${B.#L}\\r\\n`:\"\",G=`GET ${A.pathname}${A.search} HTTP/1.1\\r\\nHost: bun\\r\\nContent-type: text/event-stream\\r\\nContent-length: 0\\r\\n${F}\\r\\n`,J=w.write(G);if(J!==G.length)B.#K=G.substring(J)}static#Y(w,A,B){for(;;){if(B>=A.length)return;let F=-1,G=A.indexOf(\"\\r\\n\",B);const J=G+2;if(G>0)if(w.#O===0){const Q=parseInt(A.substring(B,G),16);if(Q===0){w.#j=2,w.#G\?.end();return}F=J+Q}else F=A.length;else{if(w.#J.length===0){w.#J+=A.substring(B);return}F=A.length}let K=A.substring(J,F);B=F+2;let L=0,M=K.indexOf(\"\\n\\n\");if(M==-1){w.#J+=A.substring(J);return}if(w.#J.length)w.#J+=K,K=w.#J,w.#J=\"\";let O=!0;while(O){const Q=K.substring(L,M);let U,V=\"\",W,X=0,Y=-1;for(;;){let z=Q.indexOf(\"\\n\",X);if(z===-1){if(X>=Q.length)break;z=Q.length}const H=Q.substring(X,z);if(H.startsWith(\"data:\"))if(V.length)V+=`\\n${H.substring(5).trim()}`;else V=H.substring(5).trim();else if(H.startsWith(\"event:\"))U=H.substring(6).trim();else if(H.startsWith(\"id:\"))W=H.substring(3).trim();else if(H.startsWith(\"retry:\")){if(Y=parseInt(H.substring(6).trim(),10),@isNaN(Y))Y=-1}X=z+1}if(w.#L=W||\"\",Y>=0)w.#U=Y;if(V||W||U)w.dispatchEvent(new MessageEvent(U||\"message\",{data:V||\"\",origin:w.#$.origin,source:w,lastEventId:W}));if(K.length===M+2){O=!1;break}const Z=K.indexOf(\"\\n\\n\",M+1);if(Z===-1)break;L=M,M=Z}}}static#Z={open(w){const A=w.data;if(A.#G=w,!A.#F)j.#X(w,A.#$)},handshake(w,A,B){const F=w.data;if(A)j.#X(w,F.#$);else F.#j=2,F.dispatchEvent(new ErrorEvent(\"error\",{error:B})),w.end()},data(w,A){const B=w.data;switch(B.#j){case 0:{let F=A.toString();const G=F.indexOf(\"\\r\\n\\r\\n\");if(G===-1){B.#J+=F;return}if(B.#J.length)B.#J+=F,F=B.#J,B.#J=\"\";const J=F.substring(0,G),K=J.indexOf(\"\\r\\n\");if(K===-1){B.#j=2,B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(\"Invalid HTTP request\")})),w.end();return}const L=J.substring(0,K);if(L!==\"HTTP/1.1 200 OK\"){B.#j=2,B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(L)})),w.end();return}let M=K+1,O=!1,Q=-1;for(;;){let V=J.indexOf(\"\\r\\n\",M);if(V===-1){if(M>=J.length){if(!O)B.#j=2,B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(`EventSource's response has no MIME type and \"text/event-stream\" is required. Aborting the connection.`)})),w.end();return}V=J.length}const W=J.substring(M+1,V),X=W.indexOf(\":\"),Y=W.substring(0,X),Z=Y.localeCompare(\"content-type\",@undefined,{sensitivity:\"accent\"})===0;if(M=V+1,Z)if(W.endsWith(\" text/event-stream\"))O=!0;else{B.#j=2,B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(`EventSource's response has a MIME type that is not \"text/event-stream\". Aborting the connection.`)})),w.end();return}else if(Y.localeCompare(\"content-length\",@undefined,{sensitivity:\"accent\"})===0){if(Q=parseInt(W.substring(X+1).trim(),10),@isNaN(Q)||Q<=0){B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(`EventSource's Content-Length is invalid. Aborting the connection.`)})),w.end();return}if(O)break}else if(Y.localeCompare(\"transfer-encoding\",@undefined,{sensitivity:\"accent\"})===0){if(W.substring(X+1).trim()!==\"chunked\"){B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(`EventSource's Transfer-Encoding is invalid. Aborting the connection.`)})),w.end();return}if(Q=0,O)break}}B.#O=Q,B.#j=1,B.dispatchEvent(new Event(\"open\"));const U=F.substring(G+4);if(j.#Y(B,U,0),B.#O>0){if(B.#Q+=U.length,B.#Q>=B.#O)B.#j=2,w.end()}return}case 1:if(j.#Y(B,A.toString(),2),B.#O>0){if(B.#Q+=A.byteLength,B.#Q>=B.#O)B.#j=2,w.end()}return;default:break}},drain(w){const A=w.data;if(A.#j===0){const B=A.#J;if(B.length){const F=w.write(B);if(F!==B.length)w.data.#K=B.substring(F);else w.data.#K=\"\"}}},close:j.#z,end(w){j.#z(w).dispatchEvent(new ErrorEvent(\"error\",{error:new Error(\"Connection closed by server\")}))},timeout(w){j.#z(w).dispatchEvent(new ErrorEvent(\"error\",{error:new Error(\"Timeout\")}))},binaryType:\"buffer\"};static#z(w){const A=w.data;if(A.#G=null,A.#Q=0,A.#j=2,A.#M){if(A.#V)clearTimeout(A.#V);A.#V=setTimeout(j.#W,A.#U,A)}return A}constructor(w,A=@undefined){super();const B=new URL(w);this.#F=B.protocol===\"https:\",this.#$=B,this.#j=2,process.nextTick(j.#W,this)}ref(){this.#V\?.ref(),this.#G\?.ref()}unref(){this.#V\?.unref(),this.#G\?.unref()}#H(){if(this.#j!==2)return;const w=this.#$,A=this.#F;this.#j=0,@Bun.connect({data:this,socket:j.#Z,hostname:w.hostname,port:parseInt(w.port||(A\?\"443\":\"80\"),10),tls:A\?{requestCert:!0,rejectUnauthorized:!1}:!1}).catch((B)=>{if(this.dispatchEvent(new ErrorEvent(\"error\",{error:B})),this.#M){if(this.#V)this.#V.unref\?.();this.#V=setTimeout(j.#W,1000,this)}})}get url(){return this.#$.href}get readyState(){return this.#j}close(){this.#M=!1,this.#j=2,this.#G\?.unref(),this.#G\?.end()}get onopen(){return this.#B}get onerror(){return this.#w}get onmessage(){return this.#A}set onopen(w){if(this.#B)super.removeEventListener(\"close\",this.#B);super.addEventListener(\"open\",w),this.#B=w}set onerror(w){if(this.#w)super.removeEventListener(\"error\",this.#w);super.addEventListener(\"error\",w),this.#w=w}set onmessage(w){if(this.#A)super.removeEventListener(\"message\",this.#A);super.addEventListener(\"message\",w),this.#A=w}}return Object.defineProperty(j.prototype,\"CONNECTING\",{enumerable:!0,value:0}),Object.defineProperty(j.prototype,\"OPEN\",{enumerable:!0,value:1}),Object.defineProperty(j.prototype,\"CLOSED\",{enumerable:!0,value:2}),j[Symbol.for(\"CommonJS\")]=0,j})\n"; +const char* const s_eventSourceGetEventSourceCode = "(function (){\"use strict\";class j extends EventTarget{#$;#j;#w;#A;#B;#F=!1;#G=null;#J=\"\";#K=\"\";#L=\"\";#M=!0;#O=0;#Q=0;#U=0;#V=null;static#W(w){w.#H()}static#X(w,A){const B=w.data,F=B.#L\?`Last-Event-ID: ${B.#L}\\r\\n`:\"\",G=`GET ${A.pathname}${A.search} HTTP/1.1\\r\\nHost: bun\\r\\nContent-type: text/event-stream\\r\\nContent-length: 0\\r\\n${F}\\r\\n`,J=w.write(G);if(J!==G.length)B.#K=G.substring(J)}static#Y(w,A,B){for(;;){if(B>=A.length)return;let F=-1,G=A.indexOf(\"\\r\\n\",B);const J=G+2;if(G>0)if(w.#O===0){const Q=parseInt(A.substring(B,G),16);if(Q===0){w.#j=2,w.#G\?.end();return}F=J+Q}else F=A.length;else{if(w.#J.length===0){w.#J+=A.substring(B);return}F=A.length}let K=A.substring(J,F);B=F+2;let L=0,M=K.indexOf(\"\\n\\n\");if(M==-1){w.#J+=A.substring(J);return}if(w.#J.length)w.#J+=K,K=w.#J,w.#J=\"\";let O=!0;while(O){const Q=K.substring(L,M);let U,V=\"\",W,X=0,Y=-1;for(;;){let z=Q.indexOf(\"\\n\",X);if(z===-1){if(X>=Q.length)break;z=Q.length}const H=Q.substring(X,z);if(H.startsWith(\"data:\"))if(V.length)V+=`\\n${H.substring(5).trim()}`;else V=H.substring(5).trim();else if(H.startsWith(\"event:\"))U=H.substring(6).trim();else if(H.startsWith(\"id:\"))W=H.substring(3).trim();else if(H.startsWith(\"retry:\")){if(Y=parseInt(H.substring(6).trim(),10),@isNaN(Y))Y=-1}X=z+1}if(w.#L=W||\"\",Y>=0)w.#U=Y;if(V||W||U)w.dispatchEvent(new MessageEvent(U||\"message\",{data:V||\"\",origin:w.#$.origin,source:w,lastEventId:W}));if(K.length===M+2){O=!1;break}const Z=K.indexOf(\"\\n\\n\",M+1);if(Z===-1)break;L=M,M=Z}}}static#Z={open(w){const A=w.data;if(A.#G=w,!A.#F)j.#X(w,A.#$)},handshake(w,A,B){const F=w.data;if(A)j.#X(w,F.#$);else F.#j=2,F.dispatchEvent(new ErrorEvent(\"error\",{error:B})),w.end()},data(w,A){const B=w.data;switch(B.#j){case 0:{let F=A.toString();const G=F.indexOf(\"\\r\\n\\r\\n\");if(G===-1){B.#J+=F;return}if(B.#J.length)B.#J+=F,F=B.#J,B.#J=\"\";const J=F.substring(0,G),K=J.indexOf(\"\\r\\n\");if(K===-1){B.#j=2,B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(\"Invalid HTTP request\")})),w.end();return}const L=J.substring(0,K);if(L!==\"HTTP/1.1 200 OK\"){B.#j=2,B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(L)})),w.end();return}let M=K+1,O=!1,Q=-1;for(;;){let V=J.indexOf(\"\\r\\n\",M);if(V===-1){if(M>=J.length){if(!O)B.#j=2,B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(`EventSource's response has no MIME type and \"text/event-stream\" is required. Aborting the connection.`)})),w.end();return}V=J.length}const W=J.substring(M+1,V),X=W.indexOf(\":\"),Y=W.substring(0,X),Z=Y.localeCompare(\"content-type\",@undefined,{sensitivity:\"accent\"})===0;if(M=V+1,Z)if(W.endsWith(\" text/event-stream\"))O=!0;else{B.#j=2,B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(`EventSource's response has a MIME type that is not \"text/event-stream\". Aborting the connection.`)})),w.end();return}else if(Y.localeCompare(\"content-length\",@undefined,{sensitivity:\"accent\"})===0){if(Q=parseInt(W.substring(X+1).trim(),10),@isNaN(Q)||Q<=0){B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(`EventSource's Content-Length is invalid. Aborting the connection.`)})),w.end();return}if(O)break}else if(Y.localeCompare(\"transfer-encoding\",@undefined,{sensitivity:\"accent\"})===0){if(W.substring(X+1).trim()!==\"chunked\"){B.dispatchEvent(new ErrorEvent(\"error\",{error:new Error(`EventSource's Transfer-Encoding is invalid. Aborting the connection.`)})),w.end();return}if(Q=0,O)break}}B.#O=Q,B.#j=1,B.dispatchEvent(new Event(\"open\"));const U=F.substring(G+4);if(j.#Y(B,U,0),B.#O>0){if(B.#Q+=U.length,B.#Q>=B.#O)B.#j=2,w.end()}return}case 1:if(j.#Y(B,A.toString(),2),B.#O>0){if(B.#Q+=A.byteLength,B.#Q>=B.#O)B.#j=2,w.end()}return;default:break}},drain(w){const A=w.data;if(A.#j===0){const B=A.#J;if(B.length){const F=w.write(B);if(F!==B.length)w.data.#K=B.substring(F);else w.data.#K=\"\"}}},close:j.#z,end(w){j.#z(w).dispatchEvent(new ErrorEvent(\"error\",{error:new Error(\"Connection closed by server\")}))},timeout(w){j.#z(w).dispatchEvent(new ErrorEvent(\"error\",{error:new Error(\"Timeout\")}))},binaryType:\"buffer\"};static#z(w){const A=w.data;if(A.#G=null,A.#Q=0,A.#j=2,A.#M){if(A.#V)clearTimeout(A.#V);A.#V=setTimeout(j.#W,A.#U,A)}return A}constructor(w,A=@undefined){super();const B=new URL(w);this.#F=B.protocol===\"https:\",this.#$=B,this.#j=2,process.nextTick(j.#W,this)}ref(){this.#V\?.ref(),this.#G\?.ref()}unref(){this.#V\?.unref(),this.#G\?.unref()}#H(){if(this.#j!==2)return;const w=this.#$,A=this.#F;this.#j=0,@Bun.connect({data:this,socket:j.#Z,hostname:w.hostname,port:parseInt(w.port||(A\?\"443\":\"80\"),10),tls:A\?{requestCert:!0,rejectUnauthorized:!1}:!1}).catch((B)=>{if(super.dispatchEvent(new ErrorEvent(\"error\",{error:B})),this.#M){if(this.#V)this.#V.unref\?.();this.#V=setTimeout(j.#W,1000,this)}})}get url(){return this.#$.href}get readyState(){return this.#j}close(){this.#M=!1,this.#j=2,this.#G\?.unref(),this.#G\?.end()}get onopen(){return this.#B}get onerror(){return this.#w}get onmessage(){return this.#A}set onopen(w){if(this.#B)super.removeEventListener(\"close\",this.#B);super.addEventListener(\"open\",w),this.#B=w}set onerror(w){if(this.#w)super.removeEventListener(\"error\",this.#w);super.addEventListener(\"error\",w),this.#w=w}set onmessage(w){if(this.#A)super.removeEventListener(\"message\",this.#A);super.addEventListener(\"message\",w),this.#A=w}}return Object.defineProperty(j.prototype,\"CONNECTING\",{enumerable:!0,value:0}),Object.defineProperty(j.prototype,\"OPEN\",{enumerable:!0,value:1}),Object.defineProperty(j.prototype,\"CLOSED\",{enumerable:!0,value:2}),j[Symbol.for(\"CommonJS\")]=0,j})\n"; #define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \ JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \ diff --git a/src/js/out/modules/node/http.js b/src/js/out/modules/node/http.js index 02e000138..dd007e740 100644 --- a/src/js/out/modules/node/http.js +++ b/src/js/out/modules/node/http.js @@ -93,8 +93,7 @@ function get(url, options, cb) { return req.end(), req; } var { EventEmitter } = import.meta.require("node:events"), { isIPv6 } = import.meta.require("node:net"), { Readable, Writable, Duplex } = import.meta.require("node:stream"), { URL } = import.meta.require("node:url"), { newArrayWithSize, String, Object, Array } = import.meta.primordials, { isTypedArray } = import.meta.require("util/types"), globalReportError = globalThis.reportError, setTimeout = globalThis.setTimeout, fetch = Bun.fetch, nop = () => { -}, __DEBUG__ = process.env.__DEBUG__, debug = __DEBUG__ ? (...args) => console.log("node:http", ...args) : nop, kEmptyObject = Object.freeze(Object.create(null)), kOutHeaders = Symbol.for("kOutHeaders"), kEndCalled = Symbol.for("kEndCalled"), kAbortController = Symbol.for("kAbortController"), kClearTimeout = Symbol("kClearTimeout"), kCorked = Symbol.for("kCorked"), searchParamsSymbol = Symbol.for("query"), StringPrototypeSlice = String.prototype.slice, StringPrototypeStartsWith = String.prototype.startsWith, StringPrototypeToUpperCase = String.prototype.toUpperCase, StringPrototypeIncludes = String.prototype.includes, StringPrototypeCharCodeAt = String.prototype.charCodeAt, StringPrototypeIndexOf = String.prototype.indexOf, ArrayIsArray = Array.isArray, RegExpPrototypeExec = RegExp.prototype.exec, ObjectAssign = Object.assign, ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty, INVALID_PATH_REGEX = /[^\u0021-\u00ff]/; -var _globalAgent, _defaultHTTPSAgent, kInternalRequest = Symbol("kInternalRequest"), kInternalSocketData = Symbol.for("::bunternal::"), kEmptyBuffer = Buffer.alloc(0), FakeSocket = class Socket extends Duplex { +}, __DEBUG__ = process.env.__DEBUG__, debug = __DEBUG__ ? (...args) => console.log("node:http", ...args) : nop, kEmptyObject = Object.freeze(Object.create(null)), kOutHeaders = Symbol.for("kOutHeaders"), kEndCalled = Symbol.for("kEndCalled"), kAbortController = Symbol.for("kAbortController"), kClearTimeout = Symbol("kClearTimeout"), kCorked = Symbol.for("kCorked"), searchParamsSymbol = Symbol.for("query"), StringPrototypeSlice = String.prototype.slice, StringPrototypeStartsWith = String.prototype.startsWith, StringPrototypeToUpperCase = String.prototype.toUpperCase, StringPrototypeIncludes = String.prototype.includes, StringPrototypeCharCodeAt = String.prototype.charCodeAt, StringPrototypeIndexOf = String.prototype.indexOf, ArrayIsArray = Array.isArray, RegExpPrototypeExec = RegExp.prototype.exec, ObjectAssign = Object.assign, ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty, INVALID_PATH_REGEX = /[^\u0021-\u00ff]/, NODE_HTTP_WARNING = "WARN: Agent is mostly unused in Bun's implementation of http. If you see strange behavior, this is probably the cause.", _globalAgent, _defaultHTTPSAgent, kInternalRequest = Symbol("kInternalRequest"), kInternalSocketData = Symbol.for("::bunternal::"), kEmptyBuffer = Buffer.alloc(0), FakeSocket = class Socket extends Duplex { bytesRead = 0; bytesWritten = 0; connecting = !1; diff --git a/src/js/out/modules/node/net.js b/src/js/out/modules/node/net.js index 162da7754..ddd799cf2 100644 --- a/src/js/out/modules/node/net.js +++ b/src/js/out/modules/node/net.js @@ -24,9 +24,7 @@ var isIPv4 = function(s) { self.emit("listening"); }, createServer = function(options, connectionListener) { return new Server(options, connectionListener); -}; -var IPv4Reg = new RegExp("^((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"); -var IPv6Reg = new RegExp("^((?:(?:[0-9a-fA-F]{1,4}):){7}(?:(?:[0-9a-fA-F]{1,4})|:)|(?:(?:[0-9a-fA-F]{1,4}):){6}(?:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|:(?:[0-9a-fA-F]{1,4})|:)|(?:(?:[0-9a-fA-F]{1,4}):){5}(?::((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,2}|:)|(?:(?:[0-9a-fA-F]{1,4}):){4}(?:(:(?:[0-9a-fA-F]{1,4})){0,1}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,3}|:)|(?:(?:[0-9a-fA-F]{1,4}):){3}(?:(:(?:[0-9a-fA-F]{1,4})){0,2}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,4}|:)|(?:(?:[0-9a-fA-F]{1,4}):){2}(?:(:(?:[0-9a-fA-F]{1,4})){0,3}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,5}|:)|(?:(?:[0-9a-fA-F]{1,4}):){1}(?:(:(?:[0-9a-fA-F]{1,4})){0,4}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,6}|:)|(?::((?::(?:[0-9a-fA-F]{1,4})){0,5}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(?::(?:[0-9a-fA-F]{1,4})){1,7}|:)))(%[0-9a-zA-Z-.:]{1,})?$"), { Bun, createFIFO, Object } = import.meta.primordials, { connect: bunConnect } = Bun, { Duplex } = import.meta.require("node:stream"), { EventEmitter } = import.meta.require("node:events"), { setTimeout } = globalThis, bunTlsSymbol = Symbol.for("::buntls::"), bunSocketServerHandlers = Symbol.for("::bunsocket_serverhandlers::"), bunSocketServerConnections = Symbol.for("::bunnetserverconnections::"), bunSocketServerOptions = Symbol.for("::bunnetserveroptions::"), SocketClass, Socket = function(InternalSocket) { +}, v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])", v4Str = "((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])", IPv4Reg = new RegExp("^((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"), v6Seg = "(?:[0-9a-fA-F]{1,4})", IPv6Reg = new RegExp("^((?:(?:[0-9a-fA-F]{1,4}):){7}(?:(?:[0-9a-fA-F]{1,4})|:)|(?:(?:[0-9a-fA-F]{1,4}):){6}(?:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|:(?:[0-9a-fA-F]{1,4})|:)|(?:(?:[0-9a-fA-F]{1,4}):){5}(?::((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,2}|:)|(?:(?:[0-9a-fA-F]{1,4}):){4}(?:(:(?:[0-9a-fA-F]{1,4})){0,1}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,3}|:)|(?:(?:[0-9a-fA-F]{1,4}):){3}(?:(:(?:[0-9a-fA-F]{1,4})){0,2}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,4}|:)|(?:(?:[0-9a-fA-F]{1,4}):){2}(?:(:(?:[0-9a-fA-F]{1,4})){0,3}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,5}|:)|(?:(?:[0-9a-fA-F]{1,4}):){1}(?:(:(?:[0-9a-fA-F]{1,4})){0,4}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(:(?:[0-9a-fA-F]{1,4})){1,6}|:)|(?::((?::(?:[0-9a-fA-F]{1,4})){0,5}:((?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|(?::(?:[0-9a-fA-F]{1,4})){1,7}|:)))(%[0-9a-zA-Z-.:]{1,})?$"), { Bun, createFIFO, Object } = import.meta.primordials, { connect: bunConnect } = Bun, { Duplex } = import.meta.require("node:stream"), { EventEmitter } = import.meta.require("node:events"), { setTimeout } = globalThis, bunTlsSymbol = Symbol.for("::buntls::"), bunSocketServerHandlers = Symbol.for("::bunsocket_serverhandlers::"), bunSocketServerConnections = Symbol.for("::bunnetserverconnections::"), bunSocketServerOptions = Symbol.for("::bunnetserveroptions::"), SocketClass, Socket = function(InternalSocket) { return SocketClass = InternalSocket, Object.defineProperty(SocketClass.prototype, Symbol.toStringTag, { value: "Socket", enumerable: !1 diff --git a/test/bun.lockb b/test/bun.lockb Binary files differindex 64371cbb1..fc86845e1 100755 --- a/test/bun.lockb +++ b/test/bun.lockb diff --git a/test/js/bun/test/bigint.test.js b/test/js/bun/test/bigint.test.js deleted file mode 100644 index 46ad50b84..000000000 --- a/test/js/bun/test/bigint.test.js +++ /dev/null @@ -1,14 +0,0 @@ -import { describe, expect, it } from "bun:test"; - -describe("BigInt", () => { - it("compares correctly (literal)", () => { - expect(42n).toBe(42n); - }); - - it("compares correctly (object)", () => { - expect(BigInt(42n)).toBe(BigInt(42n)); - expect(42n).toBe(BigInt(42n)); - expect(BigInt(Bun.inspect(42n).substring(0, 2))).toBe(BigInt(42n)); - expect(BigInt(42n).valueOf()).toBe(BigInt(42n)); - }); -}); diff --git a/test/js/bun/test/bun-test.test.ts b/test/js/bun/test/bun-test.test.ts index cc6bf644a..5a8061e8c 100644 --- a/test/js/bun/test/bun-test.test.ts +++ b/test/js/bun/test/bun-test.test.ts @@ -4,3 +4,9 @@ test("Bun.version", () => { expect(process.versions.bun).toBe(Bun.version); expect(process.revision).toBe(Bun.revision); }); + +test("expect().not.not", () => { + // bun supports this but jest doesn't + expect(1).not.not.toBe(1); + expect(1).not.not.not.toBe(2); +}); diff --git a/test/js/bun/test/expect.test.js b/test/js/bun/test/expect.test.js new file mode 100644 index 000000000..f09f7d196 --- /dev/null +++ b/test/js/bun/test/expect.test.js @@ -0,0 +1,2955 @@ +"use strict"; + +/** This file is meant to be runnable in both Jest and Bun. + * `bunx jest mock-fn.test.js` + */ +var { isBun, test, describe, expect, jest, vi, mock, bunTest, spyOn } = require("./test-interop.js")(); + +describe("expect()", () => { + test("can call without an argument", () => { + expect().toBe(undefined); + }); + + test("toStrictEqual() vs toEqual()", () => { + expect([1, , 3]).toEqual([1, , 3]); + expect({}).toEqual({}); + expect({}).toStrictEqual({}); + expect({}).toEqual({ a: undefined }); + expect({}).not.toStrictEqual({ a: undefined }); + + class C { + hi = 34; + } + class D { + hi = 34; + } + let c = new C(); + let d = new D(); + + expect(d).toEqual(c); + expect(d).not.toStrictEqual(c); + expect({ a: 1, b: undefined }).toEqual({ a: 1 }); + expect({ a: 1 }).toEqual({ a: 1, b: undefined }); + expect({ a: 1, b: undefined }).toEqual({ a: 1, b: undefined }); + + expect({ a: 1, b: undefined }).not.toStrictEqual({ a: 1 }); + expect({ a: 1 }).not.toStrictEqual({ a: 1, b: undefined }); + expect({ a: 1, b: undefined }).toStrictEqual({ a: 1, b: undefined }); + + expect({ a: 1, b: null }).not.toEqual({ a: 1 }); + expect({ a: 1 }).not.toEqual({ a: 1, b: null }); + expect({ a: 1, b: null }).toEqual({ a: 1, b: null }); + + expect({ a: 1 }).not.toEqual({ a: true }); + expect({ a: 1 }).not.toEqual({ a: "1" }); + expect({ a: 1 }).not.toEqual({ a: 1, b: 2 }); + expect({ a: 1, b: 2 }).not.toEqual({ a: 1 }); + expect({ a: 1 }).not.toStrictEqual({ a: true }); + expect({ a: 1 }).not.toStrictEqual({ a: "1" }); + expect({ a: 1 }).not.toStrictEqual({ a: 1, b: 2 }); + expect({ a: 1, b: 2 }).not.toStrictEqual({ a: 1 }); + expect({ a: 1 }).toStrictEqual({ a: 1 }); + + expect([1, undefined, 3]).toEqual([1, undefined, 3]); + expect([1, undefined, 3]).toStrictEqual([1, undefined, 3]); + expect([1, undefined, 3]).not.toEqual([1, 2, 3]); + expect([1, undefined, 3]).not.toStrictEqual([1, 2, 3]); + expect([1, undefined, 3]).not.toEqual([1, 2]); + expect([1, undefined, 3]).not.toStrictEqual([1, 2]); + expect([1, undefined, 3]).not.toEqual([1]); + expect([1, undefined, 3]).not.toStrictEqual([1]); + expect([1, undefined, 3]).not.toEqual([]); + expect([1, undefined, 3]).not.toStrictEqual([]); + expect([1, undefined, 3]).not.toEqual([1, 3]); + expect([1, undefined, 3]).not.toStrictEqual([1, 3]); + + expect([1, null, 3]).toEqual([1, null, 3]); + expect([1, null, 3]).toStrictEqual([1, null, 3]); + expect([1, null, 3]).not.toEqual([1, 2, 3]); + expect([1, null, 3]).not.toStrictEqual([1, 2, 3]); + expect([1, null, 3]).not.toEqual([1, 2]); + expect([1, null, 3]).not.toStrictEqual([1, 2]); + expect([1, null, 3]).not.toEqual([1]); + expect([1, null, 3]).not.toStrictEqual([1]); + expect([1, null, 3]).not.toEqual([]); + expect([1, null, 3]).not.toStrictEqual([]); + expect([1, null, 3]).not.toEqual([1, 3]); + expect([1, null, 3]).not.toStrictEqual([1, 3]); + + expect([, 1]).toEqual([, 1]); + expect([, 1]).toStrictEqual([, 1]); + expect([, 1]).not.toEqual([1]); + expect([1]).not.toEqual([, 1]); + expect([, 1]).not.toStrictEqual([1]); + expect([1]).not.toStrictEqual([, 1]); + expect([, 1]).toEqual([undefined, 1]); + expect([, 1]).not.toStrictEqual([undefined, 1]); + expect([, 1]).not.toEqual([null, 1]); + expect([, 1]).not.toStrictEqual([null, 1]); + expect([undefined, 1]).toEqual([, 1]); + expect([undefined, 1]).not.toStrictEqual([, 1]); + expect([null, 1]).not.toEqual([, 1]); + expect([null, 1]).not.toStrictEqual([, 1]); + expect([undefined, 1]).toEqual([undefined, 1]); + expect([undefined, 1]).toStrictEqual([undefined, 1]); + + expect([0, , 2]).toEqual([0, undefined, 2]); + expect([, "boo2"]).toEqual([undefined, "boo2"]); + expect([, "boo"]).toEqual([, "boo"]); + expect([, 1]).toEqual([undefined, 1]); + + const s1 = Symbol("test1"); + const s2 = Symbol("test2"); + + let a = { a: 1, b: 2 }; + let b = { a: 1, b: 2 }; + a[s1] = 1; + b[s1] = 1; + a[s2] = undefined; + b[s2] = null; + expect(a).not.toEqual(b); + class F extends String { + constructor() { + super(); + } + } + + let f = new F("hello"); + let j = new String("hello"); + expect(f).not.toEqual(j); + class LaCroix { + constructor(flavor) { + this.flavor = flavor; + } + } + expect(new LaCroix("pamplemousse")).not.toStrictEqual({ + flavor: "pamplemousse", + }); + expect(new LaCroix("pamplemousse")).toEqual({ flavor: "pamplemousse" }); + + expect([, 1]).not.toStrictEqual([undefined, 1]); + + expect([0, , 2]).toEqual([0, undefined, 2]); + expect([, "boo2"]).toEqual([undefined, "boo2"]); + expect([, "boo"]).toEqual([, "boo"]); + expect([, 1]).toEqual([undefined, 1]); + }); + + describe("BigInt", () => { + it("compares correctly (literal)", () => { + expect(42n).toBe(42n); + }); + + it("compares correctly (object)", () => { + expect(BigInt(42n)).toBe(BigInt(42n)); + expect(42n).toBe(BigInt(42n)); + if (isBun) expect(BigInt(Bun.inspect(42n).substring(0, 2))).toBe(BigInt(42n)); + expect(BigInt(42n).valueOf()).toBe(BigInt(42n)); + }); + }); + + function f1() { + return "hello!"; + } + function f2() { + return "hey!"; + } + test("deepEquals regex", () => { + expect(/a/imu).toEqual(/a/imu); + expect(/a/imu).not.toEqual(/ab/imu); + + expect(new RegExp("s", "g")).toEqual(new RegExp("s", "g")); + expect(new RegExp("s", "g")).not.toEqual(new RegExp("s", "i")); + }); + + test("deepEquals works with accessors", () => { + { + let l1 = [1, undefined, 2]; + let l2 = [1, undefined, 2]; + Object.defineProperty(l1, 6, { get: () => 1 }); + Object.defineProperty(l2, 6, { get: () => 1 }); + expect(l1).toEqual(l2); + expect(l1).toStrictEqual(l2); + } + { + let l1 = [1, , 2]; + let l2 = [1, undefined, 2]; + Object.defineProperty(l1, 6, { get: () => 1 }); + Object.defineProperty(l2, 6, { get: () => 2 }); + expect(l1).toEqual(l2); + expect(l1).not.toStrictEqual(l2); + } + { + let l1 = [1, , 2]; + let l2 = [1, , 2]; + Object.defineProperty(l1, "hi", { get: () => 4 }); + Object.defineProperty(l2, "hi", { get: () => 5 }); + expect(l1).toEqual(l2); + expect(l1).toStrictEqual(l2); + } + + { + let l1 = [1, , 2]; + let l2 = [1, , 2]; + Object.defineProperty(l1, "hi", { set: () => 4 }); + Object.defineProperty(l2, "hi", { set: () => 5 }); + expect(l1).toEqual(l2); + expect(l1).toStrictEqual(l2); + } + + { + let o1 = { a: 1, c: undefined, b: 2 }; + let o2 = { a: 1, c: undefined, b: 2 }; + Object.defineProperty(o1, 6, { get: () => 1 }); + Object.defineProperty(o2, 6, { get: () => 1 }); + expect(o1).toEqual(o2); + expect(o1).toStrictEqual(o2); + } + { + let o1 = { a: 1, c: undefined, b: 2 }; + let o2 = { a: 1, c: undefined, b: 2 }; + Object.defineProperty(o1, 6, { get: () => 1 }); + Object.defineProperty(o2, 6, { get: () => 2 }); + expect(o1).toEqual(o2); + expect(o1).toStrictEqual(o2); + } + { + let o1 = { a: 1, c: undefined, b: 2 }; + let o2 = { a: 1, c: undefined, b: 2 }; + Object.defineProperty(o1, "hi", { get: () => 4 }); + Object.defineProperty(o2, "hi", { get: () => 5 }); + expect(o1).toEqual(o2); + expect(o1).toStrictEqual(o2); + } + + { + let o1 = { a: 1, c: undefined, b: 2 }; + let o2 = { a: 1, c: undefined, b: 2 }; + Object.defineProperty(o1, "hi", { set: () => 4 }); + Object.defineProperty(o2, "hi", { set: () => 5 }); + expect(o1).toEqual(o2); + expect(o1).toStrictEqual(o2); + } + }); + + // Doesn't work on jest because of https://github.com/jestjs/jest/issues/10788 + if (isBun) { + test("deepEquals works with proxies", () => { + { + let p1 = new Proxy({ a: 1, b: 2 }, {}); + let p2 = new Proxy({ a: 1, b: 2 }, {}); + expect(p1).toEqual(p2); + expect(p1).toStrictEqual(p2); + let p3 = new Proxy({ a: 1, b: 2 }, {}); + let p4 = new Proxy({ a: 1, b: 3 }, {}); + expect(p3).not.toEqual(p4); + expect(p3).not.toStrictEqual(p4); + } + { + let t1 = { a: 1, b: 2 }; + let h1 = { get: (t, k) => t[k] }; + let p1 = new Proxy(t1, h1); + let t2 = { a: 1, b: 2 }; + let h2 = { get: (t, k) => 0 }; + let p2 = new Proxy(t2, h2); + expect(p1).not.toEqual(p2); + expect(p1).not.toStrictEqual(p2); + } + { + let t1 = { a: 1, b: 2 }; + let h1 = { get: (t, k) => t[k] + 2 }; + let p1 = new Proxy(t1, h1); + let t2 = { a: 1, b: 2 }; + let h2 = { get: (t, k) => t[k] + 2 }; + let p2 = new Proxy(t2, h2); + expect(p1).toEqual(p2); + expect(p1).toStrictEqual(p2); + } + { + let t1 = { a: 1, b: 2 }; + let h1 = { get: (t, k) => t[k] + 2 }; + let p1 = new Proxy(t1, h1); + let t2 = { a: 1, b: 2 }; + let h2 = { get: (t, k) => t[k] + 3 }; + let p2 = new Proxy(t2, h2); + expect(p1).not.toEqual(p2); + expect(p1).not.toStrictEqual(p2); + } + { + // same handlers, different targets + let t1 = { a: 1, b: 2 }; + let t2 = { a: 1, b: 2 }; + let h1 = { get: (t, k) => t[k] + 2 }; + let p1 = new Proxy(t1, h1); + let p2 = new Proxy(t2, h1); + expect(p1).toEqual(p2); + expect(p1).toStrictEqual(p2); + } + { + // same targets, different handlers + let t1 = { a: 1, b: 2 }; + let h1 = { get: (t, k) => t[k] + 2 }; + let h2 = { get: (t, k) => t[k] + 3 }; + let p1 = new Proxy(t1, h1); + let p2 = new Proxy(t1, h2); + expect(p1).not.toEqual(p2); + expect(p1).not.toStrictEqual(p2); + } + { + // property with object + let t1 = { a: { b: 3 } }; + let h1 = { get: (t, k) => t[k] }; + let p1 = new Proxy(t1, h1); + + let t2 = { a: { b: 3 } }; + let h2 = { get: (t, k) => t[k] }; + let p2 = new Proxy(t2, h2); + + expect(p1).toEqual(p2); + expect(p1).toStrictEqual(p2); + + let t3 = { a: { b: 3 } }; + let h3 = { get: (t, k) => t[k] }; + let p3 = new Proxy(t3, h3); + + let t4 = { a: { b: 4 } }; + let h4 = { get: (t, k) => t[k] }; + let p4 = new Proxy(t4, h4); + + expect(p3).not.toEqual(p4); + expect(p3).not.toStrictEqual(p4); + } + { + // proxy object equals itself + let t1 = { a: 1, b: 2 }; + let h1 = { get: (t, k) => t[k] + 2 }; + let p1 = new Proxy(t1, h1); + expect(p1).toEqual(p1); + expect(p1).toStrictEqual(p1); + } + { + let t1 = { a: 1, b: 2 }; + let h1 = { get: (t, k) => k }; + let p1 = new Proxy(t1, h1); + + let t2 = { a: 1, b: 2 }; + let h2 = { get: (t, k) => k }; + let p2 = new Proxy(t2, h2); + + expect(p1).toEqual(p2); + expect(p1).toStrictEqual(p2); + } + }); + } + + test("deepEquals works with sets/maps/dates/strings", () => { + const f = Symbol.for("foo"); + + let a = new Set(); + a.add([1, 2, 3]); + a.add("hello"); + a.add({ a: 1 }); + a.add(89); + + let b = new Set(); + b.add(89); + b.add({ a: 1 }); + b.add("hello"); + b.add([1, 2, 3]); + + expect(a).toEqual(b); + expect(b).toEqual(a); + expect(b).toEqual(b); + + let obj = {}; + var c = new Set(); + obj.c = c; + obj.x = obj; + c.add(obj); + expect(obj).toEqual(obj); + + let o1 = { a: new Set() }; + o1.a.add(o1); + expect(o1).toEqual(o1); + + let o2 = new Set(); + let o3 = {}; + o3.x = o3; + o2.add(o3); + expect(o2).toEqual(o2); + + var d = new Date(); + var e = new Date(d); + e[f] = "hello"; + + expect(d).toEqual(e); + expect(e).toEqual(d); + + class Date2 extends Date { + constructor() { + super(...arguments); + } + } + + class Date3 extends Date2 { + constructor() { + super(...arguments); + } + } + + let d2 = new Date2(); + let e2 = new Date(d2); + d2[f] = "hello"; + expect(d2).toEqual(e2); + expect(e2).toEqual(d2); + + let d3 = new Date3(); + let e3 = new Date(d3); + d3[f] = "hello"; + expect(d3).toEqual(e3); + expect(e3).toEqual(d3); + + let d4 = new Date(); + let e4 = new Date3(d4); + d4[f] = "hello"; + expect(d4).toEqual(e4); + expect(e4).toEqual(d4); + + let d5 = new Date2(); + let e5 = new Date3(d5); + d5[f] = "hello"; + expect(d5).toEqual(e5); + expect(e5).toEqual(d5); + + expect(new String("a")).not.toEqual(new String("b")); + + var s1 = new String("a"); + var s2 = new String("a"); + s1[f] = "hello"; + expect(s1).toEqual(s2); + + class String2 extends String { + constructor() { + super(...arguments); + } + } + + class String3 extends String2 { + constructor() { + super(...arguments); + } + } + + let string4 = {}; + string4.__proto__ = String3.prototype; + + var s3 = new String2("a"); + var s4 = new String2("a"); + s3[f] = "hello"; + expect(s3).toEqual(s4); + + var s5 = new String("a"); + var s6 = new String3("a"); + expect(s6).not.toEqual(s5); + expect(s5).not.toEqual(s6); + + var s7 = new String2("a"); + var s8 = new String3("a"); + expect(s7).not.toEqual(s8); + expect(s8).not.toEqual(s7); + + var s9 = new String2("a"); + var s10 = new string4.constructor("a"); + expect(s9).not.toEqual(s10); + expect(s10).not.toEqual(s9); + + class F2 extends Function {} + class F3 extends F2 {} + + var f1 = new Function(); + var f2 = new F2(); + var f3 = new F3(); + expect(f1).not.toEqual(f2); + expect(f2).not.toEqual(f1); + expect(f2).not.toEqual(f3); + expect(f3).not.toEqual(f2); + }); + + describe("deepEquals with asymmetric matchers", () => { + it("should accept any string", () => { + expect({ name: "alice" }).toEqual({ name: expect.any(String) }); + expect({ name: "bob" }).toEqual({ name: expect.any(String) }); + expect({ name: "charlie" }).toEqual({ name: expect.any(String) }); + }); + + it("should accept any number", () => { + expect({ age: 42 }).toEqual({ age: expect.any(Number) }); + expect({ age: 69 }).toEqual({ age: expect.any(Number) }); + expect({ age: 73 }).toEqual({ age: expect.any(Number) }); + }); + + it("should accept any boolean", () => { + expect({ active: false }).toEqual({ active: expect.any(Boolean) }); + expect({ active: true }).toEqual({ active: expect.any(Boolean) }); + }); + + it("should not match the wrong constructors", () => { + function f() { + return 32; + } + Object.defineProperty(f, "name", { value: "String" }); + expect({ a: "123" }).toEqual({ a: expect.any(String) }); + expect({ a: "123" }).not.toEqual({ a: expect.any(f) }); + + function g() { + return 32; + } + Object.defineProperty(g, "name", { value: "BigInt" }); + expect({ a: 123n }).toEqual({ a: expect.any(BigInt) }); + expect({ a: 123n }).not.toEqual({ a: expect.any(g) }); + }); + }); + + test("toThrow", () => { + expect(() => { + throw new Error("hello"); + }).toThrow("hello"); + + var err = new Error("bad"); + expect(() => { + throw err; + }).toThrow(err); + + expect(() => { + throw new Error("good"); + }).toThrow(); + + expect(() => { + throw new Error("foo"); + }).toThrow(/oo/); + + expect(() => + expect(() => { + throw new Error("bar"); + }).toThrow(/baz/), + ).toThrow("/baz/"); + + expect(() => { + return true; + }).not.toThrow(); + + expect(() => { + return true; + }).not.toThrow(err); + + const weirdThings = [ + /watttt/g, + BigInt(123), + -42, + NaN, + Infinity, + -Infinity, + undefined, + null, + true, + false, + 0, + 1, + "", + "hello", + {}, + [], + new Date(), + new Error(), + new RegExp("foo"), + new Map(), + new Set(), + Promise.resolve(), + Promise.reject(Symbol("123")).finally(() => {}), + Symbol("123"), + ]; + for (const weirdThing of weirdThings) { + expect(() => { + throw weirdThing; + }).toThrow(); + } + + err.message = "null"; + expect(() => { + throw null; + }).toThrow(err); + }); + + test("deepEquals derived strings and strings", () => { + let a = new String("hello"); + let b = "hello"; + expect(a).toEqual(a); + expect(b).toEqual(b); + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + + class F extends String { + constructor() { + super(); + } + } + + let f = new F("hello"); + expect(f).toEqual(f); + expect(f).not.toEqual(b); + expect(b).not.toEqual(f); + + let j = new String("hello"); + expect(f).not.toEqual(j); + + class G extends String { + constructor() { + super(); + this.x = 0; + } + } + + let g = new G("hello"); + expect(g).not.toEqual(f); + expect(f).not.toEqual(g); + expect(g).toEqual(g); + expect(g).not.toEqual(b); + expect(b).not.toEqual(g); + expect(g).not.toEqual(a); + }); + + test("deepEquals throw getters", () => { + let a = { + get x() { + throw new Error("a"); + }, + }; + + let b = { + get x() { + return 3; + }, + }; + + try { + expect(a).not.toEqual(b); + } catch (e) { + expect(e.message).toContain("a"); + } + + class B { + get x() { + throw new Error("b"); + } + } + + class C { + get x() { + return 3; + } + } + + expect(() => { + expect(new B()).not.toEqual(new C()); + }).toThrow(); + + let o = [ + { + get x() { + throw new Error("c"); + }, + }, + ]; + + let p = [ + { + get x() { + return 3; + }, + }, + ]; + + try { + expect(o).not.toEqual(p); + } catch (e) { + expect(e.message).toContain("c"); + } + + const s = Symbol("s"); + let q = { + get x() { + throw new Error("d"); + }, + }; + q[s] = 3; + + let r = { + get x() { + return 3; + }, + }; + r[s] = 3; + + try { + expect(q).not.toEqual(r); + } catch (e) { + expect(e.message).toContain("d"); + } + }); + + test("deepEquals large object", () => { + let o = {}; + for (let i = 0; i < 65; i++) { + o["bun" + i] = i; + } + expect(o).toEqual(o); + let b = {}; + for (let i = 0; i < 63; i++) { + b["bun" + i] = i; + } + expect(b).toEqual(b); + expect(o).not.toEqual(b); + expect(b).not.toEqual(o); + + let c = { d: [Array(o)] }; + let d = { d: [Array(b)] }; + expect(c).toEqual(c); + expect(d).toEqual(d); + expect(c).not.toEqual(d); + expect(d).not.toEqual(c); + + let e = { d: [Array(o), Array(o)] }; + let f = { d: [Array(b), Array(b)] }; + expect(e).toEqual(e); + expect(f).toEqual(f); + expect(e).not.toEqual(f); + expect(f).not.toEqual(e); + + let p = []; + p[0] = {}; + for (let i = 0; i < 1000; i++) { + p[0]["bun" + i] = i; + } + let q = []; + q[0] = {}; + for (let i = 0; i < 1000; i++) { + q[0]["bun" + i] = i; + } + expect(p).toEqual(p); + expect(q).toEqual(q); + + q[0].bun789 = 788; + expect(p).not.toEqual(q); + expect(q).not.toEqual(p); + + let r = { d: {} }; + let s = { d: {} }; + for (let i = 0; i < 1000; i++) { + r.d["bun" + i] = i; + s.d["bun" + i] = i; + } + + expect(r).toEqual(r); + expect(s).toEqual(s); + + r.d.bun790 = 791; + expect(r).not.toEqual(s); + expect(s).not.toEqual(r); + + let t = []; + t[5] = {}; + let u = []; + u[5] = {}; + for (let i = 0; i < 1000; i++) { + t[5]["bun" + i] = i; + } + for (let i = 0; i < 30; i++) { + u[5]["bun" + i] = i; + } + expect(t).toEqual(t); + expect(u).toEqual(u); + expect(t).not.toEqual(u); + expect(u).not.toEqual(t); + + let v = { j: {} }; + let w = { j: {} }; + for (let i = 0; i < 1000; i++) { + v.j["bun" + i] = i; + w.j["bun" + i] = i; + } + + expect(v).toEqual(v); + expect(w).toEqual(w); + + v.j.bun999 = 1000; + expect(v).not.toEqual(w); + expect(w).not.toEqual(v); + expect(v).toEqual(v); + + v.j.bun999 = 999; + w.j.bun0 = 1; + expect(v).not.toEqual(w); + expect(w).not.toEqual(v); + expect(v).toEqual(v); + expect(w).toEqual(w); + }); + + test("deepEquals - Date", () => { + let d = new Date(); + expect(d).toEqual(d); + let b = d; + expect(b).toEqual(d); + d.setFullYear(1998); + expect(b).toEqual(d); + expect(b).not.toEqual(new Date()); + + var date = new Date(); + date.setFullYear(1995); + expect(new Date()).not.toEqual(date); + }); + + test("deepEquals toString and functions", () => { + expect({ toString: f1 }).toEqual({ + toString: f1, + }); + expect({ toString: f1 }).not.toEqual({ + toString: f2, + }); + + expect(f1).toEqual(f1); + expect(f1).not.toEqual(f2); + }); + + test("deepEquals set and map", () => { + let e = new Map(); + e.set("a", 1); + e.set("b", 2); + e.set("c", 3); + e.set(8, 6); + + let d = new Map(); + d.set("a", 1); + d.set("b", 2); + d.set("c", 3); + d.set(8, 6); + + expect(e).toEqual(d); + expect(d).toEqual(e); + + let f = new Map(); + f.set("a", 1); + f.set("b", 2); + f.set("c", 3); + f.set(8, 7); + expect(e).not.toEqual(f); + + let g = new Map(); + g.set({ a: { b: { c: 89 } } }, 1); + + let h = new Map(); + h.set({ a: { b: { c: 89 } } }, 1); + expect(g).toEqual(h); + + let i = new Map(); + i.set({ a: { b: { c: 89 } } }, 1); + i.set({ a: { b: { c: 89 } } }, 1); + expect(g).not.toEqual(i); + + let j = new Map(); + j.set({ a: { b: { c: 89 } } }, 1); + j.set({ a: { b: { c: 89 } } }, 1); + expect(i).toEqual(j); + + let p = new Map(); + p.set({ a: { b: { c: 90 } } }, 1); + expect(p).not.toEqual(g); + + let q = new Map(); + q.set({ a: { b: { c: 90 } } }, { a: { b: 45 } }); + + let r = new Map(); + r.set({ a: { b: { c: 90 } } }, { a: { b: 45 } }); + expect(q).toEqual(r); + + let s = new Map(); + s.set({ a: { b: { c: 90 } } }, { a: { b: 49 } }); + expect(q).not.toEqual(s); + + const u = { a: 1, b: 2 }; + + let a = new Set(); + a.add({ a: 1 }); + a.add([1, 2, 3]); + a.add("hello"); + a.add(89); + + let b = new Set(); + b.add({ a: 1 }); + b.add("hello"); + b.add([1, 2, 3]); + b.add(89); + expect(a).toEqual(b); + expect(b).toEqual(a); + let c = new Set(); + c.add(89); + c.add("hello"); + c.add({ a: 1 }); + c.add([1, 2, 3, 4]); + expect(a).not.toEqual(c); + }); + + test("deepEquals - symbols", () => { + const x = [5, 6]; + x[99] = 7; + + const y = [5, 6]; + y[99] = 7; + + expect(x).toEqual(y); + + const s1 = Symbol("test1"); + const s2 = Symbol("test2"); + + const o = { a: 1 }; + o[s1] = 45; + o[99] = 99; + o[s2] = 3; + + const k = { a: 1 }; + k[99] = 99; + k[s2] = 3; + k[s1] = 45; + + expect(o).toEqual(k); + }); + + test("deepEquals should not segfault", () => { + const obj = { ...Object.fromEntries(Object.entries([1, 2, 3, 4])), length: 4 }; + expect(() => { + expect(obj).toEqual([1, 2, 3, 4]); + }).toThrow(); + expect(() => { + expect([1, 2, 3, 4]).toEqual(obj); + }).toThrow(); + }); + + test("toEqual objects and arrays", () => { + { + let obj = { 0: 4, 1: 3, length: 2 }; + expect(Array.from(obj)).toEqual([4, 3]); + expect(Array.from(obj)).toStrictEqual([4, 3]); + } + { + let obj = { 0: 4, 1: 3, length: 4 }; + expect(Array.from(obj)).toEqual([4, 3]); + expect(Array.from(obj)).not.toStrictEqual([4, 3]); + expect(Array.from(obj)).toEqual([4, 3, undefined, undefined]); + expect(Array.from(obj)).toStrictEqual([4, 3, undefined, undefined]); + expect(Array.from(obj)).toEqual([4, 3, , ,]); + expect(Array.from(obj)).not.toStrictEqual([4, 3, , ,]); + } + { + let a1 = [1, undefined, 3, , 4, null]; + let a2 = [1, undefined, 3, , 4, null, , ,]; + expect(a1).toEqual(a2); + expect(a1).not.toStrictEqual(a2); + expect(a2).toEqual(a1); + expect(a2).not.toStrictEqual(a1); + } + { + let a1 = [, , , , , , , , , , , ,]; + let a2 = [undefined]; + expect(a1).toEqual(a2); + expect(a1).not.toStrictEqual(a2); + expect(a2).toEqual(a1); + expect(a2).not.toStrictEqual(a1); + } + { + const a = [1]; + const b = [1]; + expect(a).toEqual(b); + Object.preventExtensions(b); + expect(a).toEqual(b); + Object.preventExtensions(a); + expect(a).toEqual(b); + } + { + let o1 = { 1: 4, 6: 3 }; + let o2 = { 1: 4, 6: 3 }; + expect(o1).toEqual(o2); + expect(o1).toStrictEqual(o2); + } + { + let o1 = { 1: 4, 6: 2 }; + let o2 = { 1: 4, 6: 3 }; + expect(o1).not.toEqual(o2); + expect(o1).not.toStrictEqual(o2); + } + + { + let o1 = { a: 1, 3: 0 }; + let o2 = { a: 1, 3: 0 }; + expect(o1).toEqual(o2); + expect(o1).toStrictEqual(o2); + } + { + let o1 = { a: 1, 3: 0 }; + let o2 = { a: 1, 3: 1 }; + expect(o1).not.toEqual(o2); + expect(o1).not.toStrictEqual(o2); + } + { + let o1 = { a: {}, 4: { b: 3, c: { 9: 2 } } }; + let o2 = { a: {}, 4: { b: 3, c: { 9: 2 } } }; + expect(o1).toEqual(o2); + expect(o1).toStrictEqual(o2); + } + { + let o1 = { a: {}, 4: { b: 3, c: { 9: 2 } } }; + let o2 = { a: {}, 4: { b: 3, c: { 9: 3 } } }; + expect(o1).not.toEqual(o2); + expect(o1).not.toStrictEqual(o2); + } + + { + let o1 = { a: 1, b: 2, c: 3 }; + let o2 = { a: 1, b: 2, c: 3, 0: 1 }; + expect(o1).not.toEqual(o2); + expect(o1).not.toStrictEqual(o2); + } + + { + let o1 = { a: 1, b: 2, c: 3, 0: 1 }; + let o2 = { a: 1, b: 2, c: 3 }; + expect(o1).not.toEqual(o2); + expect(o1).not.toStrictEqual(o2); + } + + expect("hello").toEqual("hello"); + const s1 = Symbol("test1"); + const s2 = Symbol("test2"); + + expect({ a: 1, b: 2 }).toEqual({ b: 2, a: 1 }); + expect([1, 2, 3]).toEqual([1, 2, 3]); + expect({ a: 1, b: 2 }).not.toEqual({ b: 2, a: 1, c: 3 }); + expect([1, 2, 3]).not.toEqual([1, 2, 3, 4]); + expect({ a: 1, b: 2, c: 3 }).not.toEqual({ a: 1, b: 2 }); + expect([1, 2, 3, 4]).not.toEqual([1, 2, 3]); + + let a = [{ a: 1 }, { b: 2, c: 3, d: 4 }, { e: 5, f: 6 }]; + let b = [{ a: 1 }, { b: 2, c: 3, d: 4 }, { e: 5, f: 6 }]; + expect(a).toEqual(b); + expect(b).toEqual(a); + a[0].a = 2; + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + + let c = { [Symbol("test")]: 1 }; + let d = { [Symbol("test")]: 1 }; + expect(c).not.toEqual(d); + expect(d).not.toEqual(c); + + a = { [s1]: 1 }; + a[s1] = 1; + b = { [s2]: 1 }; + b[s2] = 1; + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + + a = {}; + b = {}; + a[s1] = 1; + b[s1] = 1; + expect(a).toEqual(b); + + a = {}; + b = {}; + a[s1] = 1; + b[s1] = 2; + expect(a).not.toEqual(b); + + a = {}; + b = {}; + a[s1] = 1; + b[s1] = 1; + a[s2] = 2; + b[s2] = 2; + expect(a).toEqual(b); + + a = {}; + b = {}; + a[s1] = 1; + b[s1] = 1; + a[s2] = 2; + b[s2] = 3; + expect(a).not.toEqual(b); + + a = { a: 1, b: 2 }; + b = { a: 1, b: 2 }; + a[s1] = 1; + b[s1] = 1; + expect(a).toEqual(b); + + a = { a: 2, b: 2 }; + b = { a: 1, b: 2 }; + a[s1] = 1; + b[s1] = 1; + expect(a).not.toEqual(b); + + // do the same tests for arrays + a = [{ a: 1 }, { b: 2, c: 3, d: 4 }, { e: 5, f: 6 }]; + b = [{ a: 1 }, { b: 2, c: 3, d: 4 }, { e: 5, f: 6 }]; + expect(a).toEqual(b); + expect(b).toEqual(a); + a[0].a = 2; + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + + a = [1, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 1; + expect(a).toEqual(b); + + a = [1, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 2; + expect(a).not.toEqual(b); + + a = [1, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 1; + a[s2] = 2; + b[s2] = 2; + expect(a).toEqual(b); + + a = [1, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 1; + a[s2] = 2; + b[s2] = 3; + expect(a).not.toEqual(b); + + a = [1, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 1; + expect(a).toEqual(b); + + a = [2, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 1; + expect(a).not.toEqual(b); + + // do the same tests for objects and arrays with null and undefined + a = { a: 1, b: 2 }; + b = { a: 1, b: 2 }; + a[s1] = 1; + b[s1] = 1; + a[s2] = null; + b[s2] = undefined; + expect(a).not.toEqual(b); + + a = { a: 1, b: 2 }; + b = { a: 1, b: 2 }; + a[s1] = 1; + b[s1] = 1; + a[s2] = undefined; + b[s2] = null; + expect(a).not.toEqual(b); + + a = { a: 1, b: 2 }; + b = { a: 1, b: 2 }; + a[s1] = 1; + b[s1] = 1; + a[s2] = null; + b[s2] = null; + expect(a).toEqual(b); + + a = { a: 1, b: 2 }; + b = { a: 1, b: 2 }; + a[s1] = 1; + b[s1] = 1; + a[s2] = undefined; + b[s2] = undefined; + expect(a).toEqual(b); + + a = [1, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 1; + a[s2] = null; + b[s2] = undefined; + expect(a).not.toEqual(b); + + a = [1, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 1; + a[s2] = undefined; + b[s2] = null; + expect(a).not.toEqual(b); + + a = [1, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 1; + a[s2] = null; + b[s2] = null; + expect(a).toEqual(b); + + a = [1, 2, 3]; + b = [1, 2, 3]; + a[s1] = 1; + b[s1] = 1; + a[s2] = undefined; + b[s2] = undefined; + expect(a).toEqual(b); + + // similar tests for indexed objects + a = { 0: 1, 1: 2, 2: 3 }; + b = { 0: 1, 1: 2, 2: 3 }; + a[s1] = 1; + b[s1] = 1; + expect(a).toEqual(b); + + a = { 0: 1, 1: 2, 2: 3 }; + b = { 0: 1, 1: 2, 2: 3 }; + a[s1] = 1; + b[s1] = 1; + a[s2] = 2; + b[s2] = 3; + expect(a).not.toEqual(b); + + a = { 0: 1, 1: 3, 2: 3 }; + b = { 0: 1, 1: 2, 2: 3 }; + a[s1] = 1; + b[s1] = 1; + a[s2] = 2; + b[s2] = 2; + expect(a).not.toEqual(b); + + a = [1, 2, 3]; + b = [1, 2, 3, 4]; + expect(a).not.toEqual(b); + + a = [1, 2, 3, 4]; + b = [1, 2, 3]; + expect(a).not.toEqual(b); + + a = { a: 1, b: 2 }; + b = { a: 1, b: 2, c: 3 }; + expect(a).not.toEqual(b); + + a = { a: 1, b: 2, c: 3 }; + b = { a: 1, b: 2 }; + expect(a).not.toEqual(b); + }); + + test("symbol based keys in arrays are processed correctly", () => { + const mySymbol = Symbol("test"); + + const actual1 = []; + actual1[mySymbol] = 3; + + const actual2 = []; + actual2[mySymbol] = 4; + + const expected = []; + expected[mySymbol] = 3; + + expect(actual2).not.toEqual(expected); + expect(actual1).toEqual(expected); + }); + + test("non-enumerable members should be skipped during equal", () => { + const actual = { + x: 3, + }; + Object.defineProperty(actual, "test", { + enumerable: false, + value: 5, + }); + expect(actual).toEqual({ x: 3 }); + }); + + test("non-enumerable symbolic members should be skipped during equal", () => { + const actual = { + x: 3, + }; + const mySymbol = Symbol("test"); + Object.defineProperty(actual, mySymbol, { + enumerable: false, + value: 5, + }); + expect(actual).toEqual({ x: 3 }); + }); + + test("properties with the same circularity are equal", () => { + const a = {}; + a.x = a; + const b = {}; + b.x = b; + expect(a).toEqual(b); + expect(b).toEqual(a); + + const c = { + x: a, + }; + const d = { + x: b, + }; + + expect(d).toEqual(c); + expect(c).toEqual(d); + }); + + test("toEqual() - arrays", () => { + expect([1, 2, 3]).toEqual([1, 2, 3]); + expect([1, 2, 3, 4]).not.toEqual([1, 2, 3]); + }); + + test("properties with different circularity are not equal", () => { + const a = {}; + a.x = { y: a }; + const b = {}; + const bx = {}; + b.x = bx; + bx.y = bx; + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + + const c = {}; + c.x = a; + const d = {}; + d.x = b; + expect(c).not.toEqual(d); + expect(d).not.toEqual(c); + }); + + test("are not equal if circularity is not on the same property", () => { + const a = {}; + const b = {}; + a.a1 = a; + b.a1 = {}; + b.a1.a1 = a; + + expect(a).not.toEqual(b); + expect(b).not.toEqual(a); + + const c = {}; + c.x = { x: c }; + const d = {}; + d.x = d; + + expect(d).not.toEqual(c); + expect(c).not.toEqual(d); + }); + + test("random isEqual tests", () => { + expect(1).toEqual(1); + expect(1).not.toEqual(2); + expect(1).not.toEqual("1"); + expect(1).not.toEqual(true); + expect(1).not.toEqual(false); + expect(1).not.toEqual(null); + expect(1).not.toEqual(undefined); + expect(1).not.toEqual({}); + expect(1).not.toEqual([]); + expect(1).not.toEqual([1]); + expect(1).not.toEqual([1, 2]); + expect(1).not.toEqual([1, 2, 3]); + expect(1).not.toEqual([1, 2, 3, 4]); + expect(1).not.toEqual([1, 2, 3, 4, 5]); + expect(1).not.toEqual([1, 2, 3, 4, 5, 6]); + expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7]); + expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8]); + expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); + expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); + expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8]); + + // test toEquals for objects with getters and setters + + expect([]).toEqual([]); + expect([1]).toEqual([1]); + expect([1, 2]).toEqual([1, 2]); + expect([1, 2, 3]).toEqual([1, 2, 3]); + expect({}).toEqual({}); + expect({}).not.toEqual([]); + expect([]).not.toEqual({}); + + const obj = { + get a() { + return 1; + }, + }; + expect(obj).toEqual({ a: 1 }); + expect({ a: 1 }).toEqual(obj); + expect(obj).not.toEqual({ a: 2 }); + expect({ a: 2 }).not.toEqual(obj); + + let a = new Set(); + a.add([1, 2, 3]); + a.add("hello"); + a.add({ a: 1 }); + a.add(89); + let b = new Set(); + b.add(89); + b.add({ a: 1 }); + b.add("hello"); + b.add([1, 2, 3]); + expect(a).toEqual(b); + expect(b).toEqual(a); + let c = new Set(); + c.add(89); + c.add("helo"); + c.add({ a: 1 }); + c.add([1, 2, 3]); + expect(a).not.toEqual(c); + + a = new Map(); + a.set(1, 89); + a.set("hello", 2); + a.set({ a: 1 }, 3); + a.set([1, 2, 3], 4); + b = new Map(); + b.set(1, 89); + b.set("hello", 2); + b.set({ a: 1 }, 3); + b.set([1, 2, 3], 4); + expect(a).toEqual(b); + expect(b).toEqual(a); + c = new Map(); + c.set({ a: 1 }, 3); + c.set(1, 80); + c.set([1, 2, 3], 4); + c.set("hello", 2); + expect(a).not.toEqual(c); + + a = new Set(); + a.add(89); + a.add("hello"); + a.add({ a: 1 }); + a.add([1, 2, 3]); + a.add(a); + b = new Set(); + b.add(89); + b.add("hello"); + b.add(b); + b.add({ a: 1 }); + b.add([1, 2, 3]); + expect(a).toEqual(b); + expect(b).toEqual(a); + }); + test("toHaveProperty() - emojis", () => { + expect({ "👍": "thumbs up" }).toHaveProperty("👍", "thumbs up"); + expect({ "👩👩👧👧": "family" }).toHaveProperty("👩👩👧👧", "family"); + expect({ "😶🌫️": "fog" }).toHaveProperty("😶🌫️", "fog"); + expect({ "👩❤️👨": "couple" }).toHaveProperty("👩❤️👨", "couple"); + expect({ "👩❤️👨👨👧👧": "family" }).toHaveProperty("👩❤️👨👨👧👧", "family"); + expect({ "👩❤️👨👨👧": "family" }).toHaveProperty("👩❤️👨👨👧", "family"); + expect({ "👩❤️👨👨👧": "family" }).not.toHaveProperty("👩❤️👨👨👧👧", "family"); + + // emojis in array + expect(["👍", "👎"]).toHaveProperty("0", "👍"); + expect(["👍", "👎"]).toHaveProperty("1", "👎"); + expect(["👍", "👎"]).not.toHaveProperty("0", "👎"); + expect(["👍", "👎"]).not.toHaveProperty("1", "👍"); + expect(["👩❤️👨👨👧👧"]).toHaveProperty("0", "👩❤️👨👨👧👧"); + expect(["👩❤️👨👨👧👧"]).toHaveProperty([0], "👩❤️👨👨👧👧"); + expect(["😶🌫️"]).toHaveProperty([0], "😶🌫️"); + }); + + test("toHaveProperty() - dot and bracket notation edge cases", () => { + expect({ a: 1 }).not.toHaveProperty("."); + expect({ a: 1 }).not.toHaveProperty("]"); + expect({ a: 1 }).not.toHaveProperty("["); + expect({ a: 1 }).not.toHaveProperty("[]"); + expect({ a: 1 }).not.toHaveProperty("[[]]"); + expect({ a: 1 }).not.toHaveProperty("[["); + expect({ a: 1 }).not.toHaveProperty("]]"); + expect({ a: 1 }).not.toHaveProperty("[]]"); + expect({ a: 1 }).not.toHaveProperty("[[]"); + expect({ a: 1 }).not.toHaveProperty(".]"); + expect({ a: 1 }).not.toHaveProperty(".["); + expect({ "": 1 }).toHaveProperty("[.", 1); + expect({ a: 1 }).not.toHaveProperty("[."); + expect({ a: 1 }).not.toHaveProperty("]."); + expect({ a: 1 }).not.toHaveProperty("].["); + expect({ a: 1 }).not.toHaveProperty("].]"); + expect({ a: 1 }).not.toHaveProperty("[.]"); + expect({ a: 1 }).not.toHaveProperty("[.["); + + expect([1]).toHaveProperty("[0]", 1); + expect([1]).toHaveProperty("[0][", 1); + expect([1]).toHaveProperty("[0]]", 1); + expect([1]).toHaveProperty("[0][[", 1); + expect([1]).toHaveProperty("[][[[0]", 1); + expect([1]).toHaveProperty("[][[[]][[][][.0", 1); + expect([1]).toHaveProperty("[][[[]][[][][.[][[][[[][][0", 1); + expect([1]).not.toHaveProperty("......1.............", 1); + expect([1]).not.toHaveProperty("......0.............", 1); + expect([1]).not.toHaveProperty(".0", 1); + expect([1]).not.toHaveProperty("0.", 1); + expect([{ "": 1 }]).toHaveProperty("0.", 1); + expect({ "": { "": 1 } }).toHaveProperty(".", 1); + expect({ "": { "": { "": 1 } } }).toHaveProperty("..", 1); + expect({ "": { "": { "": 1 } } }).not.toHaveProperty(".", 1); + expect({ "": { "": { "": 1 } } }).not.toHaveProperty("...", 1); + expect({ "": { "": { "": 1 } } }).not.toHaveProperty("....", 1); + expect([1]).toHaveProperty("0.[[[][][]][[[][[]]]]", 1); + expect([1]).not.toHaveProperty("[0].", 1); + expect([1]).toHaveProperty("0", 1); + expect([1]).toHaveProperty("[].0", 1); + expect([1]).toHaveProperty("[.0", 1); + expect([1]).toHaveProperty("].0", 1); + expect([1]).toHaveProperty("0[]][[[]", 1); + expect([1]).toHaveProperty("[[]][[[][][0", 1); + expect([1]).toHaveProperty("0", 1); + expect([1]).toHaveProperty("0.[", 1); + expect([1]).not.toHaveProperty("0........[", 1); + expect([1]).not.toHaveProperty("0..[", 1); + expect([1]).not.toHaveProperty(".0", 1); + expect([1]).toHaveProperty("[].0", 1); + expect([1]).not.toHaveProperty("[]..0", 1); + expect([1]).toHaveProperty("[.][.[[.]]]]].[.[].].]]]]].].].0", 1); + expect([1]).not.toHaveProperty("[.][.[[.]]]]].[.[].].]]0]]].].].", 1); + expect([1]).toHaveProperty("[.][.[[.]]]]].[.[].].]]0]]].].]", 1); + expect([1]).not.toHaveProperty("[.][.[[..]]]]].[.[].].]]0]]].].]", 1); + expect([1]).toHaveProperty("[.][.[[.]]]]].[.[].].0.]]]]].].]", 1); + expect([1]).not.toHaveProperty("[.][.[[.]]]]].[.[].].0.]]] ]].].]", 1); + expect([1]).not.toHaveProperty("0 ", 1); + expect([1]).not.toHaveProperty(" 0 ", 1); + expect([1]).not.toHaveProperty(" 0[] ", 1); + expect([1]).not.toHaveProperty(" 0] ", 1); + expect([1]).not.toHaveProperty(" .[0]", 1); + + expect({ "": 1 }).not.toHaveProperty(".", 1); + expect({ "": 1 }).not.toHaveProperty("]", 1); + expect({ "": 1 }).not.toHaveProperty("[", 1); + expect({ "": 1 }).toHaveProperty("", 1); + + expect({ "": 1 }).not.toHaveProperty("..", 1); + expect({ "": { "": 1 } }).not.toHaveProperty("..", 1); + expect([{ "": 1 }]).toHaveProperty("0.", 1); + expect([{ "": 1 }]).not.toHaveProperty(".0.", 1); + expect({ "": [1] }).toHaveProperty(".0", 1); + expect({ "": [1] }).not.toHaveProperty("..0", 1); + expect([{ "": 1 }]).not.toHaveProperty("0..", 1); + expect([{ "": { "": 1 } }]).toHaveProperty("0..", 1); + + expect([1]).not.toHaveProperty("[0].", 1); + expect([1]).not.toHaveProperty("[0][0]", 1); + expect({ a: [1] }).toHaveProperty("a[[[[[[[[[0]]]", 1); + expect({ "[[[": 0 }).not.toHaveProperty("[[[", 0); + }); + + test("toHaveProperty() - with string or array", () => { + const a = new Array(["a", "b", "c"]); + expect(a).toHaveProperty("0.1", "b"); + const b = new Array("a", "b", "c"); + expect({ a: { b: { c: 1 } } }).toHaveProperty(b); + const c = { + a: { b: 1 }, + "a.b": 2, + }; + const d = new Array("a.b"); + expect(c).toHaveProperty(d, 2); + const houseForSale = { + bath: true, + bedrooms: 4, + kitchen: { + amenities: ["oven", "stove", "washer"], + area: 20, + wallColor: "white", + "nice.oven": true, + }, + livingroom: { + amenities: [ + { + couch: [ + ["large", { dimensions: [20, 20] }], + ["small", { dimensions: [10, 10] }], + ], + }, + ], + }, + sunroom: "yes", + "ceiling.height": 20, + "entrance.window": 3, + entrance: { window: 5 }, + }; + expect(houseForSale).toHaveProperty("entrance.window", 5); + expect(houseForSale).toHaveProperty(["entrance", "window"], 5); + expect(houseForSale).toHaveProperty(["entrance.window"], 3); + expect(houseForSale).toHaveProperty("bath"); + expect(houseForSale).not.toHaveProperty("jacuzzi"); + // expect(houseForSale).toHaveProperty("jacuzzi"); + // expect(houseForSale).not.toHaveProperty("bath"); + expect(houseForSale).toHaveProperty("bath", true); + expect(houseForSale).not.toHaveProperty("bath", false); + // expect(houseForSale).toHaveProperty("bath", false); + // expect(houseForSale).not.toHaveProperty("bath", true); + expect(houseForSale).toHaveProperty("bedrooms", 4); + expect(houseForSale).toHaveProperty(["sunroom"], "yes"); + expect(houseForSale).toHaveProperty("kitchen.area", 20); + expect(houseForSale).toHaveProperty("kitchen.amenities", ["oven", "stove", "washer"]); + expect(houseForSale).not.toHaveProperty(["kitchen", "area"], 21); + expect(houseForSale).toHaveProperty(["kitchen", "area"], 20); + expect(houseForSale).not.toHaveProperty(["kitchen", "area"], 29); + expect(houseForSale).toHaveProperty(["kitchen", "amenities"], ["oven", "stove", "washer"]); + expect(houseForSale).toHaveProperty("kitchen.amenities[2]", "washer"); + expect(houseForSale).toHaveProperty(["kitchen", "amenities", 1], "stove"); + expect(houseForSale).toHaveProperty(["kitchen", "amenities", 0], "oven"); + expect(houseForSale).toHaveProperty("livingroom.amenities[0].couch[0][1].dimensions[0]", 20); + expect(houseForSale).toHaveProperty(["kitchen", "nice.oven"]); + expect(houseForSale).not.toHaveProperty(["kitchen", "open"]); + expect(houseForSale).toHaveProperty(["ceiling.height"], 20); + expect({ a: { b: 1 } }).toHaveProperty("a.b"); + expect({ a: [2, 3, 4] }).toHaveProperty("a.0"); + expect({ a: [2, 3, 4] }).toHaveProperty("a.1"); + expect({ a: [2, 3, 4] }).toHaveProperty("a.2"); + expect({ a: [2, 3, 4] }).toHaveProperty("a[1]"); + expect([2, 3, 4]).toHaveProperty("1"); + expect([2, 3, 4]).toHaveProperty("[1]"); + expect([2, [6, 9], 4]).toHaveProperty("1.1"); + expect([2, [6, 9], 4]).toHaveProperty("1[1]"); + expect([2, [6, 9], 4]).toHaveProperty("[1].1"); + expect([2, [6, 9], 4]).toHaveProperty("[1][1]"); + expect([2, [6, 9], 4]).toHaveProperty([0], 2); + expect({ a: { b: 1 } }).toHaveProperty("a.b"); + expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a.2.1.b"); + expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a"); + expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a[2][1].b"); + expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a[2][1]"); + expect({ a: [1, 2, [3, { b: 1 }]] }).not.toHaveProperty("a[2][1].c"); + expect("test").toHaveProperty("length"); + expect({}).toHaveProperty("constructor"); + expect({}).toHaveProperty("constructor.name"); + expect({}).toHaveProperty("constructor.name", "Object"); + expect(new Date()).toHaveProperty("getTime"); + }); + + test("toHaveProperty() - all", () => { + expect({ a: 1 }).toHaveProperty("a"); + expect({ a: 1 }).toHaveProperty("a", 1); + expect({ a: 1 }).not.toHaveProperty("b"); + expect({ a: 1 }).not.toHaveProperty("a", 2); + + // test with object with property "a" with all types of values (including undefined) + expect({ a: undefined }).toHaveProperty("a"); + expect({ a: null }).toHaveProperty("a"); + expect({ a: 0 }).toHaveProperty("a"); + expect({ a: false }).toHaveProperty("a"); + expect({ a: "" }).toHaveProperty("a"); + expect({ a: {} }).toHaveProperty("a"); + expect({ a: [] }).toHaveProperty("a"); + expect({ a: () => {} }).toHaveProperty("a"); + + // test with object with property "a" with all types of values (including undefined) + expect({ a: undefined }).toHaveProperty("a", undefined); + expect({ a: null }).toHaveProperty("a", null); + expect({ a: 0 }).toHaveProperty("a", 0); + expect({ a: false }).toHaveProperty("a", false); + expect({ a: "" }).toHaveProperty("a", ""); + expect({ a: {} }).toHaveProperty("a", {}); + expect({ a: [] }).toHaveProperty("a", []); + expect({ a: () => {} }).not.toHaveProperty("a", () => {}); + + // test with object with property "a" with all types of values (including undefined) + + expect({ a: undefined }).not.toHaveProperty("a", null); + expect({ a: null }).not.toHaveProperty("a", undefined); + expect({ a: 0 }).not.toHaveProperty("a", null); + expect({ a: false }).not.toHaveProperty("a", null); + expect({ a: "" }).not.toHaveProperty("a", null); + expect({ a: {} }).not.toHaveProperty("a", null); + expect({ a: [] }).not.toHaveProperty("a", null); + expect({ a: () => {} }).not.toHaveProperty("a", null); + + expect({ a: undefined }).not.toHaveProperty("a", 0); + expect({ a: null }).not.toHaveProperty("a", 0); + expect({ a: 0 }).not.toHaveProperty("a", 1); + expect({ a: false }).not.toHaveProperty("a", 0); + expect({ a: "" }).not.toHaveProperty("a", 0); + expect({ a: {} }).not.toHaveProperty("a", 0); + expect({ a: [] }).not.toHaveProperty("a", 0); + expect({ a: () => {} }).not.toHaveProperty("a", 0); + + expect({ a: undefined }).not.toHaveProperty("a", false); + expect({ a: null }).not.toHaveProperty("a", false); + expect({ a: 0 }).not.toHaveProperty("a", false); + expect({ a: false }).not.toHaveProperty("a", true); + expect({ a: "" }).not.toHaveProperty("a", false); + expect({ a: {} }).not.toHaveProperty("a", false); + expect({ a: [] }).not.toHaveProperty("a", false); + expect({ a: () => {} }).not.toHaveProperty("a", false); + + expect({ a: undefined }).not.toHaveProperty("a", ""); + expect({ a: null }).not.toHaveProperty("a", ""); + expect({ a: 0 }).not.toHaveProperty("a", ""); + expect({ a: false }).not.toHaveProperty("a", ""); + expect({ a: "" }).not.toHaveProperty("a", "a"); + expect({ a: {} }).not.toHaveProperty("a", ""); + expect({ a: [] }).not.toHaveProperty("a", ""); + expect({ a: () => {} }).not.toHaveProperty("a", ""); + + expect({ a: undefined }).not.toHaveProperty("a", {}); + expect({ a: null }).not.toHaveProperty("a", {}); + expect({ a: 0 }).not.toHaveProperty("a", {}); + expect({ a: false }).not.toHaveProperty("a", {}); + expect({ a: "" }).not.toHaveProperty("a", {}); + expect({ a: {} }).not.toHaveProperty("a", { a: 1 }); + expect({ a: [] }).not.toHaveProperty("a", {}); + expect({ a: () => {} }).not.toHaveProperty("a", {}); + + // test object with property "a" with value set, map, string + expect({ a: new Set([1, 2, 3]) }).toHaveProperty("a", new Set([3, 2, 1])); + expect({ a: new Map([{ a: 1 }, { b: 2 }, { c: 3 }]) }).toHaveProperty("a", new Map([{ c: 3 }, { b: 2 }, { a: 1 }])); + expect({ a: new String("a") }).toHaveProperty("a", new String("a")); + expect({ a: new String("a") }).not.toHaveProperty("a", "a"); + expect({ a: new String("a") }).not.toHaveProperty("a", "b"); + expect({ a: new String("a") }).not.toHaveProperty("a", new String("b")); + expect({ a: new String("a") }).not.toHaveProperty("a", new Number(1)); + expect({ a: new String("a") }).not.toHaveProperty("a", new Boolean(true)); + expect({ a: new String("a") }).not.toHaveProperty("a", new Boolean(false)); + expect({ a: new String("a") }).not.toHaveProperty("a", new Object()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Function()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Date()); + expect({ a: new String("a") }).not.toHaveProperty("a", new RegExp()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Error()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Promise(() => {})); + expect({ a: new String("a") }).not.toHaveProperty("a", new WeakSet()); + expect({ a: new String("a") }).not.toHaveProperty("a", new WeakMap()); + expect({ a: new String("a") }).not.toHaveProperty("a", Symbol("a")); + expect({ a: new String("a") }).not.toHaveProperty("a", new Int8Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Uint8Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Uint8ClampedArray()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Int16Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Uint16Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Int32Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Uint32Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Float32Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new Float64Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new BigInt64Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new BigUint64Array()); + expect({ a: new String("a") }).not.toHaveProperty("a", new ArrayBuffer()); + expect({ a: new String("a") }).not.toHaveProperty("a", new SharedArrayBuffer()); + expect({ a: new String("a") }).not.toHaveProperty("a", new DataView(new ArrayBuffer(1))); + + // test property equality with sets, maps, objects, arrays, and String + expect({ a: new Set([1, 2, 3]) }).toHaveProperty("a", new Set([1, 2, 3])); + expect({ a: new Map([{ a: 1 }, { b: 2 }, { c: 3 }]) }).toHaveProperty("a", new Map([{ a: 1 }, { b: 2 }, { c: 3 }])); + expect({ a: { a: 1, b: 2, c: 3 } }).toHaveProperty("a", { a: 1, b: 2, c: 3 }); + expect({ a: [1, 2, 3] }).toHaveProperty("a", [1, 2, 3]); + expect({ a: "a" }).toHaveProperty("a", "a"); + expect({ a: new String("a") }).toHaveProperty("a", new String("a")); + expect({ a: new String("a") }).not.toHaveProperty("a", "a"); + }); + + test.todo("toHaveProperty() - null or undefined", () => { + expect(() => expect(null).toHaveProperty("length")).toThrow(); + expect(() => expect(null).not.toHaveProperty("length")).toThrow(); + expect(() => expect(undefined).toHaveProperty("length")).toThrow(); + expect(() => expect(undefined).not.toHaveProperty("length")).toThrow(); + }); + + test("toBe()", () => { + const a = 1; + const b = 1; + expect(a).toBe(a); + expect(a).toBe(b); + expect(a).toBe(1); + expect(1).toBe(a); + expect(b).toBe(a); + + const c = { a: 1 }; + const d = { a: 1 }; + expect(c).toBe(c); + expect(c).not.toBe(d); + expect(c).not.toBe({ a: 1 }); + expect({ a: 1 }).not.toBe(c); + expect(d).not.toBe(c); + + expect(1).toBe(1); + // expect(1).not.toBe(1); + + expect(1).not.toBe(2); + expect(1).not.toBe("1"); + expect("hello test").toBe("hello test"); + expect("hello test").not.toBe("hello test2"); + }); + + test("toHaveLength()", () => { + expect({ length: Number.MAX_SAFE_INTEGER }).toHaveLength(Number.MAX_SAFE_INTEGER); + expect("123").toHaveLength(3); + expect([1, 2, 3]).toHaveLength(3); + expect([1, 2, 3]).not.toHaveLength(2); + expect("123").not.toHaveLength(2); + expect({ length: 3 }).toHaveLength(3); + expect({ length: 3 }).not.toHaveLength(2); + expect({ length: 3 }).not.toHaveLength(Number.MAX_SAFE_INTEGER); + expect({ length: Number.MAX_SAFE_INTEGER }).not.toHaveLength(Number.MAX_SAFE_INTEGER - 1); + expect({ length: 3.3 }).not.toHaveLength(3); + expect("123").not.toHaveLength(-0); + }); + + if (isBun) { + test("toHaveLength() extended", () => { + // Headers + expect(new Headers()).toHaveLength(0); + expect(new Headers({ a: "1" })).toHaveLength(1); + + // FormData + const form = new FormData(); + expect(form).toHaveLength(0); + form.append("a", "1"); + expect(form).toHaveLength(1); + + // URLSearchParams + expect(new URLSearchParams()).toHaveLength(0); + expect(new URLSearchParams("a=1")).toHaveLength(1); + expect(new URLSearchParams([["a", "1"]])).toHaveLength(1); + + // files + const thisFile = Bun.file(__filename); + const thisFileSize = thisFile.size; + + expect(thisFile).toHaveLength(thisFileSize); + expect(thisFile).toHaveLength(Bun.file(__filename).size); + + // empty file should have length 0 + require("fs").writeFileSync("/tmp/empty.txt", ""); + expect(Bun.file("/tmp/empty.txt")).toHaveLength(0); + + // if a file doesn't exist, it should throw (not return 0 size) + expect(() => expect(Bun.file("/does-not-exist/file.txt")).toHaveLength(0)).toThrow(); + + // Blob + expect(new Blob([1, 2, 3])).toHaveLength(3); + expect(new Blob()).toHaveLength(0); + + // Set + expect(new Set()).toHaveLength(0); + expect(new Set([1, 2, 3])).toHaveLength(3); + + // Map + expect(new Map()).toHaveLength(0); + expect(new Map([["a", 1]])).toHaveLength(1); + + // WeakMap + expect(new WeakMap([[globalThis, 1]])).toHaveLength(1); + }); + } + + test("toContain()", () => { + const s1 = new String("123"); + expect(s1).not.toContain("12"); + const s2 = "123"; + expect(s2).toContain("12"); + + expect("test").toContain("es"); + expect("test").toContain("est"); + // expect("test").not.toContain("test"); + expect(["test", "es"]).toContain("es"); + expect("").toContain(""); + expect([""]).toContain(""); + + expect(["lemon", "lime"]).not.toContain("orange"); + expect("citrus fruits").toContain("fruit"); + + const a = new Uint16Array([1, 2, 3]); + expect(a).toContain(2); + expect(a).not.toContain(4); + expect([2, "2335", 5, true, false, null, undefined]).toContain(5); + expect([2, "2335", 5, true, false, null, undefined]).toContain("2335"); + expect([2, "2335", 5, true, false, null, undefined]).toContain(true); + expect([2, "2335", 5, true, false, null, undefined]).toContain(false); + expect([2, "2335", 5, true, false, null, undefined]).toContain(null); + expect([2, "2335", 5, true, false, null, undefined]).toContain(undefined); + expect([2, "2335", 5, true, false, null, undefined]).not.toContain(3); + + // expect([4, 5, 6]).not.toContain(5); + + expect([]).not.toContain([]); + }); + + test("toBeTruthy()", () => { + expect("test").toBeTruthy(); + expect(true).toBeTruthy(); + expect(1).toBeTruthy(); + expect({}).toBeTruthy(); + expect([]).toBeTruthy(); + expect(() => {}).toBeTruthy(); + // expect(() => {}).not.toBeTruthy(); + expect(0.5).toBeTruthy(); + expect(new Map()).toBeTruthy(); + + expect("").not.toBeTruthy(); + expect(0).not.toBeTruthy(); + expect(-0).not.toBeTruthy(); + expect(NaN).not.toBeTruthy(); + expect(0n).not.toBeTruthy(); + expect(0.0e1).not.toBeTruthy(); + expect(false).not.toBeTruthy(); + expect(null).not.toBeTruthy(); + expect(undefined).not.toBeTruthy(); + }); + + test("toBeUndefined()", () => { + expect(undefined).toBeUndefined(); + expect(() => expect(undefined).not.toBeUndefined()).toThrow(); + expect(null).not.toBeUndefined(); + expect(0).not.toBeUndefined(); + expect("hello defined").not.toBeUndefined(); + }); + + test("toBeNull()", () => { + expect(null).toBeNull(); + // expect(null).not.toBeNull(); + + expect(undefined).not.toBeNull(); + expect(0).not.toBeNull(); + expect("hello not null").not.toBeNull(); + }); + + test("toBeDefined()", () => { + expect(0).toBeDefined(); + expect("hello defined").toBeDefined(); + expect(null).toBeDefined(); + // expect(null).not.toBeDefined(); + + expect(undefined).not.toBeDefined(); + }); + + test("toBeFalsy()", () => { + expect("").toBeFalsy(); + expect(0).toBeFalsy(); + expect(-0).toBeFalsy(); + expect(NaN).toBeFalsy(); + expect(0n).toBeFalsy(); + expect(false).toBeFalsy(); + expect(null).toBeFalsy(); + expect(undefined).toBeFalsy(); + expect(() => expect("").not.toBeFalsy()).toThrow(); + expect(() => expect(0).not.toBeFalsy()).toThrow(); + expect(() => expect(-0).not.toBeFalsy()).toThrow(); + expect(() => expect(NaN).not.toBeFalsy()).toThrow(); + expect(() => expect(0n).not.toBeFalsy()).toThrow(); + expect(() => expect(false).not.toBeFalsy()).toThrow(); + expect(() => expect(null).not.toBeFalsy()).toThrow(); + expect(() => expect(undefined).not.toBeFalsy()).toThrow(); + + expect("hello not falsy").not.toBeFalsy(); + expect(1).not.toBeFalsy(); + expect(true).not.toBeFalsy(); + expect({}).not.toBeFalsy(); + expect([]).not.toBeFalsy(); + expect(() => {}).not.toBeFalsy(); + expect(() => expect("hello not falsy").toBeFalsy()).toThrow(); + expect(() => expect(1).toBeFalsy()).toThrow(); + expect(() => expect(true).toBeFalsy()).toThrow(); + expect(() => expect({}).toBeFalsy()).toThrow(); + expect(() => expect([]).toBeFalsy()).toThrow(); + expect(() => expect(() => {}).toBeFalsy()).toThrow(); + }); + + test("toBeGreaterThan()", () => { + expect(3n).toBeGreaterThan(2); + expect(Number.MAX_VALUE).not.toBeGreaterThan(Number.MAX_VALUE); + expect(1).not.toBeGreaterThan(BigInt(Number.MAX_VALUE)); + expect(1).not.toBeGreaterThan(Number.MAX_SAFE_INTEGER); + expect(1).not.toBeGreaterThan(BigInt(Number.MAX_SAFE_INTEGER)); + expect(Number.MAX_SAFE_INTEGER).not.toBeGreaterThan(Number.MAX_SAFE_INTEGER); + expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeGreaterThan(BigInt(Number.MAX_SAFE_INTEGER)); + + expect(Infinity).toBeGreaterThan(-Infinity); + expect(-Infinity).not.toBeGreaterThan(Infinity); + + expect(NaN).not.toBeGreaterThan(NaN); + expect(NaN).not.toBeGreaterThan(-Infinity); + + expect(10).toBeGreaterThan(9); + expect(10).not.toBeGreaterThan(10); + expect(10).not.toBeGreaterThan(11); + expect(10).not.toBeGreaterThan(Infinity); + expect(10).toBeGreaterThan(-Infinity); + expect(10).not.toBeGreaterThan(NaN); + expect(10).toBeGreaterThan(0); + expect(10).toBeGreaterThan(-0); + expect(10).toBeGreaterThan(0.1); + expect(10).toBeGreaterThan(-0.1); + expect(10).toBeGreaterThan(0.9); + expect(10).toBeGreaterThan(-0.9); + expect(10).toBeGreaterThan(1); + expect(10).toBeGreaterThan(-1); + // switch the order + expect(9).not.toBeGreaterThan(10); + expect(10).not.toBeGreaterThan(10); + expect(11).toBeGreaterThan(10); + expect(Infinity).toBeGreaterThan(10); + expect(-Infinity).not.toBeGreaterThan(10); + expect(NaN).not.toBeGreaterThan(10); + expect(0).not.toBeGreaterThan(10); + expect(-0).not.toBeGreaterThan(10); + expect(0.1).not.toBeGreaterThan(10); + expect(-0.1).not.toBeGreaterThan(10); + expect(0.9).not.toBeGreaterThan(10); + expect(-0.9).not.toBeGreaterThan(10); + expect(1).not.toBeGreaterThan(10); + expect(-1).not.toBeGreaterThan(10); + + // same tests but use bigints + expect(10n).toBeGreaterThan(9n); + expect(10n).not.toBeGreaterThan(10n); + expect(10n).not.toBeGreaterThan(11n); + expect(10n).not.toBeGreaterThan(Infinity); + expect(10n).toBeGreaterThan(-Infinity); + expect(10n).not.toBeGreaterThan(NaN); + expect(10n).toBeGreaterThan(0n); + expect(10n).toBeGreaterThan(-0n); + expect(10n).toBeGreaterThan(1n); + expect(10n).toBeGreaterThan(-1n); + // switch the order + expect(9n).not.toBeGreaterThan(10n); + expect(10n).not.toBeGreaterThan(10n); + expect(11n).toBeGreaterThan(10n); + expect(Infinity).toBeGreaterThan(10n); + expect(-Infinity).not.toBeGreaterThan(10n); + expect(NaN).not.toBeGreaterThan(10n); + expect(0n).not.toBeGreaterThan(10n); + expect(-0n).not.toBeGreaterThan(10n); + expect(1n).not.toBeGreaterThan(10n); + expect(-1n).not.toBeGreaterThan(10n); + + // use bigints and numbers + expect(10n).toBeGreaterThan(9); + expect(10n).not.toBeGreaterThan(10); + expect(10n).not.toBeGreaterThan(11); + expect(10n).not.toBeGreaterThan(Infinity); + expect(10n).toBeGreaterThan(-Infinity); + expect(10n).not.toBeGreaterThan(NaN); + expect(10n).toBeGreaterThan(0); + expect(10n).toBeGreaterThan(-0); + expect(10n).toBeGreaterThan(0.1); + expect(10n).toBeGreaterThan(-0.1); + expect(10n).toBeGreaterThan(0.9); + expect(10n).toBeGreaterThan(-0.9); + expect(10n).toBeGreaterThan(1); + expect(10n).toBeGreaterThan(-1); + // switch the order + expect(9n).not.toBeGreaterThan(10); + expect(10n).not.toBeGreaterThan(10); + expect(11n).toBeGreaterThan(10); + expect(Infinity).toBeGreaterThan(10n); + expect(-Infinity).not.toBeGreaterThan(10n); + expect(NaN).not.toBeGreaterThan(10n); + expect(0n).not.toBeGreaterThan(10); + expect(-0n).not.toBeGreaterThan(10); + expect(1n).not.toBeGreaterThan(10); + expect(-1n).not.toBeGreaterThan(10); + + expect(1n).not.toBeGreaterThan(1); + expect(1n).not.toBeGreaterThan(Number.MAX_SAFE_INTEGER); + expect(1n).not.toBeGreaterThan(Number.MAX_VALUE); + expect(1).not.toBeGreaterThan(1n); + expect(Number.MAX_SAFE_INTEGER).toBeGreaterThan(1n); + expect(Number.MAX_VALUE).toBeGreaterThan(1n); + + expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeGreaterThan(1n); + expect(BigInt(Number.MAX_VALUE)).toBeGreaterThan(1n); + expect(1n).not.toBeGreaterThan(BigInt(Number.MAX_SAFE_INTEGER)); + expect(1n).not.toBeGreaterThan(BigInt(Number.MAX_VALUE)); + + expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeGreaterThan(1); + expect(BigInt(Number.MAX_VALUE)).toBeGreaterThan(1); + expect(1).not.toBeGreaterThan(BigInt(Number.MAX_SAFE_INTEGER)); + }); + + test("toBeGreaterThanOrEqual()", () => { + expect(Number.MAX_VALUE).toBeGreaterThanOrEqual(Number.MAX_VALUE); + expect(1).not.toBeGreaterThanOrEqual(Number.MAX_SAFE_INTEGER); + expect(1).not.toBeGreaterThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); + expect(1).not.toBeGreaterThanOrEqual(BigInt(Number.MAX_VALUE)); + expect(Number.MAX_SAFE_INTEGER).toBeGreaterThanOrEqual(Number.MAX_SAFE_INTEGER); + expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeGreaterThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); + + expect(Infinity).toBeGreaterThanOrEqual(-Infinity); + expect(-Infinity).not.toBeGreaterThanOrEqual(Infinity); + + expect(NaN).not.toBeGreaterThanOrEqual(NaN); + expect(NaN).not.toBeGreaterThanOrEqual(-Infinity); + + expect(10).toBeGreaterThanOrEqual(9); + expect(10).toBeGreaterThanOrEqual(10); + expect(10).not.toBeGreaterThanOrEqual(11); + expect(10).not.toBeGreaterThanOrEqual(Infinity); + expect(10).toBeGreaterThanOrEqual(-Infinity); + expect(10).not.toBeGreaterThanOrEqual(NaN); + expect(10).toBeGreaterThanOrEqual(0); + expect(10).toBeGreaterThanOrEqual(-0); + expect(10).toBeGreaterThanOrEqual(0.1); + expect(10).toBeGreaterThanOrEqual(-0.1); + expect(10).toBeGreaterThanOrEqual(0.9); + expect(10).toBeGreaterThanOrEqual(-0.9); + expect(10).toBeGreaterThanOrEqual(1); + expect(10).toBeGreaterThanOrEqual(-1); + // switch the order + expect(9).not.toBeGreaterThanOrEqual(10); + expect(10).toBeGreaterThanOrEqual(10); + expect(11).toBeGreaterThanOrEqual(10); + expect(Infinity).toBeGreaterThanOrEqual(10); + expect(-Infinity).not.toBeGreaterThanOrEqual(10); + expect(NaN).not.toBeGreaterThanOrEqual(10); + expect(0).not.toBeGreaterThanOrEqual(10); + expect(-0).not.toBeGreaterThanOrEqual(10); + expect(0.1).not.toBeGreaterThanOrEqual(10); + expect(-0.1).not.toBeGreaterThanOrEqual(10); + expect(0.9).not.toBeGreaterThanOrEqual(10); + expect(-0.9).not.toBeGreaterThanOrEqual(10); + expect(1).not.toBeGreaterThanOrEqual(10); + expect(-1).not.toBeGreaterThanOrEqual(10); + + // same tests but use bigints + expect(10n).toBeGreaterThanOrEqual(9n); + expect(10n).toBeGreaterThanOrEqual(10n); + expect(10n).not.toBeGreaterThanOrEqual(11n); + expect(10n).not.toBeGreaterThanOrEqual(Infinity); + expect(10n).toBeGreaterThanOrEqual(-Infinity); + expect(10n).not.toBeGreaterThanOrEqual(NaN); + expect(10n).toBeGreaterThanOrEqual(0n); + expect(10n).toBeGreaterThanOrEqual(-0n); + expect(10n).toBeGreaterThanOrEqual(1n); + expect(10n).toBeGreaterThanOrEqual(-1n); + // switch the order + expect(9n).not.toBeGreaterThanOrEqual(10n); + expect(10n).toBeGreaterThanOrEqual(10n); + expect(11n).toBeGreaterThanOrEqual(10n); + expect(Infinity).toBeGreaterThanOrEqual(10n); + expect(-Infinity).not.toBeGreaterThanOrEqual(10n); + expect(NaN).not.toBeGreaterThanOrEqual(10n); + expect(0n).not.toBeGreaterThanOrEqual(10n); + expect(-0n).not.toBeGreaterThanOrEqual(10n); + expect(1n).not.toBeGreaterThanOrEqual(10n); + expect(-1n).not.toBeGreaterThanOrEqual(10n); + + // use bigints and numbers + expect(10n).toBeGreaterThanOrEqual(9); + expect(10n).toBeGreaterThanOrEqual(10); + expect(10n).not.toBeGreaterThanOrEqual(11); + expect(10n).not.toBeGreaterThanOrEqual(Infinity); + expect(10n).toBeGreaterThanOrEqual(-Infinity); + expect(10n).not.toBeGreaterThanOrEqual(NaN); + expect(10n).toBeGreaterThanOrEqual(0); + expect(10n).toBeGreaterThanOrEqual(-0); + expect(10n).toBeGreaterThanOrEqual(0.1); + expect(10n).toBeGreaterThanOrEqual(-0.1); + expect(10n).toBeGreaterThanOrEqual(0.9); + expect(10n).toBeGreaterThanOrEqual(-0.9); + expect(10n).toBeGreaterThanOrEqual(1); + expect(10n).toBeGreaterThanOrEqual(-1); + // switch the order + expect(9n).not.toBeGreaterThanOrEqual(10); + expect(10n).toBeGreaterThanOrEqual(10); + expect(11n).toBeGreaterThanOrEqual(10); + expect(Infinity).toBeGreaterThanOrEqual(10n); + expect(-Infinity).not.toBeGreaterThanOrEqual(10n); + expect(NaN).not.toBeGreaterThanOrEqual(10n); + expect(0n).not.toBeGreaterThanOrEqual(10); + expect(-0n).not.toBeGreaterThanOrEqual(10); + expect(1n).not.toBeGreaterThanOrEqual(10); + expect(-1n).not.toBeGreaterThanOrEqual(10); + + expect(1n).toBeGreaterThanOrEqual(1); + expect(1n).not.toBeGreaterThanOrEqual(Number.MAX_SAFE_INTEGER); + expect(1n).not.toBeGreaterThanOrEqual(Number.MAX_VALUE); + expect(1).toBeGreaterThanOrEqual(1n); + expect(Number.MAX_SAFE_INTEGER).toBeGreaterThanOrEqual(1n); + expect(Number.MAX_VALUE).toBeGreaterThanOrEqual(1n); + + expect(1).not.toBeGreaterThanOrEqual(BigInt(Number.MAX_VALUE)); + }); + + test("toBeLessThan()", () => { + expect(3n).not.toBeLessThan(2); + expect(Number.MAX_VALUE).not.toBeLessThan(Number.MAX_VALUE); + expect(1).toBeLessThan(BigInt(Number.MAX_VALUE)); + expect(1).toBeLessThan(Number.MAX_SAFE_INTEGER); + expect(1).toBeLessThan(BigInt(Number.MAX_SAFE_INTEGER)); + expect(Number.MAX_SAFE_INTEGER).not.toBeLessThan(Number.MAX_SAFE_INTEGER); + expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThan(BigInt(Number.MAX_SAFE_INTEGER)); + + expect(Number.MAX_VALUE).not.toBeLessThan(BigInt(Number.MAX_VALUE)); + + expect(NaN).not.toBeLessThan(NaN); + expect(NaN).not.toBeLessThan(-Infinity); + + expect(10).not.toBeLessThan(9); + expect(10).not.toBeLessThan(10); + expect(10).toBeLessThan(11); + expect(10).toBeLessThan(Infinity); + expect(10).not.toBeLessThan(-Infinity); + expect(10).not.toBeLessThan(NaN); + expect(10).not.toBeLessThan(0); + expect(10).not.toBeLessThan(-0); + expect(10).not.toBeLessThan(0.1); + expect(10).not.toBeLessThan(-0.1); + expect(10).not.toBeLessThan(0.9); + expect(10).not.toBeLessThan(-0.9); + expect(10).not.toBeLessThan(1); + expect(10).not.toBeLessThan(-1); + // switch the order + expect(9).toBeLessThan(10); + expect(10).not.toBeLessThan(10); + expect(11).not.toBeLessThan(10); + expect(Infinity).not.toBeLessThan(10); + expect(-Infinity).toBeLessThan(10); + expect(NaN).not.toBeLessThan(10); + expect(0).toBeLessThan(10); + expect(-0).toBeLessThan(10); + expect(0.1).toBeLessThan(10); + expect(-0.1).toBeLessThan(10); + expect(0.9).toBeLessThan(10); + expect(-0.9).toBeLessThan(10); + expect(1).toBeLessThan(10); + expect(-1).toBeLessThan(10); + + // same tests but use bigints + expect(10n).not.toBeLessThan(9n); + expect(10n).not.toBeLessThan(10n); + expect(10n).toBeLessThan(11n); + expect(10n).toBeLessThan(Infinity); + expect(10n).not.toBeLessThan(-Infinity); + expect(10n).not.toBeLessThan(NaN); + expect(10n).not.toBeLessThan(0n); + expect(10n).not.toBeLessThan(-0n); + expect(10n).not.toBeLessThan(1n); + expect(10n).not.toBeLessThan(-1n); + // switch the order + expect(9n).toBeLessThan(10n); + expect(10n).not.toBeLessThan(10n); + expect(11n).not.toBeLessThan(10n); + expect(Infinity).not.toBeLessThan(10n); + expect(-Infinity).toBeLessThan(10n); + expect(NaN).not.toBeLessThan(10n); + expect(0n).toBeLessThan(10n); + expect(-0n).toBeLessThan(10n); + expect(1n).toBeLessThan(10n); + expect(-1n).toBeLessThan(10n); + + // use bigints and numbers + expect(10n).not.toBeLessThan(9); + expect(10n).not.toBeLessThan(10); + expect(10n).toBeLessThan(11); + expect(10n).toBeLessThan(Infinity); + expect(10n).not.toBeLessThan(-Infinity); + expect(10n).not.toBeLessThan(NaN); + expect(10n).not.toBeLessThan(0); + expect(10n).not.toBeLessThan(-0); + expect(10n).not.toBeLessThan(0.1); + expect(10n).not.toBeLessThan(-0.1); + expect(10n).not.toBeLessThan(0.9); + expect(10n).not.toBeLessThan(-0.9); + expect(10n).not.toBeLessThan(1); + expect(10n).not.toBeLessThan(-1); + // switch the order + expect(9n).toBeLessThan(10); + expect(10n).not.toBeLessThan(10); + expect(11n).not.toBeLessThan(10); + expect(Infinity).not.toBeLessThan(10n); + expect(-Infinity).toBeLessThan(10n); + expect(NaN).not.toBeLessThan(10n); + expect(0n).toBeLessThan(10); + expect(-0n).toBeLessThan(10); + expect(1n).toBeLessThan(10); + expect(-1n).toBeLessThan(10); + + expect(1n).not.toBeLessThan(1); + expect(1n).toBeLessThan(Number.MAX_SAFE_INTEGER); + expect(1n).toBeLessThan(Number.MAX_VALUE); + expect(1).not.toBeLessThan(1n); + expect(Number.MAX_SAFE_INTEGER).not.toBeLessThan(1n); + expect(Number.MAX_VALUE).not.toBeLessThan(1n); + + expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThan(1n); + expect(BigInt(Number.MAX_VALUE)).not.toBeLessThan(1n); + expect(1n).toBeLessThan(BigInt(Number.MAX_SAFE_INTEGER)); + expect(1n).toBeLessThan(BigInt(Number.MAX_VALUE)); + + expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThan(1); + expect(BigInt(Number.MAX_VALUE)).not.toBeLessThan(1); + expect(1).toBeLessThan(BigInt(Number.MAX_SAFE_INTEGER)); + }); + + test("toBeLessThanOrEqual()", () => { + expect(3n).not.toBeLessThanOrEqual(2); + expect(Number.MAX_VALUE).toBeLessThanOrEqual(Number.MAX_VALUE); + expect(1).toBeLessThanOrEqual(BigInt(Number.MAX_VALUE)); + expect(1).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER); + expect(1).toBeLessThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); + expect(Number.MAX_SAFE_INTEGER).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER); + expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeLessThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); + + expect(Number.MAX_VALUE).toBeLessThanOrEqual(BigInt(Number.MAX_VALUE)); + expect(BigInt(Number.MAX_VALUE)).toBeLessThanOrEqual(Number.MAX_VALUE); + + expect(NaN).not.toBeLessThanOrEqual(NaN); + expect(NaN).not.toBeLessThanOrEqual(-Infinity); + + expect(10).not.toBeLessThanOrEqual(9); + expect(10).toBeLessThanOrEqual(10); + expect(10).toBeLessThanOrEqual(11); + expect(10).toBeLessThanOrEqual(Infinity); + expect(10).not.toBeLessThanOrEqual(-Infinity); + expect(10).not.toBeLessThanOrEqual(NaN); + expect(10).not.toBeLessThanOrEqual(0); + expect(10).not.toBeLessThanOrEqual(-0); + expect(10).not.toBeLessThanOrEqual(0.1); + expect(10).not.toBeLessThanOrEqual(-0.1); + expect(10).not.toBeLessThanOrEqual(0.9); + expect(10).not.toBeLessThanOrEqual(-0.9); + expect(10).not.toBeLessThanOrEqual(1); + expect(10).not.toBeLessThanOrEqual(-1); + // switch the order + expect(9).toBeLessThanOrEqual(10); + expect(10).toBeLessThanOrEqual(10); + expect(11).not.toBeLessThanOrEqual(10); + expect(Infinity).not.toBeLessThanOrEqual(10); + expect(-Infinity).toBeLessThanOrEqual(10); + expect(NaN).not.toBeLessThanOrEqual(10); + expect(0).toBeLessThanOrEqual(10); + expect(-0).toBeLessThanOrEqual(10); + expect(0.1).toBeLessThanOrEqual(10); + expect(-0.1).toBeLessThanOrEqual(10); + expect(0.9).toBeLessThanOrEqual(10); + expect(-0.9).toBeLessThanOrEqual(10); + expect(1).toBeLessThanOrEqual(10); + expect(-1).toBeLessThanOrEqual(10); + + // same tests but use bigints + expect(10n).not.toBeLessThanOrEqual(9n); + expect(10n).toBeLessThanOrEqual(10n); + expect(10n).toBeLessThanOrEqual(11n); + expect(10n).toBeLessThanOrEqual(Infinity); + expect(10n).not.toBeLessThanOrEqual(-Infinity); + expect(10n).not.toBeLessThanOrEqual(NaN); + expect(10n).not.toBeLessThanOrEqual(0n); + expect(10n).not.toBeLessThanOrEqual(-0n); + expect(10n).not.toBeLessThanOrEqual(1n); + expect(10n).not.toBeLessThanOrEqual(-1n); + // switch the order + expect(9n).toBeLessThanOrEqual(10n); + expect(10n).toBeLessThanOrEqual(10n); + expect(11n).not.toBeLessThanOrEqual(10n); + expect(Infinity).not.toBeLessThanOrEqual(10n); + expect(-Infinity).toBeLessThanOrEqual(10n); + expect(NaN).not.toBeLessThanOrEqual(10n); + expect(0n).toBeLessThanOrEqual(10n); + expect(-0n).toBeLessThanOrEqual(10n); + expect(1n).toBeLessThanOrEqual(10n); + expect(-1n).toBeLessThanOrEqual(10n); + + // use bigints and numbers + expect(10n).not.toBeLessThanOrEqual(9); + expect(10n).toBeLessThanOrEqual(10); + expect(10n).toBeLessThanOrEqual(11); + expect(10n).toBeLessThanOrEqual(Infinity); + expect(10n).not.toBeLessThanOrEqual(-Infinity); + expect(10n).not.toBeLessThanOrEqual(NaN); + expect(10n).not.toBeLessThanOrEqual(0); + expect(10n).not.toBeLessThanOrEqual(-0); + expect(10n).not.toBeLessThanOrEqual(0.1); + expect(10n).not.toBeLessThanOrEqual(-0.1); + expect(10n).not.toBeLessThanOrEqual(0.9); + expect(10n).not.toBeLessThanOrEqual(-0.9); + expect(10n).not.toBeLessThanOrEqual(1); + expect(10n).not.toBeLessThanOrEqual(-1); + // switch the order + expect(9n).toBeLessThanOrEqual(10); + expect(10n).toBeLessThanOrEqual(10); + expect(11n).not.toBeLessThanOrEqual(10); + expect(Infinity).not.toBeLessThanOrEqual(10n); + expect(-Infinity).toBeLessThanOrEqual(10n); + expect(NaN).not.toBeLessThanOrEqual(10n); + expect(0n).toBeLessThanOrEqual(10); + expect(-0n).toBeLessThanOrEqual(10); + expect(1n).toBeLessThanOrEqual(10); + expect(-1n).toBeLessThanOrEqual(10); + + expect(1n).toBeLessThanOrEqual(1); + expect(1n).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER); + expect(1n).toBeLessThanOrEqual(Number.MAX_VALUE); + expect(1).toBeLessThanOrEqual(1n); + expect(Number.MAX_SAFE_INTEGER).not.toBeLessThanOrEqual(1n); + expect(Number.MAX_VALUE).not.toBeLessThanOrEqual(1n); + + expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThanOrEqual(1n); + expect(BigInt(Number.MAX_VALUE)).not.toBeLessThanOrEqual(1n); + expect(1n).toBeLessThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); + expect(1n).toBeLessThanOrEqual(BigInt(Number.MAX_VALUE)); + + expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThanOrEqual(1); + expect(BigInt(Number.MAX_VALUE)).not.toBeLessThanOrEqual(1); + expect(1).toBeLessThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); + }); + + describe("toMatchObject", () => { + if (isBun) { + 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); + }); + }); + + describe("toMatch()", () => { + const tests = [ + { + label: "reguler expression", + value: "123", + matched: /123/, + }, + { + label: "reguler expression object", + value: "123", + matched: new RegExp("123"), + }, + { + label: "substring", + value: "123", + matched: "12", + }, + { + label: "substring emojis", + value: "👍👎", + matched: "👍", + }, + { + label: "substring UTF-16", + value: "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊 😇 🙂", + matched: "🥲 ☺️ 😊", + }, + ]; + for (const { label, value, matched } of tests) { + test(label, () => expect(value).toMatch(matched)); + } + }); + + test("toBeNaN()", () => { + expect(NaN).toBeNaN(); + expect(() => expect(NaN).not.toBeNaN()).toThrow(); + expect(0).not.toBeNaN(); + expect("hello not NaN").not.toBeNaN(); + }); + + describe("toBeEmpty()", () => { + const values = [ + "", + [], + {}, + new Set(), + new Map(), + new String(), + new Array(), + new Uint8Array(), + new Object(), + Buffer.from(""), + ...(isBun ? [Bun.file("/tmp/empty.txt")] : []), + new Headers(), + new URLSearchParams(), + new FormData(), + (function* () {})(), + ]; + for (const value of values) { + test(label(value), () => { + if (value && typeof value === "object" && value instanceof Blob) { + require("fs").writeFileSync("/tmp/empty.txt", ""); + } + + expect(value).toBeEmpty(); + }); + } + }); + + describe("not.toBeEmpty()", () => { + const values = [ + " ", + [""], + [undefined], + { "": "" }, + new Set([""]), + new Map([["", ""]]), + new String(" "), + new Array(1), + new Uint8Array(1), + Buffer.from(" "), + ...(isBun ? [Bun.file(__filename)] : []), + new Headers({ + a: "b", + c: "d", + }), + new URL("https://example.com?d=e&f=g").searchParams, + (() => { + var a = new FormData(); + a.append("a", "b"); + a.append("c", "d"); + return a; + })(), + (function* () { + yield "123"; + })(), + ]; + for (const value of values) { + test(label(value), () => { + expect(value).not.toBeEmpty(); + }); + } + }); + + test("toBeNil()", () => { + expect(null).toBeNil(); + expect(undefined).toBeNil(); + expect(false).not.toBeNil(); + expect(0).not.toBeNil(); + expect("").not.toBeNil(); + expect([]).not.toBeNil(); + expect(true).not.toBeNil(); + expect({}).not.toBeNil(); + }); + + test("toBeArray()", () => { + expect([]).toBeArray(); + expect([1, 2, 3, "🫓"]).toBeArray(); + expect(new Array()).toBeArray(); + expect(new Array(1, 2, 3)).toBeArray(); + expect({}).not.toBeArray(); + expect("🫓").not.toBeArray(); + expect(0).not.toBeArray(); + expect(true).not.toBeArray(); + expect(null).not.toBeArray(); + }); + + test("toBeArrayOfSize()", () => { + expect([]).toBeArrayOfSize(0); + expect(new Array()).toBeArrayOfSize(0); + expect([1, 2, 3, "🫓"]).toBeArrayOfSize(4); + expect((new Array() < string) | (number > (1, 2, 3, "🫓"))).toBeArrayOfSize(4); + expect({}).not.toBeArrayOfSize(1); + expect("").not.toBeArrayOfSize(1); + expect(0).not.toBeArrayOfSize(1); + }); + + test("toBeTypeOf()", () => { + expect("Bun! 🫓").toBeTypeOf("string"); + expect(0).toBeTypeOf("number"); + expect(true).toBeTypeOf("boolean"); + expect([]).toBeTypeOf("object"); + expect({}).toBeTypeOf("object"); + expect(null).toBeTypeOf("object"); + expect(undefined).toBeTypeOf("undefined"); + expect(() => {}).toBeTypeOf("function"); + expect(function () {}).toBeTypeOf("function"); + expect(async () => {}).toBeTypeOf("function"); + expect(async function () {}).toBeTypeOf("function"); + expect(function* () {}).toBeTypeOf("function"); + expect(class {}).toBeTypeOf("function"); + expect(new Array()).toBeTypeOf("object"); + expect(BigInt(5)).toBeTypeOf("bigint"); + expect(/(foo|bar)/g).toBeTypeOf("object"); + expect(new RegExp("(foo|bar)", "g")).toBeTypeOf("object"); + expect(new Date()).toBeTypeOf("object"); + + expect("Bun!").not.toBeTypeOf("number"); + expect(0).not.toBeTypeOf("string"); + expect(true).not.toBeTypeOf("number"); + expect([]).not.toBeTypeOf("string"); + expect({}).not.toBeTypeOf("number"); + expect(null).not.toBeTypeOf("string"); + expect(undefined).not.toBeTypeOf("boolean"); + expect(() => {}).not.toBeTypeOf("string"); + expect(function () {}).not.toBeTypeOf("boolean"); + expect(async () => {}).not.toBeTypeOf("object"); + expect(class {}).not.toBeTypeOf("bigint"); + expect(/(foo|bar)/g).not.toBeTypeOf("string"); + expect(new RegExp("(foo|bar)", "g")).not.toBeTypeOf("number"); + expect(new Date()).not.toBeTypeOf("string"); + }); + + test("toBeBoolean()", () => { + expect(true).toBeBoolean(); + expect(false).toBeBoolean(); + expect(0).not.toBeBoolean(); + expect(1).not.toBeBoolean(); + expect("").not.toBeBoolean(); + expect({}).not.toBeBoolean(); + }); + + test("toBeTrue()", () => { + expect(true).toBeTrue(); + expect(false).not.toBeTrue(); + expect(0).not.toBeTrue(); + expect(1).not.toBeTrue(); + expect("").not.toBeTrue(); + expect({}).not.toBeTrue(); + }); + + test("toBeFalse()", () => { + expect(false).toBeFalse(); + expect(true).not.toBeFalse(); + expect(0).not.toBeFalse(); + expect(1).not.toBeFalse(); + expect("").not.toBeFalse(); + expect({}).not.toBeFalse(); + }); + + test("toBeNumber()", () => { + expect(0).toBeNumber(); + expect(1).toBeNumber(); + expect(1.23).toBeNumber(); + expect(Infinity).toBeNumber(); + expect(-Infinity).toBeNumber(); + expect(NaN).toBeNumber(); + expect("").not.toBeNumber(); + expect({}).not.toBeNumber(); + }); + + test("toBeInteger()", () => { + expect(0).toBeInteger(); + expect(1).toBeInteger(); + expect(1.23).not.toBeInteger(); + expect(Infinity).not.toBeInteger(); + expect(-Infinity).not.toBeInteger(); + expect(NaN).not.toBeInteger(); + expect("").not.toBeInteger(); + expect({}).not.toBeInteger(); + }); + + test("toBeFinite()", () => { + expect(0).toBeFinite(); + expect(1).toBeFinite(); + expect(1.23).toBeFinite(); + expect(Infinity).not.toBeFinite(); + expect(-Infinity).not.toBeFinite(); + expect(NaN).not.toBeFinite(); + expect("").not.toBeFinite(); + expect({}).not.toBeFinite(); + }); + + test("toBePositive()", () => { + expect(1).toBePositive(); + expect(1.23).toBePositive(); + expect(Infinity).not.toBePositive(); + expect(0).not.toBePositive(); + expect(-Infinity).not.toBePositive(); + expect(NaN).not.toBePositive(); + expect("").not.toBePositive(); + expect({}).not.toBePositive(); + }); + + test("toBeNegative()", () => { + expect(-1).toBeNegative(); + expect(-1.23).toBeNegative(); + expect(-Infinity).not.toBeNegative(); + expect(0).not.toBeNegative(); + expect(Infinity).not.toBeNegative(); + expect(NaN).not.toBeNegative(); + expect("").not.toBeNegative(); + expect({}).not.toBeNegative(); + }); + + test("toBeWithin()", () => { + expect(0).toBeWithin(0, 1); + expect(3.14).toBeWithin(3, 3.141); + expect(-25).toBeWithin(-100, 0); + expect(0).not.toBeWithin(1, 2); + expect(3.14).not.toBeWithin(3.1, 3.14); + expect(99).not.toBeWithin(99, 99); + expect(100).not.toBeWithin(99, 100); + expect(NaN).not.toBeWithin(0, 1); + // expect("").not.toBeWithin(0, 1); + expect({}).not.toBeWithin(0, 1); + expect(Infinity).not.toBeWithin(-Infinity, Infinity); + }); + + test("toBeSymbol()", () => { + expect(Symbol()).toBeSymbol(); + expect(Symbol("")).toBeSymbol(); + expect(Symbol.iterator).toBeSymbol(); + expect("").not.toBeSymbol(); + expect({}).not.toBeSymbol(); + }); + + test("toBeFunction()", () => { + expect(() => {}).toBeFunction(); + expect(function () {}).toBeFunction(); + expect(async function () {}).toBeFunction(); + expect(async () => {}).toBeFunction(); + expect(function* () {}).toBeFunction(); + expect(async function* () {}).toBeFunction(); + expect("").not.toBeFunction(); + expect({}).not.toBeFunction(); + expect(null).not.toBeFunction(); + }); + + test("toBeDate()", () => { + expect(new Date()).toBeDate(); + expect(new Date(0)).toBeDate(); + expect(new Date("2021-01-01")).toBeDate(); + expect("2021-01-01").not.toBeDate(); + expect({}).not.toBeDate(); + expect(null).not.toBeDate(); + }); + + test.todo("toBeValidDate()", () => { + expect(new Date()).toBeValidDate(); + expect(new Date(-1)).toBeValidDate(); + expect("2021-01-01").not.toBeValidDate(); + expect({}).not.toBeValidDate(); + expect(null).not.toBeValidDate(); + }); + + test("toBeString()", () => { + expect("").toBeString(); + expect("123").toBeString(); + expect(new String()).toBeString(); + expect(new String("123")).toBeString(); + expect(123).not.toBeString(); + expect({}).not.toBeString(); + }); + + test("toInclude()", () => { + expect("123").toInclude("1"); + expect("abc").toInclude("abc"); + expect(" 123 ").toInclude(" "); + expect("").toInclude(""); + expect("bob").not.toInclude("alice"); + }); + + test("toStartWith()", () => { + expect("123").toStartWith("1"); + expect("abc").toStartWith("abc"); + expect(" 123 ").toStartWith(" "); + expect(" ").toStartWith(""); + expect("").toStartWith(""); + expect("bob").not.toStartWith("alice"); + }); + + test("toEndWith()", () => { + expect("123").toEndWith("3"); + expect("abc").toEndWith("abc"); + expect(" 123 ").toEndWith(" "); + expect(" ").toEndWith(""); + expect("").toEndWith(""); + expect("bob").not.toEndWith("alice"); + }); +}); diff --git a/test/js/bun/test/expect.test.ts b/test/js/bun/test/expect.test.ts deleted file mode 100644 index a511174ff..000000000 --- a/test/js/bun/test/expect.test.ts +++ /dev/null @@ -1,442 +0,0 @@ -import { inspect } from "bun"; -import { describe, test, expect } from "bun:test"; - -describe("expect()", () => { - describe("toBeInstanceOf()", () => { - class Animal {} - class Dog extends Animal {} - const tests = [ - { - label: "string", - value: new String(""), - instanceOf: String, - }, - { - label: "number", - value: new Number(1), - instanceOf: Number, - }, - { - label: "object", - value: {}, - instanceOf: Object, - }, - { - label: "function", - value: () => {}, - instanceOf: Function, - }, - { - label: "Class", - value: new Animal(), - instanceOf: Animal, - }, - { - label: "extends Class", - value: new Dog(), - instanceOf: Dog, - }, - { - label: "super Class", - value: new Dog(), - instanceOf: Animal, - }, - ]; - for (const { label, value, instanceOf } of tests) { - test(label, () => expect(value).toBeInstanceOf(instanceOf)); - } - }); - - describe("toMatch()", () => { - const tests = [ - { - label: "reguler expression", - value: "123", - matched: /123/, - }, - { - label: "reguler expression object", - value: "123", - matched: new RegExp("123"), - }, - { - label: "substring", - value: "123", - matched: "12", - }, - { - label: "substring emojis", - value: "👍👎", - matched: "👍", - }, - { - label: "substring UTF-16", - value: "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊 😇 🙂", - matched: "🥲 ☺️ 😊", - }, - ]; - for (const { label, value, matched } of tests) { - test(label, () => expect(value).toMatch(matched)); - } - }); - - describe("toBeCloseTo()", () => { - const passTests = [ - [0, 0], - [0, 0.001], - [1.23, 1.229], - [1.23, 1.226], - [1.23, 1.225], - [1.23, 1.234], - [Infinity, Infinity], - [-Infinity, -Infinity], - [0, 0.1, 0], - [0, 0.0001, 3], - [0, 0.000004, 5], - [2.0000002, 2, 5], - ]; - for (const [actual, expected, precision] of passTests) { - if (precision === undefined) { - test(`actual = ${actual}, expected = ${expected}`, () => { - expect(actual).toBeCloseTo(expected); - }); - } else { - test(`actual = ${actual}, expected = ${expected}, precision = ${precision}`, () => { - expect(actual).toBeCloseTo(expected, precision); - }); - } - } - const failTests = [ - [0, 0.01], - [1, 1.23], - [1.23, 1.2249999], - [Infinity, -Infinity], - [Infinity, 1.23], - [-Infinity, -1.23], - [3.141592e-7, 3e-7, 8], - [56789, 51234, -4], - ]; - for (const [actual, expected, precision] of failTests) { - if (precision === undefined) { - test(`actual = ${actual}, expected != ${expected}`, () => { - expect(actual).not.toBeCloseTo(expected); - }); - } else { - test(`actual = ${actual}, expected != ${expected}, precision = ${precision}`, () => { - expect(actual).not.toBeCloseTo(expected, precision); - }); - } - } - }); - - describe("toBeEmpty()", () => { - const values = [ - "", - [], - {}, - new Set(), - new Map(), - new String(), - new Array(), - new Uint8Array(), - new Object(), - Buffer.from(""), - Bun.file("/tmp/empty.txt"), - new Headers(), - new URLSearchParams(), - new FormData(), - (function* () {})(), - ]; - for (const value of values) { - test(label(value), () => { - if (value && typeof value === "object" && value instanceof Blob) { - require("fs").writeFileSync("/tmp/empty.txt", ""); - } - - expect(value).toBeEmpty(); - }); - } - }); - - describe("not.toBeEmpty()", () => { - const values = [ - " ", - [""], - [undefined], - { "": "" }, - new Set([""]), - new Map([["", ""]]), - new String(" "), - new Array(1), - new Uint8Array(1), - Buffer.from(" "), - Bun.file(import.meta.path), - new Headers({ - a: "b", - c: "d", - }), - new URL("https://example.com?d=e&f=g").searchParams, - (() => { - var a = new FormData(); - a.append("a", "b"); - a.append("c", "d"); - return a; - })(), - (function* () { - yield "123"; - })(), - ]; - for (const value of values) { - test(label(value), () => { - expect(value).not.toBeEmpty(); - }); - } - }); - - test("toBeNil()", () => { - expect(null).toBeNil(); - expect(undefined).toBeNil(); - expect(false).not.toBeNil(); - expect(0).not.toBeNil(); - expect("").not.toBeNil(); - expect([]).not.toBeNil(); - expect(true).not.toBeNil(); - expect({}).not.toBeNil(); - }); - - test("toBeArray()", () => { - expect([]).toBeArray(); - expect([1, 2, 3, "🫓"]).toBeArray(); - expect(new Array()).toBeArray(); - expect(new Array(1, 2, 3)).toBeArray(); - expect({}).not.toBeArray(); - expect("🫓").not.toBeArray(); - expect(0).not.toBeArray(); - expect(true).not.toBeArray(); - expect(null).not.toBeArray(); - }); - - test("toBeArrayOfSize()", () => { - expect([]).toBeArrayOfSize(0); - expect(new Array()).toBeArrayOfSize(0); - expect([1, 2, 3, "🫓"]).toBeArrayOfSize(4); - expect(new Array<string | number>(1, 2, 3, "🫓")).toBeArrayOfSize(4); - expect({}).not.toBeArrayOfSize(1); - expect("").not.toBeArrayOfSize(1); - expect(0).not.toBeArrayOfSize(1); - }); - - test("toBeTypeOf()", () => { - expect("Bun! 🫓").toBeTypeOf("string"); - expect(0).toBeTypeOf("number"); - expect(true).toBeTypeOf("boolean"); - expect([]).toBeTypeOf("object"); - expect({}).toBeTypeOf("object"); - expect(null).toBeTypeOf("object"); - expect(undefined).toBeTypeOf("undefined"); - expect(() => {}).toBeTypeOf("function"); - expect(function () {}).toBeTypeOf("function"); - expect(async () => {}).toBeTypeOf("function"); - expect(async function () {}).toBeTypeOf("function"); - expect(function* () {}).toBeTypeOf("function"); - expect(class {}).toBeTypeOf("function"); - expect(new Array()).toBeTypeOf("object"); - expect(BigInt(5)).toBeTypeOf("bigint"); - expect(/(foo|bar)/g).toBeTypeOf("object"); - expect(new RegExp("(foo|bar)", "g")).toBeTypeOf("object"); - expect(new Date()).toBeTypeOf("object"); - - expect("Bun!").not.toBeTypeOf("number"); - expect(0).not.toBeTypeOf("string"); - expect(true).not.toBeTypeOf("number"); - expect([]).not.toBeTypeOf("string"); - expect({}).not.toBeTypeOf("number"); - expect(null).not.toBeTypeOf("string"); - expect(undefined).not.toBeTypeOf("boolean"); - expect(() => {}).not.toBeTypeOf("string"); - expect(function () {}).not.toBeTypeOf("boolean"); - expect(async () => {}).not.toBeTypeOf("object"); - expect(class {}).not.toBeTypeOf("bigint"); - expect(/(foo|bar)/g).not.toBeTypeOf("string"); - expect(new RegExp("(foo|bar)", "g")).not.toBeTypeOf("number"); - expect(new Date()).not.toBeTypeOf("string"); - }); - - test("toBeBoolean()", () => { - expect(true).toBeBoolean(); - expect(false).toBeBoolean(); - expect(0).not.toBeBoolean(); - expect(1).not.toBeBoolean(); - expect("").not.toBeBoolean(); - expect({}).not.toBeBoolean(); - }); - - test("toBeTrue()", () => { - expect(true).toBeTrue(); - expect(false).not.toBeTrue(); - expect(0).not.toBeTrue(); - expect(1).not.toBeTrue(); - expect("").not.toBeTrue(); - expect({}).not.toBeTrue(); - }); - - test("toBeFalse()", () => { - expect(false).toBeFalse(); - expect(true).not.toBeFalse(); - expect(0).not.toBeFalse(); - expect(1).not.toBeFalse(); - expect("").not.toBeFalse(); - expect({}).not.toBeFalse(); - }); - - test("toBeNumber()", () => { - expect(0).toBeNumber(); - expect(1).toBeNumber(); - expect(1.23).toBeNumber(); - expect(Infinity).toBeNumber(); - expect(-Infinity).toBeNumber(); - expect(NaN).toBeNumber(); - expect("").not.toBeNumber(); - expect({}).not.toBeNumber(); - }); - - test("toBeInteger()", () => { - expect(0).toBeInteger(); - expect(1).toBeInteger(); - expect(1.23).not.toBeInteger(); - expect(Infinity).not.toBeInteger(); - expect(-Infinity).not.toBeInteger(); - expect(NaN).not.toBeInteger(); - expect("").not.toBeInteger(); - expect({}).not.toBeInteger(); - }); - - test("toBeFinite()", () => { - expect(0).toBeFinite(); - expect(1).toBeFinite(); - expect(1.23).toBeFinite(); - expect(Infinity).not.toBeFinite(); - expect(-Infinity).not.toBeFinite(); - expect(NaN).not.toBeFinite(); - expect("").not.toBeFinite(); - expect({}).not.toBeFinite(); - }); - - test("toBePositive()", () => { - expect(1).toBePositive(); - expect(1.23).toBePositive(); - expect(Infinity).not.toBePositive(); - expect(0).not.toBePositive(); - expect(-Infinity).not.toBePositive(); - expect(NaN).not.toBePositive(); - expect("").not.toBePositive(); - expect({}).not.toBePositive(); - }); - - test("toBeNegative()", () => { - expect(-1).toBeNegative(); - expect(-1.23).toBeNegative(); - expect(-Infinity).not.toBeNegative(); - expect(0).not.toBeNegative(); - expect(Infinity).not.toBeNegative(); - expect(NaN).not.toBeNegative(); - expect("").not.toBeNegative(); - expect({}).not.toBeNegative(); - }); - - test("toBeWithin()", () => { - expect(0).toBeWithin(0, 1); - expect(3.14).toBeWithin(3, 3.141); - expect(-25).toBeWithin(-100, 0); - expect(0).not.toBeWithin(1, 2); - expect(3.14).not.toBeWithin(3.1, 3.14); - expect(99).not.toBeWithin(99, 99); - expect(100).not.toBeWithin(99, 100); - expect(NaN).not.toBeWithin(0, 1); - expect("").not.toBeWithin(0, 1); - expect({}).not.toBeWithin(0, 1); - expect(Infinity).not.toBeWithin(-Infinity, Infinity); - }); - - test("toBeSymbol()", () => { - expect(Symbol()).toBeSymbol(); - expect(Symbol("")).toBeSymbol(); - expect(Symbol.iterator).toBeSymbol(); - expect("").not.toBeSymbol(); - expect({}).not.toBeSymbol(); - }); - - test("toBeFunction()", () => { - expect(() => {}).toBeFunction(); - expect(function () {}).toBeFunction(); - expect(async function () {}).toBeFunction(); - expect(async () => {}).toBeFunction(); - expect(function* () {}).toBeFunction(); - expect(async function* () {}).toBeFunction(); - expect("").not.toBeFunction(); - expect({}).not.toBeFunction(); - expect(null).not.toBeFunction(); - }); - - test("toBeDate()", () => { - expect(new Date()).toBeDate(); - expect(new Date(0)).toBeDate(); - expect(new Date("2021-01-01")).toBeDate(); - expect("2021-01-01").not.toBeDate(); - expect({}).not.toBeDate(); - expect(null).not.toBeDate(); - }); - - test.todo("toBeValidDate()", () => { - expect(new Date()).toBeValidDate(); - expect(new Date(-1)).not.toBeValidDate(); - expect("2021-01-01").not.toBeValidDate(); - expect({}).not.toBeValidDate(); - expect(null).not.toBeValidDate(); - }); - - test("toBeString()", () => { - expect("").toBeString(); - expect("123").toBeString(); - expect(new String()).toBeString(); - expect(new String("123")).toBeString(); - expect(123).not.toBeString(); - expect({}).not.toBeString(); - }); - - test("toInclude()", () => { - expect("123").toInclude("1"); - expect("abc").toInclude("abc"); - expect(" 123 ").toInclude(" "); - expect("").toInclude(""); - expect("bob").not.toInclude("alice"); - }); - - test("toStartWith()", () => { - expect("123").toStartWith("1"); - expect("abc").toStartWith("abc"); - expect(" 123 ").toStartWith(" "); - expect(" ").toStartWith(""); - expect("").toStartWith(""); - expect("bob").not.toStartWith("alice"); - }); - - test("toEndWith()", () => { - expect("123").toEndWith("3"); - expect("abc").toEndWith("abc"); - expect(" 123 ").toEndWith(" "); - expect(" ").toEndWith(""); - expect("").toEndWith(""); - expect("bob").not.toEndWith("alice"); - }); -}); - -function label(value: unknown): string { - switch (typeof value) { - case "object": - const string = inspect(value).replace(/\n/g, ""); - return string || '""'; - default: - return JSON.stringify(value); - } -} diff --git a/test/js/bun/test/jest-extended.test.js b/test/js/bun/test/jest-extended.test.js new file mode 100644 index 000000000..b03e0828f --- /dev/null +++ b/test/js/bun/test/jest-extended.test.js @@ -0,0 +1,516 @@ +"use strict"; + +/** This file is meant to be runnable in both Jest and Bun. + * `bunx jest jest-extended.test.js` + */ + +const isBun = typeof Bun !== "undefined"; +if (!isBun) { + const extended = require("jest-extended"); + expect.extend(extended); + test.todo = test; +} + +const inspect = isBun ? Bun.inspect : require("util").inspect; + +// https://jest-extended.jestcommunity.dev/docs/matchers/ +describe("jest-extended", () => { + test.todo("pass()", () => { + expect(typeof expect().pass).toBe("function"); + expect(() => expect().not.pass()).toThrow(); + expect().pass(); + expect().pass("message ignored"); + }); + + test.todo("fail()", () => { + expect(typeof expect().fail).toBe("function"); + expect(() => expect("ignored value").fail("message here")).toThrow("message here"); + expect().not.fail(); + }); + + describe("toBeEmpty()", () => { + const values = [ + "", + [], + {}, + new Set(), + new Map(), + new String(), + new Array(), + new Uint8Array(), + new Object(), + Buffer.from(""), + ...(isBun ? [Bun.file("/tmp/empty.txt")] : []), + new Headers(), + new URLSearchParams(), + new FormData(), + (function* () {})(), + ]; + for (const value of values) { + test(label(value), () => { + if (value && typeof value === "object" && value instanceof Blob) { + require("fs").writeFileSync("/tmp/empty.txt", ""); + } + + expect(value).toBeEmpty(); + }); + } + }); + + describe("not.toBeEmpty()", () => { + const values = [ + " ", + [""], + [undefined], + { "": "" }, + new Set([""]), + new Map([["", ""]]), + new String(" "), + new Array(1), + new Uint8Array(1), + Buffer.from(" "), + ...(isBun ? [Bun.file(__filename)] : []), + new Headers({ + a: "b", + c: "d", + }), + new URL("https://example.com?d=e&f=g").searchParams, + (() => { + var a = new FormData(); + a.append("a", "b"); + a.append("c", "d"); + return a; + })(), + (function* () { + yield "123"; + })(), + ]; + for (const value of values) { + test(label(value), () => { + expect(value).not.toBeEmpty(); + }); + } + }); + + // toBeOneOf('toSatisfy()') + + test("toBeNil()", () => { + expect(null).toBeNil(); + expect(undefined).toBeNil(); + expect(false).not.toBeNil(); + expect(0).not.toBeNil(); + expect("").not.toBeNil(); + expect([]).not.toBeNil(); + expect(true).not.toBeNil(); + expect({}).not.toBeNil(); + }); + + // test('toSatisfy()') + + // Array + + // test('toBeArray()') + // test('toBeArrayOfSize()') + // test('toIncludeAllMembers()') + // test('toIncludeAllPartialMembers()') + // test('toIncludeAnyMembers()') + // test('toIncludeSameMembers()') + // test('toPartiallyContain()') + // test('toSatisfyAll()') + // test('toSatisfyAny()') + // test('toBeInRange()') + + // Boolean + + test("toBeBoolean()", () => { + expect(true).toBeBoolean(); + expect(false).toBeBoolean(); + expect(0).not.toBeBoolean(); + expect(1).not.toBeBoolean(); + expect("").not.toBeBoolean(); + expect({}).not.toBeBoolean(); + }); + + test("toBeTrue()", () => { + expect(true).toBeTrue(); + expect(false).not.toBeTrue(); + expect(0).not.toBeTrue(); + expect(1).not.toBeTrue(); + expect("").not.toBeTrue(); + expect({}).not.toBeTrue(); + }); + + test("toBeFalse()", () => { + expect(false).toBeFalse(); + expect(true).not.toBeFalse(); + expect(0).not.toBeFalse(); + expect(1).not.toBeFalse(); + expect("").not.toBeFalse(); + expect({}).not.toBeFalse(); + }); + + // Date + + test("toBeDate()", () => { + expect(new Date()).toBeDate(); + expect(new Date(0)).toBeDate(); + expect(new Date("2021-01-01")).toBeDate(); + expect("2021-01-01").not.toBeDate(); + expect({}).not.toBeDate(); + expect(null).not.toBeDate(); + }); + + test.todo("toBeValidDate()", () => { + expect(new Date()).toBeValidDate(); + expect(new Date(-1)).toBeValidDate(); + expect("2021-01-01").not.toBeValidDate(); + expect({}).not.toBeValidDate(); + expect(null).not.toBeValidDate(); + }); + + // expect("toBeAfter()") + // expect("toBeBefore()") + // expect("toBeAfterOrEqualTo()") + // expect("toBeBeforeOrEqualTo()") + // expect("toBeBetween()") + + // Function + + test("toBeFunction()", () => { + expect(() => {}).toBeFunction(); + expect(function () {}).toBeFunction(); + expect(async function () {}).toBeFunction(); + expect(async () => {}).toBeFunction(); + expect(function* () {}).toBeFunction(); + expect(async function* () {}).toBeFunction(); + expect("").not.toBeFunction(); + expect({}).not.toBeFunction(); + expect(null).not.toBeFunction(); + }); + + // expect('toThrowWithMessage()') + + // Mock + + // Number + test("toBeNumber()", () => { + expect(0).toBeNumber(); + expect(1).toBeNumber(); + expect(1.23).toBeNumber(); + expect(Infinity).toBeNumber(); + expect(-Infinity).toBeNumber(); + expect(NaN).toBeNumber(); + expect("").not.toBeNumber(); + expect({}).not.toBeNumber(); + }); + + test("toBeFinite()", () => { + expect(0).toBeFinite(); + expect(1).toBeFinite(); + expect(1.23).toBeFinite(); + expect(Infinity).not.toBeFinite(); + expect(-Infinity).not.toBeFinite(); + expect(NaN).not.toBeFinite(); + expect("").not.toBeFinite(); + expect({}).not.toBeFinite(); + }); + + test("toBePositive()", () => { + expect(1).toBePositive(); + expect(1.23).toBePositive(); + expect(Infinity).not.toBePositive(); + expect(0).not.toBePositive(); + expect(-Infinity).not.toBePositive(); + expect(NaN).not.toBePositive(); + expect("").not.toBePositive(); + expect({}).not.toBePositive(); + }); + + test("toBeNegative()", () => { + expect(-1).toBeNegative(); + expect(-1.23).toBeNegative(); + expect(-Infinity).not.toBeNegative(); + expect(0).not.toBeNegative(); + expect(Infinity).not.toBeNegative(); + expect(NaN).not.toBeNegative(); + expect("").not.toBeNegative(); + expect({}).not.toBeNegative(); + }); + + test("toBeWithin()", () => { + expect(0).toBeWithin(0, 1); + expect(3.14).toBeWithin(3, 3.141); + expect(-25).toBeWithin(-100, 0); + expect(0).not.toBeWithin(1, 2); + expect(3.14).not.toBeWithin(3.1, 3.14); + expect(99).not.toBeWithin(99, 99); + expect(100).not.toBeWithin(99, 100); + expect(NaN).not.toBeWithin(0, 1); + // expect("").not.toBeWithin(0, 1); + expect({}).not.toBeWithin(0, 1); + expect(Infinity).not.toBeWithin(-Infinity, Infinity); + }); + + test("toBeEven()", () => { + expect(1).not.toBeEven(); + expect(2).toBeEven(); + expect(3).not.toBeEven(); + expect(3.1).not.toBeEven(); + expect(2.1).not.toBeEven(); + expect(4).toBeEven(); + expect(5).not.toBeEven(); + expect(6).toBeEven(); + expect(0).toBeEven(); + expect(-8).toBeEven(); + expect(-0).toBeEven(); + expect(NaN).not.toBeEven(); + expect([]).not.toBeEven(); + expect([1, 2]).not.toBeEven(); + expect({}).not.toBeEven(); + expect(() => {}).not.toBeEven(); + expect("").not.toBeEven(); + expect("string").not.toBeEven(); + expect(undefined).not.toBeEven(); + expect(Math.floor(Date.now() / 1000) * 2).toBeEven(); // Slight fuzz by using timestamp times 2 + expect(Math.floor(Date.now() / 1000) * 4 - 1).not.toBeEven(); + expect(4.0e1).toBeEven(); + expect(6.2e1).toBeEven(); + expect(6.3e1).not.toBeEven(); + expect(6.33e1).not.toBeEven(); + expect(3.3e-1).not.toBeEven(); //throw + expect(0.3).not.toBeEven(); //throw + expect(0.4).not.toBeEven(); + expect(1).not.toBeEven(); + expect(0).toBeEven(); + expect(2.0).toBeEven(); + expect(NaN).not.toBeEven(); + expect(2n).toBeEven(); // BigInt at this time not supported in jest-extended + expect(3n).not.toBeEven(); + expect(9007199254740990).toBeEven(); // manual typical max safe -1 // not int? + if (isBun) expect(9007199254740990n).toBeEven(); // manual typical max safe -1 as bigint + expect(Number.MAX_SAFE_INTEGER - 1).toBeEven(); // not int? + expect(Number.MAX_SAFE_INTEGER).not.toBeEven(); + if (isBun) { + expect(BigInt(Number.MAX_SAFE_INTEGER) - 1n).toBeEven(); + expect(BigInt(Number.MIN_SAFE_INTEGER) + 1n).toBeEven(); + } + expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeEven(); + expect(BigInt(Number.MAX_VALUE - 1)).toBeEven(); + expect(Number.MIN_SAFE_INTEGER + 1).toBeEven(); // not int? + expect(Number.MIN_SAFE_INTEGER).not.toBeEven(); + expect(BigInt(Number.MIN_SAFE_INTEGER)).not.toBeEven(); + expect(4 / Number.NEGATIVE_INFINITY).toBeEven(); // as in IEEE-754: + / -inf => neg zero + expect(5 / Number.NEGATIVE_INFINITY).toBeEven(); + expect(-7 / Number.NEGATIVE_INFINITY).toBeEven(); // as in IEEE-754: - / -inf => zero + expect(-8 / Number.NEGATIVE_INFINITY).toBeEven(); + expect(new WebAssembly.Global({ value: "i32", mutable: false }, 4).value).toBeEven(); + expect(new WebAssembly.Global({ value: "i32", mutable: false }, 3).value).not.toBeEven(); + expect(new WebAssembly.Global({ value: "i32", mutable: true }, 2).value).toBeEven(); + expect(new WebAssembly.Global({ value: "i32", mutable: true }, 1).value).not.toBeEven(); + if (isBun) { + expect(new WebAssembly.Global({ value: "i64", mutable: true }, -9223372036854775808n).value).toBeEven(); + expect(new WebAssembly.Global({ value: "i64", mutable: false }, -9223372036854775808n).value).toBeEven(); + expect(new WebAssembly.Global({ value: "i64", mutable: true }, 9223372036854775807n).value).not.toBeEven(); + expect(new WebAssembly.Global({ value: "i64", mutable: false }, 9223372036854775807n).value).not.toBeEven(); + } + expect(new WebAssembly.Global({ value: "f32", mutable: true }, 42.0).value).toBeEven(); + expect(new WebAssembly.Global({ value: "f32", mutable: false }, 42.0).value).toBeEven(); + expect(new WebAssembly.Global({ value: "f64", mutable: true }, 42.0).value).toBeEven(); + expect(new WebAssembly.Global({ value: "f64", mutable: false }, 42.0).value).toBeEven(); + expect(new WebAssembly.Global({ value: "f32", mutable: true }, 43.0).value).not.toBeEven(); + expect(new WebAssembly.Global({ value: "f32", mutable: false }, 43.0).value).not.toBeEven(); + expect(new WebAssembly.Global({ value: "f64", mutable: true }, 43.0).value).not.toBeEven(); + expect(new WebAssembly.Global({ value: "f64", mutable: false }, 43.0).value).not.toBeEven(); + expect(new WebAssembly.Global({ value: "f32", mutable: true }, 4.3).value).not.toBeEven(); + expect(new WebAssembly.Global({ value: "f32", mutable: false }, 4.3).value).not.toBeEven(); + expect(new WebAssembly.Global({ value: "f64", mutable: true }, 4.3).value).not.toBeEven(); + expect(new WebAssembly.Global({ value: "f64", mutable: false }, 4.3).value).not.toBeEven(); + // did not seem to support SIMD v128 type yet (which is not in W3C specs for JS but is a valid global type) + // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:false}, -170141183460469231731687303715884105728n).value).toBeEven(); + // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:true}, -170141183460469231731687303715884105728n).value).toBeEven(); + // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:true}, 170141183460469231731687303715884105727n).value).not.toBeEven(); + // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:false}, 170141183460469231731687303715884105727n).value).not.toBeEven(); + // FUTURE: with uintv128: expect(new WebAssembly.Global({value:'v128', mutable:false}, 340282366920938463463374607431768211456n).value).toThrow(); + }); + + test("toBeOdd()", () => { + expect(1).toBeOdd(); + expect(2).not.toBeOdd(); + expect(3).toBeOdd(); + expect(3.1).not.toBeOdd(); + expect(2.1).not.toBeOdd(); + expect(4).not.toBeOdd(); + expect(5).toBeOdd(); + expect(6).not.toBeOdd(); + expect(0).not.toBeOdd(); + expect(-8).not.toBeOdd(); + expect(-0).not.toBeOdd(); + expect(NaN).not.toBeOdd(); + expect([]).not.toBeOdd(); + // SHOULD FAIL: expect([]).toBeOdd(); + expect([1, 2]).not.toBeOdd(); + expect({}).not.toBeOdd(); + expect(() => {}).not.toBeOdd(); + expect("").not.toBeOdd(); + expect("string").not.toBeOdd(); + expect(undefined).not.toBeOdd(); + expect(Math.floor(Date.now() / 1000) * 2 - 1).toBeOdd(); // Slight fuzz by using timestamp times 2 + expect(Math.floor(Date.now() / 1000) * 4 - 1).toBeOdd(); + expect(4.0e1).not.toBeOdd(); + expect(6.2e1).not.toBeOdd(); + expect(6.3e1).toBeOdd(); + expect(6.33e1).not.toBeOdd(); + expect(3.2e-3).not.toBeOdd(); + expect(0.3).not.toBeOdd(); + expect(0.4).not.toBeOdd(); + expect(1).toBeOdd(); + expect(0).not.toBeOdd(); + expect(2.0).not.toBeOdd(); + expect(NaN).not.toBeOdd(); + if (isBun) expect(2n).not.toBeOdd(); // BigInt at this time not supported in jest-extended + if (isBun) expect(3n).toBeOdd(); + expect(9007199254740990).not.toBeOdd(); // manual typical max safe -1 + expect(9007199254740991).toBeOdd(); + if (isBun) expect(9007199254740990n).not.toBeOdd(); // manual typical max safe -1 as bigint + if (isBun) expect(9007199254740991n).toBeOdd(); + expect(Number.MAX_SAFE_INTEGER - 1).not.toBeOdd(); + expect(Number.MAX_SAFE_INTEGER).toBeOdd(); + expect(BigInt(Number.MAX_SAFE_INTEGER) - 1n).not.toBeOdd(); + expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeOdd(); + expect(Number.MIN_SAFE_INTEGER + 1).not.toBeOdd(); + expect(Number.MIN_SAFE_INTEGER).toBeOdd(); + expect(BigInt(Number.MIN_SAFE_INTEGER) + 1n).not.toBeOdd(); + expect(BigInt(Number.MIN_SAFE_INTEGER)).toBeOdd(); + expect(4 / Number.NEGATIVE_INFINITY).not.toBeOdd(); // in IEEE-754: + / -inf => neg zero + expect(5 / Number.NEGATIVE_INFINITY).not.toBeOdd(); + expect(-7 / Number.NEGATIVE_INFINITY).not.toBeOdd(); // in IEEE-754: - / -inf => zero + expect(-8 / Number.NEGATIVE_INFINITY).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "i32", mutable: false }, 4).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "i32", mutable: false }, 3).value).toBeOdd(); + expect(new WebAssembly.Global({ value: "i32", mutable: true }, 2).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "i32", mutable: true }, 1).value).toBeOdd(); + if (isBun) { + expect(new WebAssembly.Global({ value: "i64", mutable: true }, -9223372036854775808n).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "i64", mutable: false }, -9223372036854775808n).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "i64", mutable: true }, 9223372036854775807n).value).toBeOdd(); + expect(new WebAssembly.Global({ value: "i64", mutable: false }, 9223372036854775807n).value).toBeOdd(); + } + expect(new WebAssembly.Global({ value: "f32", mutable: true }, 42.0).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "f32", mutable: false }, 42.0).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "f64", mutable: true }, 42.0).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "f64", mutable: false }, 42.0).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "f32", mutable: true }, 43.0).value).toBeOdd(); + expect(new WebAssembly.Global({ value: "f32", mutable: false }, 43.0).value).toBeOdd(); + expect(new WebAssembly.Global({ value: "f64", mutable: true }, 43.0).value).toBeOdd(); + expect(new WebAssembly.Global({ value: "f64", mutable: false }, 43.0).value).toBeOdd(); + expect(new WebAssembly.Global({ value: "f32", mutable: true }, 4.3).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "f32", mutable: false }, 4.3).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "f64", mutable: true }, 4.3).value).not.toBeOdd(); + expect(new WebAssembly.Global({ value: "f64", mutable: false }, 4.3).value).not.toBeOdd(); + // did not seem to support SIMD v128 type yet + // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:false}, 42).value).not.toBeOdd(); + // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:true}, 42).value).not.toBeOdd(); + // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:true}, 43).value).toBeOdd(); + }); + + test("toBeInteger()", () => { + expect(0).toBeInteger(); + expect(1).toBeInteger(); + expect(1.23).not.toBeInteger(); + expect(Infinity).not.toBeInteger(); + expect(-Infinity).not.toBeInteger(); + expect(NaN).not.toBeInteger(); + expect("").not.toBeInteger(); + expect({}).not.toBeInteger(); + }); + + // Object + + // test("toBeObject()") + // test("toBeEmptyObject()") + // test("toContainKey()") + // test("toContainKeys()") + // test("toContainAllKeys()") + // test("toContainAnyKeys()") + // test("toContainValue()") + // test("toContainValues()") + // test("toContainAllValues()") + // test("toContainAnyValues()") + // test("toContainEntry()") + // test("toContainEntries()") + // test("toContainAllEntries()") + // test("toContainAnyEntries()") + // test("toBeExtensible()") + // test("toBeFrozen()") + // test("toBeSealed()") + + // String + + test("toBeString()", () => { + expect("").toBeString(); + expect("123").toBeString(); + expect(new String()).toBeString(); + expect(new String("123")).toBeString(); + expect(123).not.toBeString(); + expect({}).not.toBeString(); + }); + // test("toBeHexadecimal()") + // test("toBeDateString()") + // test("toEqualCaseInsensitive()") + + test("toStartWith()", () => { + expect("123").toStartWith("1"); + expect("abc").toStartWith("abc"); + expect(" 123 ").toStartWith(" "); + expect(" ").toStartWith(""); + expect("").toStartWith(""); + expect("bob").not.toStartWith("alice"); + }); + + test("toEndWith()", () => { + expect("123").toEndWith("3"); + expect("abc").toEndWith("abc"); + expect(" 123 ").toEndWith(" "); + expect(" ").toEndWith(""); + expect("").toEndWith(""); + expect("bob").not.toEndWith("alice"); + }); + + test("toInclude()", () => { + expect("123").toInclude("1"); + expect("abc").toInclude("abc"); + expect(" 123 ").toInclude(" "); + expect("").toInclude(""); + expect("bob").not.toInclude("alice"); + }); + + // test("toIncludeRepeated()") + // test("toIncludeMultiple()") + // test("toEqualIgnoringWhitespace()") + + // Symbol + + test("toBeSymbol()", () => { + expect(Symbol()).toBeSymbol(); + expect(Symbol("")).toBeSymbol(); + expect(Symbol.iterator).toBeSymbol(); + expect("").not.toBeSymbol(); + expect({}).not.toBeSymbol(); + }); +}); + +/** + * @param {string} value + * @returns {string} + */ +function label(value) { + switch (typeof value) { + case "object": + const string = inspect(value).replace(/\n/g, ""); + return string || '""'; + case "undefined": + return "undefined"; + default: + return JSON.stringify(value); + } +} diff --git a/test/js/bun/test/jest.d.ts b/test/js/bun/test/jest.d.ts new file mode 100644 index 000000000..fccb87b40 --- /dev/null +++ b/test/js/bun/test/jest.d.ts @@ -0,0 +1,5 @@ +declare var jest: typeof import("bun:test").jest; +declare var describe: typeof import("bun:test").describe; +declare var test: typeof import("bun:test").test; +declare var expect: typeof import("bun:test").expect; +declare var it: typeof import("bun:test").it; diff --git a/test/js/bun/test/mock-fn.test.js b/test/js/bun/test/mock-fn.test.js new file mode 100644 index 000000000..eac981fd1 --- /dev/null +++ b/test/js/bun/test/mock-fn.test.js @@ -0,0 +1,599 @@ +/** + * This file is meant to be runnable in both Jest and Bun. + * `bunx jest mock-fn.test.js` + */ +var { isBun, test, describe, expect, jest, vi, mock, bunTest, spyOn } = require("./test-interop.js")(); + +async function expectResolves(promise) { + expect(promise).toBeInstanceOf(Promise); + return await promise; +} + +async function expectRejects(promise) { + expect(promise).toBeInstanceOf(Promise); + var value; + try { + value = await promise; + } catch (e) { + return e; + } + throw new Error("Expected promise to reject, but it resolved to " + value); +} + +describe("mock()", () => { + if (isBun) { + test("exists as jest.fn, bunTest.mock, and vi.fn", () => { + expect(jest.fn).toBe(mock); + expect(jest.fn).toBe(vi.fn); + }); + } + test("are callable", () => { + const fn = jest.fn(() => 42); + expect(fn()).toBe(42); + expect(fn).toHaveBeenCalled(); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn.mock.calls).toHaveLength(1); + expect(fn.mock.calls[0]).toBeEmpty(); + expect(fn()).toBe(42); + expect(fn).toHaveBeenCalledTimes(2); + expect(fn.mock.calls).toHaveLength(2); + expect(fn.mock.calls[1]).toBeEmpty(); + }); + test("passes this value", () => { + const fn = jest.fn(function hey() { + "use strict"; + return this; + }); + const obj = { fn }; + expect(obj.fn()).toBe(obj); + }); + if (isBun) { + test("jest.fn(10) return value shorthand", () => { + expect(jest.fn(10)()).toBe(10); + expect(jest.fn(null)()).toBe(null); + }); + } + test("blank function still logs a return", () => { + const fn = jest.fn(); + expect(fn()).toBe(undefined); + expect(fn.mock.results[0]).toEqual({ + type: "return", + value: undefined, + }); + }); + test(".call passes this value", () => { + const fn = jest.fn(function () { + return this; + }); + expect(Number(fn.call(123))).toBe(123); + }); + test(".call works", () => { + const fn = jest.fn(function hey() { + return this; + }); + expect(Number(fn.call(123))).toBe(123); + expect(fn).toHaveBeenCalled(); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn.mock.calls).toHaveLength(1); + expect(fn.mock.calls[0]).toBeEmpty(); + expect(Number(fn.call(234))).toBe(234); + expect(fn).toHaveBeenCalledTimes(2); + expect(fn.mock.calls).toHaveLength(2); + expect(fn.mock.calls[1]).toBeEmpty(); + }); + test(".apply works", function () { + const fn = jest.fn(function hey() { + return this; + }); + expect(Number(fn.apply(123))).toBe(123); + expect(fn).toHaveBeenCalled(); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn.mock.calls).toHaveLength(1); + expect(fn.mock.calls[0]).toBeEmpty(); + expect(Number(fn.apply(234))).toBe(234); + expect(fn).toHaveBeenCalledTimes(2); + expect(fn.mock.calls).toHaveLength(2); + expect(fn.mock.calls[1]).toBeEmpty(); + }); + test(".bind works", () => { + const fn = jest.fn(function hey() { + return this; + }); + expect(Number(fn.bind(123)())).toBe(123); + expect(fn).toHaveBeenCalled(); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn.mock.calls).toHaveLength(1); + expect(fn.mock.calls[0]).toBeEmpty(); + expect(Number(fn.bind(234)())).toBe(234); + expect(fn).toHaveBeenCalledTimes(2); + expect(fn.mock.calls).toHaveLength(2); + expect(fn.mock.calls[1]).toBeEmpty(); + }); + test(".name works", () => { + const fn = jest.fn(function hey() { + return this; + }); + + if (isBun) { + expect(fn.name).toBe("hey"); + } + expect(typeof fn.name).toBe("string"); + }); + test(".name without implementation", () => { + const fn = jest.fn(); + expect(fn.name).toBe("mockConstructor"); + }); + test(".name throwing doesnt segfault", () => { + function baddie() { + return this; + } + Object.defineProperty(baddie, "name", { + get() { + throw new Error("foo"); + }, + }); + const fn = jest.fn(baddie); + expect(typeof fn.name).toBe("string"); + }); + test(".length works", () => { + const fn = jest.fn(function hey(a, b, c) { + return this; + }); + + expect(fn.length).toBe(3); + }); + test("include arguments", () => { + const fn = jest.fn(f => f); + expect(fn(43)).toBe(43); + expect(fn.mock.results[0]).toEqual({ + type: "return", + value: 43, + }); + expect(fn.mock.calls[0]).toEqual([43]); + }); + test("works when throwing", () => { + const instance = new Error("foo"); + const fn = jest.fn(f => { + throw instance; + }); + expect(() => fn(43)).toThrow("foo"); + expect(fn.mock.results[0]).toEqual({ + type: "throw", + value: instance, + }); + expect(fn.mock.calls[0]).toEqual([43]); + }); + test("mockReset works", () => { + const instance = new Error("foo"); + const fn = jest.fn(f => { + throw instance; + }); + expect(() => fn(43)).toThrow("foo"); + expect(fn.mock.results[0]).toEqual({ + type: "throw", + value: instance, + }); + expect(fn.mock.calls[0]).toEqual([43]); + fn.mockReset(); + expect(fn.mock.calls).toBeEmpty(); + expect(fn.mock.results).toBeEmpty(); + expect(fn.mock.instances).toBeEmpty(); + expect(fn).not.toHaveBeenCalled(); + expect(() => expect(fn).toHaveBeenCalled()).toThrow(); + expect(fn(43)).toBe(undefined); + expect(fn.mock.results).toEqual([ + { + type: "return", + value: undefined, + }, + ]); + expect(fn.mock.calls).toEqual([[43]]); + }); + test("mockClear works", () => { + const instance = new Error("foo"); + const fn = jest.fn(f => { + throw instance; + }); + expect(() => fn(43)).toThrow("foo"); + expect(fn.mock.results[0]).toEqual({ + type: "throw", + value: instance, + }); + expect(fn.mock.calls[0]).toEqual([43]); + fn.mockClear(); + expect(fn.mock.calls).toBeEmpty(); + expect(fn.mock.results).toBeEmpty(); + expect(fn.mock.instances).toBeEmpty(); + expect(fn).not.toHaveBeenCalled(); + expect(() => fn(43)).toThrow("foo"); + expect(fn.mock.results[0]).toEqual({ + type: "throw", + value: instance, + }); + expect(fn.mock.calls[0]).toEqual([43]); + }); + // this is an implementation detail i don't think we *need* to support + test("mockClear doesnt update existing object", () => { + const instance = new Error("foo"); + const fn = jest.fn(f => { + throw instance; + }); + expect(() => fn(43)).toThrow("foo"); + expect(fn.mock.results[0]).toEqual({ + type: "throw", + value: instance, + }); + expect(fn.mock.calls[0]).toEqual([43]); + const stolen = fn.mock; + fn.mockClear(); + expect(stolen).not.toBe(fn.mock); + expect(fn.mock.calls).toBeEmpty(); + expect(stolen.calls).not.toBeEmpty(); + expect(fn.mock.results).toBeEmpty(); + expect(stolen.results).not.toBeEmpty(); + expect(fn.mock.instances).toBeEmpty(); + expect(stolen.instances).not.toBe(fn.mock.instances); + expect(fn).not.toHaveBeenCalled(); + expect(() => fn(43)).toThrow("foo"); + expect(fn.mock.results[0]).toEqual({ + type: "throw", + value: instance, + }); + expect(fn.mock.calls[0]).toEqual([43]); + }); + test("multiple calls work", () => { + const fn = jest.fn(f => f); + expect(fn(43)).toBe(43); + expect(fn(44)).toBe(44); + expect(fn.mock.calls[0]).toEqual([43]); + expect(fn.mock.results[0]).toEqual({ + type: "return", + value: 43, + }); + expect(fn.mock.calls[1]).toEqual([44]); + expect(fn.mock.results[1]).toEqual({ + type: "return", + value: 44, + }); + expect(fn.mock.contexts).toEqual([undefined, undefined]); + }); + test("this arg", () => { + const fn = jest.fn(function (add) { + return this.foo + add; + }); + const obj = { foo: 42, fn }; + expect(obj.fn(2)).toBe(44); + expect(fn.mock.calls[0]).toEqual([2]); + expect(fn.mock.results[0]).toEqual({ + type: "return", + value: 44, + }); + }); + test("looks like a function", () => { + const fn = jest.fn(function nameHere(a, b, c) { + return [a, b, c]; + }); + expect(typeof fn).toBe("function"); + expect(typeof fn.name).toBe("string"); + expect(fn.name.length).toBeGreaterThan(0); + expect(fn.toString).not.toBe(undefined); + expect(fn.bind).not.toBe(undefined); + expect(fn.call).not.toBe(undefined); + expect(fn.apply).not.toBe(undefined); + expect(typeof fn.length).toBe("number"); + }); + test("apply/call/bind", () => { + const fn = jest.fn(function (add) { + "use strict"; + return this.foo + add; + }); + const obj = { foo: 42, fn }; + expect(obj.fn(2)).toBe(44); + const this2 = { foo: 43 }; + expect(fn.call(this2, 2)).toBe(45); + const this3 = { foo: 44 }; + expect(fn.apply(this3, [2])).toBe(46); + const this4 = { foo: 45 }; + expect(fn.bind(this4)(3)).toBe(48); + const this5 = { foo: 45 }; + expect(fn.bind(this5, 2)()).toBe(47); + expect(fn.mock.calls[0]).toEqual([2]); + expect(fn.mock.calls[1]).toEqual([2]); + expect(fn.mock.calls[2]).toEqual([2]); + expect(fn.mock.calls[3]).toEqual([3]); + expect(fn.mock.calls[4]).toEqual([2]); + expect(fn.mock.results[0]).toEqual({ + type: "return", + value: 44, + }); + expect(fn.mock.results[1]).toEqual({ + type: "return", + value: 45, + }); + expect(fn.mock.results[2]).toEqual({ + type: "return", + value: 46, + }); + expect(fn.mock.results[3]).toEqual({ + type: "return", + value: 48, + }); + expect(fn.mock.results[4]).toEqual({ + type: "return", + value: 47, + }); + }); + test("mockReturnValueOnce with no implementation", () => { + const fn = jest.fn(); + fn.mockReturnValueOnce(10); + expect(fn()).toBe(10); + expect(fn()).toBe(undefined); + fn.mockReturnValueOnce("x").mockReturnValue(true); + expect(fn()).toBe("x"); + expect(fn()).toBe(true); + expect(fn()).toBe(true); + fn.mockReturnValue("y"); + expect(fn()).toBe("y"); + }); + test("mockReturnValue then mockReturnValueOnce", () => { + const fn = jest.fn(); + fn.mockReturnValue(true).mockReturnValueOnce(10).mockReturnValueOnce("x"); + expect(fn()).toBe(10); + expect(fn()).toBe("x"); + expect(fn()).toBe(true); + expect(fn()).toBe(true); + }); + test("mockReturnValue then fallback to original", () => { + const fn = jest.fn(() => "fallback"); + fn.mockReturnValueOnce(true).mockReturnValueOnce(10).mockReturnValueOnce("x"); + expect(fn()).toBe(true); + expect(fn()).toBe(10); + expect(fn()).toBe("x"); + expect(fn()).toBe("fallback"); + }); + test("mockImplementation", () => { + const fn = jest.fn(); + fn.mockImplementation(a => !a); + expect(fn()).toBe(true); + expect(fn()).toBe(true); + fn.mockImplementation(a => a + 2); + expect(fn(8)).toBe(10); + }); + test("mockImplementationOnce", () => { + const fn = jest.fn(); + fn.mockImplementationOnce(a => ["a", a]); + fn.mockImplementationOnce(a => ["b", a]); + fn.mockImplementationOnce(a => ["c", a]); + fn.mockImplementation(a => ["d", a]); + expect(fn(1)).toEqual(["a", 1]); + expect(fn(2)).toEqual(["b", 2]); + expect(fn(3)).toEqual(["c", 3]); + expect(fn(4)).toEqual(["d", 4]); + expect(fn(5)).toEqual(["d", 5]); + fn.mockImplementationOnce(a => ["e", a]); + expect(fn(5)).toEqual(["e", 5]); + expect(fn(6)).toEqual(["d", 6]); + fn.mockImplementationOnce(a => ["f", a]); + fn.mockImplementation(a => ["g", a]); + expect(fn(7)).toEqual(["f", 7]); + expect(fn(8)).toEqual(["g", 8]); + expect(fn(9)).toEqual(["g", 9]); + }); + test("mockImplementation falls back", () => { + const fn = jest.fn(() => "fallback"); + fn.mockImplementationOnce(a => ["a", a]); + fn.mockImplementationOnce(a => ["b", a]); + expect(fn(1)).toEqual(["a", 1]); + expect(fn(2)).toEqual(["b", 2]); + expect(fn(3)).toEqual("fallback"); + }); + test("mixing mockImplementation and mockReturnValue", () => { + const fn = jest.fn(() => "fallback"); + fn.mockReturnValueOnce(true).mockImplementationOnce(() => 12); + expect(fn()).toBe(true); + expect(fn()).toBe(12); + expect(fn()).toBe("fallback"); + fn.mockImplementation(() => 13); + expect(fn()).toBe(13); + fn.mockReturnValue("FAIL").mockImplementation(() => 14); + expect(fn()).toBe(14); + fn.mockReturnValueOnce(15).mockImplementation(() => 16); + expect(fn()).toBe(15); + expect(fn()).toBe(16); + }); + // these promise based tests were written before .resolves/.rejects were added to bun:test + test("mockResolvedValue", async () => { + const fn = jest.fn(); + fn.mockResolvedValue(42); + expect(await expectResolves(fn())).toBe(42); + fn.mockResolvedValueOnce(43); + fn.mockResolvedValueOnce(44); + expect(await expectResolves(fn())).toBe(43); + expect(await expectResolves(fn())).toBe(44); + expect(await expectResolves(fn())).toBe(42); + }); + test("mockRejectedValue", async () => { + const fn = jest.fn(); + fn.mockRejectedValue(42); + expect(await expectRejects(fn())).toBe(42); + expect(await expectRejects(fn())).toBe(42); + fn.mockRejectedValueOnce(43); + fn.mockRejectedValueOnce(44); + expect(await expectRejects(fn())).toBe(43); + expect(await expectRejects(fn())).toBe(44); + expect(await expectRejects(fn())).toBe(42); + }); + test("withImplementation (sync)", () => { + const fn = jest.fn(() => "1"); + expect(fn()).toBe("1"); + const result = fn.withImplementation( + () => "2", + function () { + expect(fn()).toBe("2"); + expect(fn()).toBe("2"); + return "3"; + }, + ); + expect(result).toBe(undefined); + expect(fn()).toBe("1"); + }); + test("withImplementation (async)", async () => { + const fn = jest.fn(() => "1"); + expect(fn()).toBe("1"); + const result = fn.withImplementation( + () => "2", + async function () { + expect(fn()).toBe("2"); + expect(fn()).toBe("2"); + await new Promise(resolve => setTimeout(resolve, 10)); + expect(fn()).toBe("2"); + expect(fn()).toBe("2"); + await new Promise(resolve => setTimeout(resolve, 10)); + expect(fn()).toBe("2"); + expect(fn()).toBe("2"); + return "3"; + }, + ); + await expectResolves(result); + expect(fn()).toBe("1"); + await new Promise(resolve => setTimeout(resolve, 10)); + expect(fn()).toBe("1"); + }); + test("lastCall works", () => { + const fn = jest.fn(v => -v); + expect(fn.mock.lastCall).toBeUndefined(); + expect(fn(1)).toBe(-1); + expect(fn.mock.lastCall).toEqual([1]); + expect(fn(-2)).toBe(2); + expect(fn.mock.lastCall).toEqual([-2]); + }); + test("invocationCallOrder works", () => { + const fn1 = jest.fn(v => -v); + const fn2 = jest.fn(v => -v); + fn1(1); + fn2(1); + fn2(1); + fn1(1); + const first = fn1.mock.invocationCallOrder[0]; + expect(first).toBeGreaterThan(0); + expect(fn1.mock.invocationCallOrder).toEqual([first, first + 3]); + expect(fn2.mock.invocationCallOrder).toEqual([first + 1, first + 2]); + }); +}); + +describe("spyOn", () => { + test("works on functions", () => { + var obj = { + original() { + return 42; + }, + }; + const fn = spyOn(obj, "original"); + expect(fn).toBe(obj.original); + expect(fn).not.toHaveBeenCalled(); + expect(() => expect(fn).toHaveBeenCalled()).toThrow(); + expect(obj.original()).toBe(42); + expect(fn).toHaveBeenCalled(); + expect(fn).toHaveBeenCalledTimes(1); + expect(() => expect(fn).not.toHaveBeenCalled()).toThrow(); + expect(() => expect(fn).not.toHaveBeenCalledTimes(1)).toThrow(); + expect(fn.mock.calls).toHaveLength(1); + expect(fn.mock.calls[0]).toBeEmpty(); + jest.restoreAllMocks(); + expect(() => expect(obj.original).toHaveBeenCalled()).toThrow(); + expect(fn).not.toHaveBeenCalled(); + expect(obj.original()).toBe(42); + expect(fn).not.toHaveBeenCalled(); + }); + + test("override impl after doesnt break restore", () => { + var obj = { + original() { + return 42; + }, + }; + const fn = spyOn(obj, "original"); + fn.mockImplementation(() => 43); + expect(fn).toBe(obj.original); + expect(obj.original()).toBe(43); + expect(fn).toHaveBeenCalled(); + fn.mockRestore(); + expect(obj.original()).toBe(42); + expect(fn).not.toHaveBeenCalled(); + }); + + test("mockRestore works", () => { + var obj = { + original() { + return 42; + }, + }; + const fn = spyOn(obj, "original"); + expect(fn).toBe(obj.original); + expect(fn).not.toHaveBeenCalled(); + expect(() => expect(fn).toHaveBeenCalled()).toThrow(); + expect(obj.original()).toBe(42); + expect(fn).toHaveBeenCalled(); + expect(fn).toHaveBeenCalledTimes(1); + expect(() => expect(fn).not.toHaveBeenCalled()).toThrow(); + expect(() => expect(fn).not.toHaveBeenCalledTimes(1)).toThrow(); + expect(fn.mock.calls).toHaveLength(1); + expect(fn.mock.calls[0]).toBeEmpty(); + fn.mockRestore(); + expect(() => expect(obj.original).toHaveBeenCalled()).toThrow(); + expect(fn).not.toHaveBeenCalled(); + expect(obj.original()).toBe(42); + expect(fn).not.toHaveBeenCalled(); + }); + + if (isBun) { + // Jest doesn't allow spying on properties + test("spyOn works on object", () => { + var obj = { original: 42 }; + obj.original = 42; + const fn = spyOn(obj, "original"); + expect(fn).not.toHaveBeenCalled(); + expect(obj.original).toBe(42); + expect(fn).toHaveBeenCalled(); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn.mock.calls).toHaveLength(1); + expect(fn.mock.calls[0]).toBeEmpty(); + jest.restoreAllMocks(); + expect(() => expect(obj.original).toHaveBeenCalled()).toThrow(); + }); + + test("spyOn on object doens't crash if object GC'd", () => { + const spies = new Array(1000); + (() => { + for (let i = 0; i < 1000; i++) { + var obj = { original: 42 }; + obj.original = 42; + const fn = spyOn(obj, "original"); + spies[i] = fn; + } + Bun.gc(true); + })(); + Bun.gc(true); + + jest.restoreAllMocks(); + }); + + test("spyOn works on globalThis", () => { + var obj = globalThis; + obj.original = 42; + const fn = spyOn(obj, "original"); + expect(fn).not.toHaveBeenCalled(); + expect(obj.original).toBe(42); + expect(fn).toHaveBeenCalled(); + expect(fn).toHaveBeenCalledTimes(1); + expect(fn.mock.calls).toHaveLength(1); + expect(fn.mock.calls[0]).toBeEmpty(); + jest.restoreAllMocks(); + expect(() => expect(obj.original).toHaveBeenCalled()).toThrow(); + obj.original; + expect(fn).not.toHaveBeenCalled(); + }); + } + + // spyOn does not work with getters/setters yet. +}); diff --git a/test/js/bun/test/mock-test.test.ts b/test/js/bun/test/mock-test.test.ts deleted file mode 100644 index 8c998942c..000000000 --- a/test/js/bun/test/mock-test.test.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { test, mock, expect, spyOn, jest } from "bun:test"; - -test("mockResolvedValue", async () => { - const fn = mock.mockResolvedValueOnce(42).mockResolvedValue(43); - expect(await fn()).toBe(42); - expect(await fn()).toBe(43); - expect(await fn()).toBe(43); -}); - -test("mockRejectedValue", async () => { - const fn = mock.mockRejectedValue(42); - expect(await fn()).toBe(42); - fn.mockRejectedValue(43); - expect(await fn()).toBe(43); -}); - -test("are callable", () => { - const fn = mock(() => 42); - expect(fn()).toBe(42); - expect(fn).toHaveBeenCalled(); - expect(fn).toHaveBeenCalledTimes(1); - expect(fn.mock.calls).toHaveLength(1); - expect(fn.mock.calls[0]).toBeEmpty(); - - expect(fn()).toBe(42); - expect(fn).toHaveBeenCalledTimes(2); - - expect(fn.mock.calls).toHaveLength(2); - expect(fn.mock.calls[1]).toBeEmpty(); -}); - -test(".call works", () => { - const fn = mock(function hey() { - // @ts-expect-error - return this; - }); - expect(fn.call(123)).toBe(123); - expect(fn).toHaveBeenCalled(); - expect(fn).toHaveBeenCalledTimes(1); - expect(fn.mock.calls).toHaveLength(1); - expect(fn.mock.calls[0]).toBeEmpty(); - - expect(fn()).toBe(undefined); - expect(fn).toHaveBeenCalledTimes(2); - - expect(fn.mock.calls).toHaveLength(2); - expect(fn.mock.calls[1]).toBeEmpty(); -}); - -test(".apply works", () => { - const fn = mock(function hey() { - // @ts-expect-error - return this; - }); - expect(fn.apply(123)).toBe(123); - expect(fn).toHaveBeenCalled(); - expect(fn).toHaveBeenCalledTimes(1); - expect(fn.mock.calls).toHaveLength(1); - expect(fn.mock.calls[0]).toBeEmpty(); - - expect(fn.apply(undefined)).toBe(undefined); - expect(fn).toHaveBeenCalledTimes(2); - - expect(fn.mock.calls).toHaveLength(2); - expect(fn.mock.calls[1]).toBeEmpty(); -}); - -test(".bind works", () => { - const fn = mock(function hey() { - // @ts-expect-error - return this; - }); - expect(fn.bind(123)()).toBe(123); - expect(fn).toHaveBeenCalled(); - expect(fn).toHaveBeenCalledTimes(1); - expect(fn.mock.calls).toHaveLength(1); - expect(fn.mock.calls[0]).toBeEmpty(); - - expect(fn.bind(undefined)()).toBe(undefined); - expect(fn).toHaveBeenCalledTimes(2); - - expect(fn.mock.calls).toHaveLength(2); - expect(fn.mock.calls[1]).toBeEmpty(); -}); - -test(".name works", () => { - const fn = mock(function hey() { - // @ts-expect-error - return this; - }); - expect(fn.name).toBe("hey"); -}); - -test(".name throwing doesnt segfault", () => { - function baddie() { - // @ts-expect-error - return this; - } - Object.defineProperty(baddie, "name", { - get() { - throw new Error("foo"); - }, - }); - - const fn = mock(baddie); - fn.name; -}); - -test("include arguments", () => { - const fn = mock(f => f); - expect(fn(43)).toBe(43); - expect(fn.mock.results[0]).toEqual({ - type: "return", - value: 43, - }); - expect(fn.mock.calls[0]).toEqual([43]); -}); - -test("works when throwing", () => { - const instance = new Error("foo"); - const fn = mock(f => { - throw instance; - }); - expect(() => fn(43)).toThrow("foo"); - expect(fn.mock.results[0]).toEqual({ - type: "throw", - value: instance, - }); - expect(fn.mock.calls[0]).toEqual([43]); -}); - -test("mockReset works", () => { - const instance = new Error("foo"); - const fn = mock(f => { - throw instance; - }); - expect(() => fn(43)).toThrow("foo"); - expect(fn.mock.results[0]).toEqual({ - type: "throw", - value: instance, - }); - expect(fn.mock.calls[0]).toEqual([43]); - - fn.mockReset(); - - expect(fn.mock.calls).toBeEmpty(); - expect(fn.mock.results).toBeEmpty(); - expect(fn.mock.instances).toBeEmpty(); - expect(fn).not.toHaveBeenCalled(); - - expect(() => fn(43)).toThrow("foo"); - expect(fn.mock.results[0]).toEqual({ - type: "throw", - value: instance, - }); - expect(fn.mock.calls[0]).toEqual([43]); -}); - -test("spyOn works on functions", () => { - var obj = { - original() { - return 42; - }, - }; - const fn = spyOn(obj, "original"); - expect(fn).not.toHaveBeenCalled(); - expect(obj.original()).toBe(42); - expect(fn).toHaveBeenCalled(); - expect(fn).toHaveBeenCalledTimes(1); - expect(fn.mock.calls).toHaveLength(1); - expect(fn.mock.calls[0]).toBeEmpty(); - jest.restoreAllMocks(); - expect(() => expect(obj.original).toHaveBeenCalled()).toThrow(); -}); - -test("spyOn works on object", () => { - var obj = { original: 42 }; - obj.original = 42; - const fn = spyOn(obj, "original"); - expect(fn).not.toHaveBeenCalled(); - expect(obj.original).toBe(42); - expect(fn).toHaveBeenCalled(); - expect(fn).toHaveBeenCalledTimes(1); - expect(fn.mock.calls).toHaveLength(1); - expect(fn.mock.calls[0]).toBeEmpty(); - jest.restoreAllMocks(); - expect(() => expect(obj.original).toHaveBeenCalled()).toThrow(); -}); - -test("spyOn on object doens't crash if object GC'd", () => { - const spies = new Array(1000); - (() => { - for (let i = 0; i < 1000; i++) { - var obj = { original: 42 }; - obj.original = 42; - const fn = spyOn(obj, "original"); - spies[i] = fn; - } - Bun.gc(true); - })(); - Bun.gc(true); - - jest.restoreAllMocks(); -}); - -declare global { - var original: number; -} - -test("spyOn works on globalThis", () => { - var obj = globalThis; - obj.original = 42; - const fn = spyOn(obj, "original"); - expect(fn).not.toHaveBeenCalled(); - expect(obj.original).toBe(42); - expect(fn).toHaveBeenCalled(); - expect(fn).toHaveBeenCalledTimes(1); - expect(fn.mock.calls).toHaveLength(1); - expect(fn.mock.calls[0]).toBeEmpty(); - jest.restoreAllMocks(); - expect(() => expect(obj.original).toHaveBeenCalled()).toThrow(); - obj.original; - expect(fn).not.toHaveBeenCalled(); -}); diff --git a/test/js/bun/test/test-interop.js b/test/js/bun/test/test-interop.js new file mode 100644 index 000000000..5c41082d6 --- /dev/null +++ b/test/js/bun/test/test-interop.js @@ -0,0 +1,45 @@ +module.exports = () => { + if (globalThis.Bun) { + /** @type {import('bun:jsc')} */ + const jsc = require("bun:jsc"); + const source = Bun.fileURLToPath(jsc.callerSourceOrigin()); + const bunTest = Bun.jest(source); + return { + isBun: true, + bunTest, + test: bunTest.test, + describe: bunTest.describe, + it: bunTest.it, + expect: bunTest.expect, + beforeEach: bunTest.beforeEach, + afterEach: bunTest.afterEach, + beforeAll: bunTest.beforeAll, + afterAll: bunTest.afterAll, + jest: bunTest.jest, + mock: bunTest.mock, + vi: bunTest.vi, + spyOn: bunTest.spyOn, + }; + } else { + const globals = require("@jest/globals"); + const extended = require("jest-extended"); + globals.expect.extend(extended); + globals.test.todo = globals.test; + return { + isBun: false, + bunTest: null, + test: globals.test, + describe: globals.describe, + it: globals.it, + expect: globals.expect, + beforeEach: globals.beforeEach, + afterEach: globals.afterEach, + beforeAll: globals.beforeAll, + afterAll: globals.afterAll, + jest: globals.jest, + mock: null, + vi: null, + spyOn: globals.jest.spyOn, + }; + } +}; diff --git a/test/js/bun/test/test-test.test.ts b/test/js/bun/test/test-test.test.ts index ed71e7652..7ecfdef11 100644 --- a/test/js/bun/test/test-test.test.ts +++ b/test/js/bun/test/test-test.test.ts @@ -50,1429 +50,6 @@ it("shouldn't crash when async test runner callback throws", async () => { } }); -test("toStrictEqual() vs toEqual()", () => { - expect([1, , 3]).toEqual([1, , 3]); - expect({}).toEqual({}); - expect({}).toStrictEqual({}); - expect({}).toEqual({ a: undefined }); - expect({}).not.toStrictEqual({ a: undefined }); - - class C { - hi = 34; - } - class D { - hi = 34; - } - let c = new C(); - let d = new D(); - - expect(d).toEqual(c); - expect(d).not.toStrictEqual(c); - expect({ a: 1, b: undefined }).toEqual({ a: 1 }); - expect({ a: 1 }).toEqual({ a: 1, b: undefined }); - expect({ a: 1, b: undefined }).toEqual({ a: 1, b: undefined }); - - expect({ a: 1, b: undefined }).not.toStrictEqual({ a: 1 }); - expect({ a: 1 }).not.toStrictEqual({ a: 1, b: undefined }); - expect({ a: 1, b: undefined }).toStrictEqual({ a: 1, b: undefined }); - - expect({ a: 1, b: null }).not.toEqual({ a: 1 }); - expect({ a: 1 }).not.toEqual({ a: 1, b: null }); - expect({ a: 1, b: null }).toEqual({ a: 1, b: null }); - - expect({ a: 1 }).not.toEqual({ a: true }); - expect({ a: 1 }).not.toEqual({ a: "1" }); - expect({ a: 1 }).not.toEqual({ a: 1, b: 2 }); - expect({ a: 1, b: 2 }).not.toEqual({ a: 1 }); - expect({ a: 1 }).not.toStrictEqual({ a: true }); - expect({ a: 1 }).not.toStrictEqual({ a: "1" }); - expect({ a: 1 }).not.toStrictEqual({ a: 1, b: 2 }); - expect({ a: 1, b: 2 }).not.toStrictEqual({ a: 1 }); - expect({ a: 1 }).toStrictEqual({ a: 1 }); - - expect([1, undefined, 3]).toEqual([1, undefined, 3]); - expect([1, undefined, 3]).toStrictEqual([1, undefined, 3]); - expect([1, undefined, 3]).not.toEqual([1, 2, 3]); - expect([1, undefined, 3]).not.toStrictEqual([1, 2, 3]); - expect([1, undefined, 3]).not.toEqual([1, 2]); - expect([1, undefined, 3]).not.toStrictEqual([1, 2]); - expect([1, undefined, 3]).not.toEqual([1]); - expect([1, undefined, 3]).not.toStrictEqual([1]); - expect([1, undefined, 3]).not.toEqual([]); - expect([1, undefined, 3]).not.toStrictEqual([]); - expect([1, undefined, 3]).not.toEqual([1, 3]); - expect([1, undefined, 3]).not.toStrictEqual([1, 3]); - - expect([1, null, 3]).toEqual([1, null, 3]); - expect([1, null, 3]).toStrictEqual([1, null, 3]); - expect([1, null, 3]).not.toEqual([1, 2, 3]); - expect([1, null, 3]).not.toStrictEqual([1, 2, 3]); - expect([1, null, 3]).not.toEqual([1, 2]); - expect([1, null, 3]).not.toStrictEqual([1, 2]); - expect([1, null, 3]).not.toEqual([1]); - expect([1, null, 3]).not.toStrictEqual([1]); - expect([1, null, 3]).not.toEqual([]); - expect([1, null, 3]).not.toStrictEqual([]); - expect([1, null, 3]).not.toEqual([1, 3]); - expect([1, null, 3]).not.toStrictEqual([1, 3]); - - expect([, 1]).toEqual([, 1]); - expect([, 1]).toStrictEqual([, 1]); - expect([, 1]).not.toEqual([1]); - expect([1]).not.toEqual([, 1]); - expect([, 1]).not.toStrictEqual([1]); - expect([1]).not.toStrictEqual([, 1]); - expect([, 1]).toEqual([undefined, 1]); - expect([, 1]).not.toStrictEqual([undefined, 1]); - expect([, 1]).not.toEqual([null, 1]); - expect([, 1]).not.toStrictEqual([null, 1]); - expect([undefined, 1]).toEqual([, 1]); - expect([undefined, 1]).not.toStrictEqual([, 1]); - expect([null, 1]).not.toEqual([, 1]); - expect([null, 1]).not.toStrictEqual([, 1]); - expect([undefined, 1]).toEqual([undefined, 1]); - expect([undefined, 1]).toStrictEqual([undefined, 1]); - - expect([0, , 2]).toEqual([0, undefined, 2]); - expect([, "boo2"]).toEqual([undefined, "boo2"]); - expect([, "boo"]).toEqual([, "boo"]); - expect([, 1]).toEqual([undefined, 1]); - - const s1 = Symbol("test1"); - const s2 = Symbol("test2"); - - let a = { a: 1, b: 2 }; - let b = { a: 1, b: 2 }; - a[s1] = 1; - b[s1] = 1; - a[s2] = undefined; - b[s2] = null; - expect(a).not.toEqual(b); - class F extends String { - constructor() { - super(); - } - } - - let f = new F("hello"); - let j = new String("hello"); - expect(f).not.toEqual(j); - class LaCroix { - constructor(flavor) { - this.flavor = flavor; - } - } - expect(new LaCroix("pamplemousse")).not.toStrictEqual({ - flavor: "pamplemousse", - }); - expect(new LaCroix("pamplemousse")).toEqual({ flavor: "pamplemousse" }); - - expect([, 1]).not.toStrictEqual([undefined, 1]); - - expect([0, , 2]).toEqual([0, undefined, 2]); - expect([, "boo2"]).toEqual([undefined, "boo2"]); - expect([, "boo"]).toEqual([, "boo"]); - expect([, 1]).toEqual([undefined, 1]); -}); - -function f1() { - return "hello!"; -} -function f2() { - return "hey!"; -} -test("deepEquals regex", () => { - expect(/a/imu).toEqual(/a/imu); - expect(/a/imu).not.toEqual(/ab/imu); - - expect(new RegExp("s", "g")).toEqual(new RegExp("s", "g")); - expect(new RegExp("s", "g")).not.toEqual(new RegExp("s", "i")); -}); - -test("deepEquals works with accessors", () => { - { - let l1 = [1, undefined, 2]; - let l2 = [1, undefined, 2]; - Object.defineProperty(l1, 6, { get: () => 1 }); - Object.defineProperty(l2, 6, { get: () => 1 }); - expect(l1).toEqual(l2); - expect(l1).toStrictEqual(l2); - } - { - let l1 = [1, , 2]; - let l2 = [1, undefined, 2]; - Object.defineProperty(l1, 6, { get: () => 1 }); - Object.defineProperty(l2, 6, { get: () => 2 }); - expect(l1).toEqual(l2); - expect(l1).not.toStrictEqual(l2); - } - { - let l1 = [1, , 2]; - let l2 = [1, , 2]; - Object.defineProperty(l1, "hi", { get: () => 4 }); - Object.defineProperty(l2, "hi", { get: () => 5 }); - expect(l1).toEqual(l2); - expect(l1).toStrictEqual(l2); - } - - { - let l1 = [1, , 2]; - let l2 = [1, , 2]; - Object.defineProperty(l1, "hi", { set: () => 4 }); - Object.defineProperty(l2, "hi", { set: () => 5 }); - expect(l1).toEqual(l2); - expect(l1).toStrictEqual(l2); - } - - { - let o1 = { a: 1, c: undefined, b: 2 }; - let o2 = { a: 1, c: undefined, b: 2 }; - Object.defineProperty(o1, 6, { get: () => 1 }); - Object.defineProperty(o2, 6, { get: () => 1 }); - expect(o1).toEqual(o2); - expect(o1).toStrictEqual(o2); - } - { - let o1 = { a: 1, c: undefined, b: 2 }; - let o2 = { a: 1, c: undefined, b: 2 }; - Object.defineProperty(o1, 6, { get: () => 1 }); - Object.defineProperty(o2, 6, { get: () => 2 }); - expect(o1).toEqual(o2); - expect(o1).toStrictEqual(o2); - } - { - let o1 = { a: 1, c: undefined, b: 2 }; - let o2 = { a: 1, c: undefined, b: 2 }; - Object.defineProperty(o1, "hi", { get: () => 4 }); - Object.defineProperty(o2, "hi", { get: () => 5 }); - expect(o1).toEqual(o2); - expect(o1).toStrictEqual(o2); - } - - { - let o1 = { a: 1, c: undefined, b: 2 }; - let o2 = { a: 1, c: undefined, b: 2 }; - Object.defineProperty(o1, "hi", { set: () => 4 }); - Object.defineProperty(o2, "hi", { set: () => 5 }); - expect(o1).toEqual(o2); - expect(o1).toStrictEqual(o2); - } -}); - -test("deepEquals works with proxies", () => { - { - let p1 = new Proxy({ a: 1, b: 2 }, {}); - let p2 = new Proxy({ a: 1, b: 2 }, {}); - expect(p1).toEqual(p2); - expect(p1).toStrictEqual(p2); - let p3 = new Proxy({ a: 1, b: 2 }, {}); - let p4 = new Proxy({ a: 1, b: 3 }, {}); - expect(p3).not.toEqual(p4); - expect(p3).not.toStrictEqual(p4); - } - { - let t1 = { a: 1, b: 2 }; - let h1 = { get: (t, k) => t[k] }; - let p1 = new Proxy(t1, h1); - let t2 = { a: 1, b: 2 }; - let h2 = { get: (t, k) => 0 }; - let p2 = new Proxy(t2, h2); - expect(p1).not.toEqual(p2); - expect(p1).not.toStrictEqual(p2); - } - { - let t1 = { a: 1, b: 2 }; - let h1 = { get: (t, k) => t[k] + 2 }; - let p1 = new Proxy(t1, h1); - let t2 = { a: 1, b: 2 }; - let h2 = { get: (t, k) => t[k] + 2 }; - let p2 = new Proxy(t2, h2); - expect(p1).toEqual(p2); - expect(p1).toStrictEqual(p2); - } - { - let t1 = { a: 1, b: 2 }; - let h1 = { get: (t, k) => t[k] + 2 }; - let p1 = new Proxy(t1, h1); - let t2 = { a: 1, b: 2 }; - let h2 = { get: (t, k) => t[k] + 3 }; - let p2 = new Proxy(t2, h2); - expect(p1).not.toEqual(p2); - expect(p1).not.toStrictEqual(p2); - } - { - // same handlers, different targets - let t1 = { a: 1, b: 2 }; - let t2 = { a: 1, b: 2 }; - let h1 = { get: (t, k) => t[k] + 2 }; - let p1 = new Proxy(t1, h1); - let p2 = new Proxy(t2, h1); - expect(p1).toEqual(p2); - expect(p1).toStrictEqual(p2); - } - { - // same targets, different handlers - let t1 = { a: 1, b: 2 }; - let h1 = { get: (t, k) => t[k] + 2 }; - let h2 = { get: (t, k) => t[k] + 3 }; - let p1 = new Proxy(t1, h1); - let p2 = new Proxy(t1, h2); - expect(p1).not.toEqual(p2); - expect(p1).not.toStrictEqual(p2); - } - { - // property with object - let t1 = { a: { b: 3 } }; - let h1 = { get: (t, k) => t[k] }; - let p1 = new Proxy(t1, h1); - - let t2 = { a: { b: 3 } }; - let h2 = { get: (t, k) => t[k] }; - let p2 = new Proxy(t2, h2); - - expect(p1).toEqual(p2); - expect(p1).toStrictEqual(p2); - - let t3 = { a: { b: 3 } }; - let h3 = { get: (t, k) => t[k] }; - let p3 = new Proxy(t3, h3); - - let t4 = { a: { b: 4 } }; - let h4 = { get: (t, k) => t[k] }; - let p4 = new Proxy(t4, h4); - - expect(p3).not.toEqual(p4); - expect(p3).not.toStrictEqual(p4); - } - { - // proxy object equals itself - let t1 = { a: 1, b: 2 }; - let h1 = { get: (t, k) => t[k] + 2 }; - let p1 = new Proxy(t1, h1); - expect(p1).toEqual(p1); - expect(p1).toStrictEqual(p1); - } - { - let t1 = { a: 1, b: 2 }; - let h1 = { get: (t, k) => k }; - let p1 = new Proxy(t1, h1); - - let t2 = { a: 1, b: 2 }; - let h2 = { get: (t, k) => k }; - let p2 = new Proxy(t2, h2); - - expect(p1).toEqual(p2); - expect(p1).toStrictEqual(p2); - } -}); - -test("deepEquals works with sets/maps/dates/strings", () => { - const f = Symbol.for("foo"); - - let a = new Set(); - a.add([1, 2, 3]); - a.add("hello"); - a.add({ a: 1 }); - a.add(89); - - let b = new Set(); - b.add(89); - b.add({ a: 1 }); - b.add("hello"); - b.add([1, 2, 3]); - - expect(a).toEqual(b); - expect(b).toEqual(a); - expect(b).toEqual(b); - - let obj = {}; - var c = new Set(); - obj.c = c; - obj.x = obj; - c.add(obj); - expect(obj).toEqual(obj); - - let o1 = { a: new Set() }; - o1.a.add(o1); - expect(o1).toEqual(o1); - - let o2 = new Set(); - let o3 = {}; - o3.x = o3; - o2.add(o3); - expect(o2).toEqual(o2); - - var d = new Date(); - var e = new Date(d); - e[f] = "hello"; - - expect(d).toEqual(e); - expect(e).toEqual(d); - - class Date2 extends Date { - constructor() { - super(...arguments); - } - } - - class Date3 extends Date2 { - constructor() { - super(...arguments); - } - } - - let d2 = new Date2(); - let e2 = new Date(d2); - d2[f] = "hello"; - expect(d2).toEqual(e2); - expect(e2).toEqual(d2); - - let d3 = new Date3(); - let e3 = new Date(d3); - d3[f] = "hello"; - expect(d3).toEqual(e3); - expect(e3).toEqual(d3); - - let d4 = new Date(); - let e4 = new Date3(d4); - d4[f] = "hello"; - expect(d4).toEqual(e4); - expect(e4).toEqual(d4); - - let d5 = new Date2(); - let e5 = new Date3(d5); - d5[f] = "hello"; - expect(d5).toEqual(e5); - expect(e5).toEqual(d5); - - expect(new String("a")).not.toEqual(new String("b")); - - var s1 = new String("a"); - var s2 = new String("a"); - s1[f] = "hello"; - expect(s1).toEqual(s2); - - class String2 extends String { - constructor() { - super(...arguments); - } - } - - class String3 extends String2 { - constructor() { - super(...arguments); - } - } - - let string4 = {}; - string4.__proto__ = String3.prototype; - - var s3 = new String2("a"); - var s4 = new String2("a"); - s3[f] = "hello"; - expect(s3).toEqual(s4); - - var s5 = new String("a"); - var s6 = new String3("a"); - expect(s6).not.toEqual(s5); - expect(s5).not.toEqual(s6); - - var s7 = new String2("a"); - var s8 = new String3("a"); - expect(s7).not.toEqual(s8); - expect(s8).not.toEqual(s7); - - var s9 = new String2("a"); - var s10 = new string4.constructor("a"); - expect(s9).not.toEqual(s10); - expect(s10).not.toEqual(s9); - - class F2 extends Function {} - class F3 extends F2 {} - - var f1 = new Function(); - var f2 = new F2(); - var f3 = new F3(); - expect(f1).not.toEqual(f2); - expect(f2).not.toEqual(f1); - expect(f2).not.toEqual(f3); - expect(f3).not.toEqual(f2); -}); - -describe("deepEquals with asymmetric matchers", () => { - it("should accept any string", () => { - expect({ name: "alice" }).toEqual({ name: expect.any(String) }); - expect({ name: "bob" }).toEqual({ name: expect.any(String) }); - expect({ name: "charlie" }).toEqual({ name: expect.any(String) }); - }); - - it("should accept any number", () => { - expect({ age: 42 }).toEqual({ age: expect.any(Number) }); - expect({ age: 69 }).toEqual({ age: expect.any(Number) }); - expect({ age: 73 }).toEqual({ age: expect.any(Number) }); - }); - - it("should accept any boolean", () => { - expect({ active: false }).toEqual({ active: expect.any(Boolean) }); - expect({ active: true }).toEqual({ active: expect.any(Boolean) }); - }); - - it("should not match the wrong constructors", () => { - function f() { - return 32; - } - Object.defineProperty(f, "name", { value: "String" }); - expect({ a: "123" }).toEqual({ a: expect.any(String) }); - expect({ a: "123" }).not.toEqual({ a: expect.any(f) }); - - function g() { - return 32; - } - Object.defineProperty(g, "name", { value: "BigInt" }); - expect({ a: 123n }).toEqual({ a: expect.any(BigInt) }); - expect({ a: 123n }).not.toEqual({ a: expect.any(g) }); - }); -}); - -test("toThrow", () => { - expect(() => { - throw new Error("hello"); - }).toThrow("hello"); - - var err = new Error("bad"); - expect(() => { - throw err; - }).toThrow(err); - - expect(() => { - throw new Error("good"); - }).toThrow(); - - expect(() => { - throw new Error("foo"); - }).toThrow(/oo/); - - expect(() => - expect(() => { - throw new Error("bar"); - }).toThrow(/baz/), - ).toThrow("/baz/"); - - expect(() => { - return true; - }).not.toThrow(); - - expect(() => { - return true; - }).not.toThrow(err); - - const weirdThings = [ - /watttt/g, - BigInt(123), - -42, - NaN, - Infinity, - -Infinity, - undefined, - null, - true, - false, - 0, - 1, - "", - "hello", - {}, - [], - new Date(), - new Error(), - new RegExp("foo"), - new Map(), - new Set(), - Promise.resolve(), - Promise.reject(Symbol("123")).finally(() => {}), - Symbol("123"), - ]; - for (const weirdThing of weirdThings) { - expect(() => { - throw weirdThing; - }).toThrow(); - } - - err.message = "null"; - expect(() => { - throw null; - }).toThrow(err); -}); - -test("deepEquals derived strings and strings", () => { - let a = new String("hello"); - let b = "hello"; - expect(a).toEqual(a); - expect(b).toEqual(b); - expect(a).not.toEqual(b); - expect(b).not.toEqual(a); - - class F extends String { - constructor() { - super(); - } - } - - let f = new F("hello"); - expect(f).toEqual(f); - expect(f).not.toEqual(b); - expect(b).not.toEqual(f); - - let j = new String("hello"); - expect(f).not.toEqual(j); - - class G extends String { - constructor() { - super(); - this.x = 0; - } - } - - let g = new G("hello"); - expect(g).not.toEqual(f); - expect(f).not.toEqual(g); - expect(g).toEqual(g); - expect(g).not.toEqual(b); - expect(b).not.toEqual(g); - expect(g).not.toEqual(a); -}); - -test("deepEquals throw getters", () => { - let a = { - get x() { - throw new Error("a"); - }, - }; - - let b = { - get x() { - return 3; - }, - }; - - try { - expect(a).not.toEqual(b); - } catch (e) { - expect(e.message).toContain("a"); - } - - class B { - get x() { - throw new Error("b"); - } - } - - class C { - get x() { - return 3; - } - } - - expect(() => { - expect(new B()).not.toEqual(new C()); - }).toThrow(); - - let o = [ - { - get x() { - throw new Error("c"); - }, - }, - ]; - - let p = [ - { - get x() { - return 3; - }, - }, - ]; - - try { - expect(o).not.toEqual(p); - } catch (e) { - expect(e.message).toContain("c"); - } - - const s = Symbol("s"); - let q = { - get x() { - throw new Error("d"); - }, - }; - q[s] = 3; - - let r = { - get x() { - return 3; - }, - }; - r[s] = 3; - - try { - expect(q).not.toEqual(r); - } catch (e) { - expect(e.message).toContain("d"); - } -}); - -test("deepEquals large object", () => { - let o = {}; - for (let i = 0; i < 65; i++) { - o["bun" + i] = i; - } - expect(o).toEqual(o); - let b = {}; - for (let i = 0; i < 63; i++) { - b["bun" + i] = i; - } - expect(b).toEqual(b); - expect(o).not.toEqual(b); - expect(b).not.toEqual(o); - - let c = { d: [Array(o)] }; - let d = { d: [Array(b)] }; - expect(c).toEqual(c); - expect(d).toEqual(d); - expect(c).not.toEqual(d); - expect(d).not.toEqual(c); - - let e = { d: [Array(o), Array(o)] }; - let f = { d: [Array(b), Array(b)] }; - expect(e).toEqual(e); - expect(f).toEqual(f); - expect(e).not.toEqual(f); - expect(f).not.toEqual(e); - - let p = []; - p[0] = {}; - for (let i = 0; i < 1000; i++) { - p[0]["bun" + i] = i; - } - let q = []; - q[0] = {}; - for (let i = 0; i < 1000; i++) { - q[0]["bun" + i] = i; - } - expect(p).toEqual(p); - expect(q).toEqual(q); - - q[0].bun789 = 788; - expect(p).not.toEqual(q); - expect(q).not.toEqual(p); - - let r = { d: {} }; - let s = { d: {} }; - for (let i = 0; i < 1000; i++) { - r.d["bun" + i] = i; - s.d["bun" + i] = i; - } - - expect(r).toEqual(r); - expect(s).toEqual(s); - - r.d.bun790 = 791; - expect(r).not.toEqual(s); - expect(s).not.toEqual(r); - - let t = []; - t[5] = {}; - let u = []; - u[5] = {}; - for (let i = 0; i < 1000; i++) { - t[5]["bun" + i] = i; - } - for (let i = 0; i < 30; i++) { - u[5]["bun" + i] = i; - } - expect(t).toEqual(t); - expect(u).toEqual(u); - expect(t).not.toEqual(u); - expect(u).not.toEqual(t); - - let v = { j: {} }; - let w = { j: {} }; - for (let i = 0; i < 1000; i++) { - v.j["bun" + i] = i; - w.j["bun" + i] = i; - } - - expect(v).toEqual(v); - expect(w).toEqual(w); - - v.j.bun999 = 1000; - expect(v).not.toEqual(w); - expect(w).not.toEqual(v); - expect(v).toEqual(v); - - v.j.bun999 = 999; - w.j.bun0 = 1; - expect(v).not.toEqual(w); - expect(w).not.toEqual(v); - expect(v).toEqual(v); - expect(w).toEqual(w); -}); - -test("deepEquals - Date", () => { - let d = new Date(); - expect(d).toEqual(d); - let b = d; - expect(b).toEqual(d); - d.setFullYear(1998); - expect(b).toEqual(d); - expect(b).not.toEqual(new Date()); - - var date = new Date(); - date.setFullYear(1995); - expect(new Date()).not.toEqual(date); -}); - -test("deepEquals toString and functions", () => { - expect({ toString: f1 }).toEqual({ - toString: f1, - }); - expect({ toString: f1 }).not.toEqual({ - toString: f2, - }); - - expect(f1).toEqual(f1); - expect(f1).not.toEqual(f2); -}); - -test("deepEquals set and map", () => { - let e = new Map(); - e.set("a", 1); - e.set("b", 2); - e.set("c", 3); - e.set(8, 6); - - let d = new Map(); - d.set("a", 1); - d.set("b", 2); - d.set("c", 3); - d.set(8, 6); - - expect(e).toEqual(d); - expect(d).toEqual(e); - - let f = new Map(); - f.set("a", 1); - f.set("b", 2); - f.set("c", 3); - f.set(8, 7); - expect(e).not.toEqual(f); - - let g = new Map(); - g.set({ a: { b: { c: 89 } } }, 1); - - let h = new Map(); - h.set({ a: { b: { c: 89 } } }, 1); - expect(g).toEqual(h); - - let i = new Map(); - i.set({ a: { b: { c: 89 } } }, 1); - i.set({ a: { b: { c: 89 } } }, 1); - expect(g).not.toEqual(i); - - let j = new Map(); - j.set({ a: { b: { c: 89 } } }, 1); - j.set({ a: { b: { c: 89 } } }, 1); - expect(i).toEqual(j); - - let p = new Map(); - p.set({ a: { b: { c: 90 } } }, 1); - expect(p).not.toEqual(g); - - let q = new Map(); - q.set({ a: { b: { c: 90 } } }, { a: { b: 45 } }); - - let r = new Map(); - r.set({ a: { b: { c: 90 } } }, { a: { b: 45 } }); - expect(q).toEqual(r); - - let s = new Map(); - s.set({ a: { b: { c: 90 } } }, { a: { b: 49 } }); - expect(q).not.toEqual(s); - - const u = { a: 1, b: 2 }; - - let a = new Set(); - a.add({ a: 1 }); - a.add([1, 2, 3]); - a.add("hello"); - a.add(89); - - let b = new Set(); - b.add({ a: 1 }); - b.add("hello"); - b.add([1, 2, 3]); - b.add(89); - expect(a).toEqual(b); - expect(b).toEqual(a); - let c = new Set(); - c.add(89); - c.add("hello"); - c.add({ a: 1 }); - c.add([1, 2, 3, 4]); - expect(a).not.toEqual(c); -}); - -test("deepEquals - symbols", () => { - const x = [5, 6]; - x[99] = 7; - - const y = [5, 6]; - y[99] = 7; - - expect(x).toEqual(y); - - const s1 = Symbol("test1"); - const s2 = Symbol("test2"); - - const o = { a: 1 }; - o[s1] = 45; - o[99] = 99; - o[s2] = 3; - - const k = { a: 1 }; - k[99] = 99; - k[s2] = 3; - k[s1] = 45; - - expect(o).toEqual(k); -}); - -test("deepEquals should not segfault", () => { - const obj = { ...Object.fromEntries(Object.entries([1, 2, 3, 4])), length: 4 }; - expect(() => { - expect(obj).toEqual([1, 2, 3, 4]); - }).toThrow(); - expect(() => { - expect([1, 2, 3, 4]).toEqual(obj); - }).toThrow(); -}); - -test("toEqual objects and arrays", () => { - { - let obj = { 0: 4, 1: 3, length: 2 }; - expect(Array.from(obj)).toEqual([4, 3]); - expect(Array.from(obj)).toStrictEqual([4, 3]); - } - { - let obj = { 0: 4, 1: 3, length: 4 }; - expect(Array.from(obj)).toEqual([4, 3]); - expect(Array.from(obj)).not.toStrictEqual([4, 3]); - expect(Array.from(obj)).toEqual([4, 3, undefined, undefined]); - expect(Array.from(obj)).toStrictEqual([4, 3, undefined, undefined]); - expect(Array.from(obj)).toEqual([4, 3, , ,]); - expect(Array.from(obj)).not.toStrictEqual([4, 3, , ,]); - } - { - let a1 = [1, undefined, 3, , 4, null]; - let a2 = [1, undefined, 3, , 4, null, , ,]; - expect(a1).toEqual(a2); - expect(a1).not.toStrictEqual(a2); - expect(a2).toEqual(a1); - expect(a2).not.toStrictEqual(a1); - } - { - let a1 = [, , , , , , , , , , , ,]; - let a2 = [undefined]; - expect(a1).toEqual(a2); - expect(a1).not.toStrictEqual(a2); - expect(a2).toEqual(a1); - expect(a2).not.toStrictEqual(a1); - } - { - const a = [1]; - const b = [1]; - expect(a).toEqual(b); - Object.preventExtensions(b); - expect(a).toEqual(b); - Object.preventExtensions(a); - expect(a).toEqual(b); - } - { - let o1 = { 1: 4, 6: 3 }; - let o2 = { 1: 4, 6: 3 }; - expect(o1).toEqual(o2); - expect(o1).toStrictEqual(o2); - } - { - let o1 = { 1: 4, 6: 2 }; - let o2 = { 1: 4, 6: 3 }; - expect(o1).not.toEqual(o2); - expect(o1).not.toStrictEqual(o2); - } - - { - let o1 = { a: 1, 3: 0 }; - let o2 = { a: 1, 3: 0 }; - expect(o1).toEqual(o2); - expect(o1).toStrictEqual(o2); - } - { - let o1 = { a: 1, 3: 0 }; - let o2 = { a: 1, 3: 1 }; - expect(o1).not.toEqual(o2); - expect(o1).not.toStrictEqual(o2); - } - { - let o1 = { a: {}, 4: { b: 3, c: { 9: 2 } } }; - let o2 = { a: {}, 4: { b: 3, c: { 9: 2 } } }; - expect(o1).toEqual(o2); - expect(o1).toStrictEqual(o2); - } - { - let o1 = { a: {}, 4: { b: 3, c: { 9: 2 } } }; - let o2 = { a: {}, 4: { b: 3, c: { 9: 3 } } }; - expect(o1).not.toEqual(o2); - expect(o1).not.toStrictEqual(o2); - } - - { - let o1 = { a: 1, b: 2, c: 3 }; - let o2 = { a: 1, b: 2, c: 3, 0: 1 }; - expect(o1).not.toEqual(o2); - expect(o1).not.toStrictEqual(o2); - } - - { - let o1 = { a: 1, b: 2, c: 3, 0: 1 }; - let o2 = { a: 1, b: 2, c: 3 }; - expect(o1).not.toEqual(o2); - expect(o1).not.toStrictEqual(o2); - } - - expect("hello").toEqual("hello"); - const s1 = Symbol("test1"); - const s2 = Symbol("test2"); - - expect({ a: 1, b: 2 }).toEqual({ b: 2, a: 1 }); - expect([1, 2, 3]).toEqual([1, 2, 3]); - expect({ a: 1, b: 2 }).not.toEqual({ b: 2, a: 1, c: 3 }); - expect([1, 2, 3]).not.toEqual([1, 2, 3, 4]); - expect({ a: 1, b: 2, c: 3 }).not.toEqual({ a: 1, b: 2 }); - expect([1, 2, 3, 4]).not.toEqual([1, 2, 3]); - - let a = [{ a: 1 }, { b: 2, c: 3, d: 4 }, { e: 5, f: 6 }]; - let b = [{ a: 1 }, { b: 2, c: 3, d: 4 }, { e: 5, f: 6 }]; - expect(a).toEqual(b); - expect(b).toEqual(a); - a[0].a = 2; - expect(a).not.toEqual(b); - expect(b).not.toEqual(a); - - let c = { [Symbol("test")]: 1 }; - let d = { [Symbol("test")]: 1 }; - expect(c).not.toEqual(d); - expect(d).not.toEqual(c); - - a = { [s1]: 1 }; - a[s1] = 1; - b = { [s2]: 1 }; - b[s2] = 1; - expect(a).not.toEqual(b); - expect(b).not.toEqual(a); - - a = {}; - b = {}; - a[s1] = 1; - b[s1] = 1; - expect(a).toEqual(b); - - a = {}; - b = {}; - a[s1] = 1; - b[s1] = 2; - expect(a).not.toEqual(b); - - a = {}; - b = {}; - a[s1] = 1; - b[s1] = 1; - a[s2] = 2; - b[s2] = 2; - expect(a).toEqual(b); - - a = {}; - b = {}; - a[s1] = 1; - b[s1] = 1; - a[s2] = 2; - b[s2] = 3; - expect(a).not.toEqual(b); - - a = { a: 1, b: 2 }; - b = { a: 1, b: 2 }; - a[s1] = 1; - b[s1] = 1; - expect(a).toEqual(b); - - a = { a: 2, b: 2 }; - b = { a: 1, b: 2 }; - a[s1] = 1; - b[s1] = 1; - expect(a).not.toEqual(b); - - // do the same tests for arrays - a = [{ a: 1 }, { b: 2, c: 3, d: 4 }, { e: 5, f: 6 }]; - b = [{ a: 1 }, { b: 2, c: 3, d: 4 }, { e: 5, f: 6 }]; - expect(a).toEqual(b); - expect(b).toEqual(a); - a[0].a = 2; - expect(a).not.toEqual(b); - expect(b).not.toEqual(a); - - a = [1, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 1; - expect(a).toEqual(b); - - a = [1, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 2; - expect(a).not.toEqual(b); - - a = [1, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 1; - a[s2] = 2; - b[s2] = 2; - expect(a).toEqual(b); - - a = [1, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 1; - a[s2] = 2; - b[s2] = 3; - expect(a).not.toEqual(b); - - a = [1, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 1; - expect(a).toEqual(b); - - a = [2, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 1; - expect(a).not.toEqual(b); - - // do the same tests for objects and arrays with null and undefined - a = { a: 1, b: 2 }; - b = { a: 1, b: 2 }; - a[s1] = 1; - b[s1] = 1; - a[s2] = null; - b[s2] = undefined; - expect(a).not.toEqual(b); - - a = { a: 1, b: 2 }; - b = { a: 1, b: 2 }; - a[s1] = 1; - b[s1] = 1; - a[s2] = undefined; - b[s2] = null; - expect(a).not.toEqual(b); - - a = { a: 1, b: 2 }; - b = { a: 1, b: 2 }; - a[s1] = 1; - b[s1] = 1; - a[s2] = null; - b[s2] = null; - expect(a).toEqual(b); - - a = { a: 1, b: 2 }; - b = { a: 1, b: 2 }; - a[s1] = 1; - b[s1] = 1; - a[s2] = undefined; - b[s2] = undefined; - expect(a).toEqual(b); - - a = [1, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 1; - a[s2] = null; - b[s2] = undefined; - expect(a).not.toEqual(b); - - a = [1, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 1; - a[s2] = undefined; - b[s2] = null; - expect(a).not.toEqual(b); - - a = [1, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 1; - a[s2] = null; - b[s2] = null; - expect(a).toEqual(b); - - a = [1, 2, 3]; - b = [1, 2, 3]; - a[s1] = 1; - b[s1] = 1; - a[s2] = undefined; - b[s2] = undefined; - expect(a).toEqual(b); - - // similar tests for indexed objects - a = { 0: 1, 1: 2, 2: 3 }; - b = { 0: 1, 1: 2, 2: 3 }; - a[s1] = 1; - b[s1] = 1; - expect(a).toEqual(b); - - a = { 0: 1, 1: 2, 2: 3 }; - b = { 0: 1, 1: 2, 2: 3 }; - a[s1] = 1; - b[s1] = 1; - a[s2] = 2; - b[s2] = 3; - expect(a).not.toEqual(b); - - a = { 0: 1, 1: 3, 2: 3 }; - b = { 0: 1, 1: 2, 2: 3 }; - a[s1] = 1; - b[s1] = 1; - a[s2] = 2; - b[s2] = 2; - expect(a).not.toEqual(b); - - a = [1, 2, 3]; - b = [1, 2, 3, 4]; - expect(a).not.toEqual(b); - - a = [1, 2, 3, 4]; - b = [1, 2, 3]; - expect(a).not.toEqual(b); - - a = { a: 1, b: 2 }; - b = { a: 1, b: 2, c: 3 }; - expect(a).not.toEqual(b); - - a = { a: 1, b: 2, c: 3 }; - b = { a: 1, b: 2 }; - expect(a).not.toEqual(b); -}); - -test("symbol based keys in arrays are processed correctly", () => { - const mySymbol = Symbol("test"); - - const actual1 = []; - actual1[mySymbol] = 3; - - const actual2 = []; - actual2[mySymbol] = 4; - - const expected = []; - expected[mySymbol] = 3; - - expect(actual2).not.toEqual(expected); - expect(actual1).toEqual(expected); -}); - -test("non-enumerable members should be skipped during equal", () => { - const actual = { - x: 3, - }; - Object.defineProperty(actual, "test", { - enumerable: false, - value: 5, - }); - expect(actual).toEqual({ x: 3 }); -}); - -test("non-enumerable symbolic members should be skipped during equal", () => { - const actual = { - x: 3, - }; - const mySymbol = Symbol("test"); - Object.defineProperty(actual, mySymbol, { - enumerable: false, - value: 5, - }); - expect(actual).toEqual({ x: 3 }); -}); - -test("properties with the same circularity are equal", () => { - const a = {}; - a.x = a; - const b = {}; - b.x = b; - expect(a).toEqual(b); - expect(b).toEqual(a); - - const c = { - x: a, - }; - const d = { - x: b, - }; - - expect(d).toEqual(c); - expect(c).toEqual(d); -}); - -test("toEqual() - arrays", () => { - expect([1, 2, 3]).toEqual([1, 2, 3]); - expect([1, 2, 3, 4]).not.toEqual([1, 2, 3]); -}); - -test("properties with different circularity are not equal", () => { - const a = {}; - a.x = { y: a }; - const b = {}; - const bx = {}; - b.x = bx; - bx.y = bx; - expect(a).not.toEqual(b); - expect(b).not.toEqual(a); - - const c = {}; - c.x = a; - const d = {}; - d.x = b; - expect(c).not.toEqual(d); - expect(d).not.toEqual(c); -}); - -test("are not equal if circularity is not on the same property", () => { - const a = {}; - const b = {}; - a.a1 = a; - b.a1 = {}; - b.a1.a1 = a; - - expect(a).not.toEqual(b); - expect(b).not.toEqual(a); - - const c = {}; - c.x = { x: c }; - const d = {}; - d.x = d; - - expect(d).not.toEqual(c); - expect(c).not.toEqual(d); -}); - -test("random isEqual tests", () => { - expect(1).toEqual(1); - expect(1).not.toEqual(2); - expect(1).not.toEqual("1"); - expect(1).not.toEqual(true); - expect(1).not.toEqual(false); - expect(1).not.toEqual(null); - expect(1).not.toEqual(undefined); - expect(1).not.toEqual({}); - expect(1).not.toEqual([]); - expect(1).not.toEqual([1]); - expect(1).not.toEqual([1, 2]); - expect(1).not.toEqual([1, 2, 3]); - expect(1).not.toEqual([1, 2, 3, 4]); - expect(1).not.toEqual([1, 2, 3, 4, 5]); - expect(1).not.toEqual([1, 2, 3, 4, 5, 6]); - expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7]); - expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8]); - expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); - expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); - expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); - expect(1).not.toEqual([1, 2, 3, 4, 5, 6, 7, 8]); - - // test toEquals for objects with getters and setters - - expect([]).toEqual([]); - expect([1]).toEqual([1]); - expect([1, 2]).toEqual([1, 2]); - expect([1, 2, 3]).toEqual([1, 2, 3]); - expect({}).toEqual({}); - expect({}).not.toEqual([]); - expect([]).not.toEqual({}); - - const obj = { - get a() { - return 1; - }, - }; - expect(obj).toEqual({ a: 1 }); - expect({ a: 1 }).toEqual(obj); - expect(obj).not.toEqual({ a: 2 }); - expect({ a: 2 }).not.toEqual(obj); - - let a = new Set(); - a.add([1, 2, 3]); - a.add("hello"); - a.add({ a: 1 }); - a.add(89); - let b = new Set(); - b.add(89); - b.add({ a: 1 }); - b.add("hello"); - b.add([1, 2, 3]); - expect(a).toEqual(b); - expect(b).toEqual(a); - let c = new Set(); - c.add(89); - c.add("helo"); - c.add({ a: 1 }); - c.add([1, 2, 3]); - expect(a).not.toEqual(c); - - a = new Map(); - a.set(1, 89); - a.set("hello", 2); - a.set({ a: 1 }, 3); - a.set([1, 2, 3], 4); - b = new Map(); - b.set(1, 89); - b.set("hello", 2); - b.set({ a: 1 }, 3); - b.set([1, 2, 3], 4); - expect(a).toEqual(b); - expect(b).toEqual(a); - c = new Map(); - c.set({ a: 1 }, 3); - c.set(1, 80); - c.set([1, 2, 3], 4); - c.set("hello", 2); - expect(a).not.toEqual(c); - - a = new Set(); - a.add(89); - a.add("hello"); - a.add({ a: 1 }); - a.add([1, 2, 3]); - a.add(a); - b = new Set(); - b.add(89); - b.add("hello"); - b.add(b); - b.add({ a: 1 }); - b.add([1, 2, 3]); - expect(a).toEqual(b); - expect(b).toEqual(a); -}); - test("testing Bun.deepEquals() using isEqual()", () => { const t = new Uint8Array([1, 2, 3, 4, 5]); expect(t).toEqual(t.slice()); @@ -1586,1319 +163,6 @@ test("testing Bun.deepEquals() using isEqual()", () => { expect(-Infinity).toEqual(-1 / 0); }); -test("toHaveProperty() - emojis", () => { - expect({ "👍": "thumbs up" }).toHaveProperty("👍", "thumbs up"); - expect({ "👩👩👧👧": "family" }).toHaveProperty("👩👩👧👧", "family"); - expect({ "😶🌫️": "fog" }).toHaveProperty("😶🌫️", "fog"); - expect({ "👩❤️👨": "couple" }).toHaveProperty("👩❤️👨", "couple"); - expect({ "👩❤️👨👨👧👧": "family" }).toHaveProperty("👩❤️👨👨👧👧", "family"); - expect({ "👩❤️👨👨👧": "family" }).toHaveProperty("👩❤️👨👨👧", "family"); - expect({ "👩❤️👨👨👧": "family" }).not.toHaveProperty("👩❤️👨👨👧👧", "family"); - - // emojis in array - expect(["👍", "👎"]).toHaveProperty("0", "👍"); - expect(["👍", "👎"]).toHaveProperty("1", "👎"); - expect(["👍", "👎"]).not.toHaveProperty("0", "👎"); - expect(["👍", "👎"]).not.toHaveProperty("1", "👍"); - expect(["👩❤️👨👨👧👧"]).toHaveProperty("0", "👩❤️👨👨👧👧"); - expect(["👩❤️👨👨👧👧"]).toHaveProperty([0], "👩❤️👨👨👧👧"); - expect(["😶🌫️"]).toHaveProperty([0], "😶🌫️"); -}); - -test("toHaveProperty() - dot and bracket notation edge cases", () => { - expect({ a: 1 }).not.toHaveProperty("."); - expect({ a: 1 }).not.toHaveProperty("]"); - expect({ a: 1 }).not.toHaveProperty("["); - expect({ a: 1 }).not.toHaveProperty("[]"); - expect({ a: 1 }).not.toHaveProperty("[[]]"); - expect({ a: 1 }).not.toHaveProperty("[["); - expect({ a: 1 }).not.toHaveProperty("]]"); - expect({ a: 1 }).not.toHaveProperty("[]]"); - expect({ a: 1 }).not.toHaveProperty("[[]"); - expect({ a: 1 }).not.toHaveProperty(".]"); - expect({ a: 1 }).not.toHaveProperty(".["); - expect({ "": 1 }).toHaveProperty("[.", 1); - expect({ a: 1 }).not.toHaveProperty("[."); - expect({ a: 1 }).not.toHaveProperty("]."); - expect({ a: 1 }).not.toHaveProperty("].["); - expect({ a: 1 }).not.toHaveProperty("].]"); - expect({ a: 1 }).not.toHaveProperty("[.]"); - expect({ a: 1 }).not.toHaveProperty("[.["); - - expect([1]).toHaveProperty("[0]", 1); - expect([1]).toHaveProperty("[0][", 1); - expect([1]).toHaveProperty("[0]]", 1); - expect([1]).toHaveProperty("[0][[", 1); - expect([1]).toHaveProperty("[][[[0]", 1); - expect([1]).toHaveProperty("[][[[]][[][][.0", 1); - expect([1]).toHaveProperty("[][[[]][[][][.[][[][[[][][0", 1); - expect([1]).not.toHaveProperty("......1.............", 1); - expect([1]).not.toHaveProperty("......0.............", 1); - expect([1]).not.toHaveProperty(".0", 1); - expect([1]).not.toHaveProperty("0.", 1); - expect([{ "": 1 }]).toHaveProperty("0.", 1); - expect({ "": { "": 1 } }).toHaveProperty(".", 1); - expect({ "": { "": { "": 1 } } }).toHaveProperty("..", 1); - expect({ "": { "": { "": 1 } } }).not.toHaveProperty(".", 1); - expect({ "": { "": { "": 1 } } }).not.toHaveProperty("...", 1); - expect({ "": { "": { "": 1 } } }).not.toHaveProperty("....", 1); - expect([1]).toHaveProperty("0.[[[][][]][[[][[]]]]", 1); - expect([1]).not.toHaveProperty("[0].", 1); - expect([1]).toHaveProperty("0", 1); - expect([1]).toHaveProperty("[].0", 1); - expect([1]).toHaveProperty("[.0", 1); - expect([1]).toHaveProperty("].0", 1); - expect([1]).toHaveProperty("0[]][[[]", 1); - expect([1]).toHaveProperty("[[]][[[][][0", 1); - expect([1]).toHaveProperty("0", 1); - expect([1]).toHaveProperty("0.[", 1); - expect([1]).not.toHaveProperty("0........[", 1); - expect([1]).not.toHaveProperty("0..[", 1); - expect([1]).not.toHaveProperty(".0", 1); - expect([1]).toHaveProperty("[].0", 1); - expect([1]).not.toHaveProperty("[]..0", 1); - expect([1]).toHaveProperty("[.][.[[.]]]]].[.[].].]]]]].].].0", 1); - expect([1]).not.toHaveProperty("[.][.[[.]]]]].[.[].].]]0]]].].].", 1); - expect([1]).toHaveProperty("[.][.[[.]]]]].[.[].].]]0]]].].]", 1); - expect([1]).not.toHaveProperty("[.][.[[..]]]]].[.[].].]]0]]].].]", 1); - expect([1]).toHaveProperty("[.][.[[.]]]]].[.[].].0.]]]]].].]", 1); - expect([1]).not.toHaveProperty("[.][.[[.]]]]].[.[].].0.]]] ]].].]", 1); - expect([1]).not.toHaveProperty("0 ", 1); - expect([1]).not.toHaveProperty(" 0 ", 1); - expect([1]).not.toHaveProperty(" 0[] ", 1); - expect([1]).not.toHaveProperty(" 0] ", 1); - expect([1]).not.toHaveProperty(" .[0]", 1); - - expect({ "": 1 }).not.toHaveProperty(".", 1); - expect({ "": 1 }).not.toHaveProperty("]", 1); - expect({ "": 1 }).not.toHaveProperty("[", 1); - expect({ "": 1 }).toHaveProperty("", 1); - - expect({ "": 1 }).not.toHaveProperty("..", 1); - expect({ "": { "": 1 } }).not.toHaveProperty("..", 1); - expect([{ "": 1 }]).toHaveProperty("0.", 1); - expect([{ "": 1 }]).not.toHaveProperty(".0.", 1); - expect({ "": [1] }).toHaveProperty(".0", 1); - expect({ "": [1] }).not.toHaveProperty("..0", 1); - expect([{ "": 1 }]).not.toHaveProperty("0..", 1); - expect([{ "": { "": 1 } }]).toHaveProperty("0..", 1); - - expect([1]).not.toHaveProperty("[0].", 1); - expect([1]).not.toHaveProperty("[0][0]", 1); - expect({ a: [1] }).toHaveProperty("a[[[[[[[[[0]]]", 1); - expect({ "[[[": 0 }).not.toHaveProperty("[[[", 0); -}); - -test("toHaveProperty() - with string or array", () => { - const a = new Array(["a", "b", "c"]); - expect(a).toHaveProperty("0.1", "b"); - const b = new Array("a", "b", "c"); - expect({ a: { b: { c: 1 } } }).toHaveProperty(b); - const c = { - a: { b: 1 }, - "a.b": 2, - }; - const d = new Array("a.b"); - expect(c).toHaveProperty(d, 2); - const houseForSale = { - bath: true, - bedrooms: 4, - kitchen: { - amenities: ["oven", "stove", "washer"], - area: 20, - wallColor: "white", - "nice.oven": true, - }, - livingroom: { - amenities: [ - { - couch: [ - ["large", { dimensions: [20, 20] }], - ["small", { dimensions: [10, 10] }], - ], - }, - ], - }, - sunroom: "yes", - "ceiling.height": 20, - "entrance.window": 3, - entrance: { window: 5 }, - }; - expect(houseForSale).toHaveProperty("entrance.window", 5); - expect(houseForSale).toHaveProperty(["entrance", "window"], 5); - expect(houseForSale).toHaveProperty(["entrance.window"], 3); - expect(houseForSale).toHaveProperty("bath"); - expect(houseForSale).not.toHaveProperty("jacuzzi"); - // expect(houseForSale).toHaveProperty("jacuzzi"); - // expect(houseForSale).not.toHaveProperty("bath"); - expect(houseForSale).toHaveProperty("bath", true); - expect(houseForSale).not.toHaveProperty("bath", false); - // expect(houseForSale).toHaveProperty("bath", false); - // expect(houseForSale).not.toHaveProperty("bath", true); - expect(houseForSale).toHaveProperty("bedrooms", 4); - expect(houseForSale).toHaveProperty(["sunroom"], "yes"); - expect(houseForSale).toHaveProperty("kitchen.area", 20); - expect(houseForSale).toHaveProperty("kitchen.amenities", ["oven", "stove", "washer"]); - expect(houseForSale).not.toHaveProperty(["kitchen", "area"], 21); - expect(houseForSale).toHaveProperty(["kitchen", "area"], 20); - expect(houseForSale).not.toHaveProperty(["kitchen", "area"], 29); - expect(houseForSale).toHaveProperty(["kitchen", "amenities"], ["oven", "stove", "washer"]); - expect(houseForSale).toHaveProperty("kitchen.amenities[2]", "washer"); - expect(houseForSale).toHaveProperty(["kitchen", "amenities", 1], "stove"); - expect(houseForSale).toHaveProperty(["kitchen", "amenities", 0], "oven"); - expect(houseForSale).toHaveProperty("livingroom.amenities[0].couch[0][1].dimensions[0]", 20); - expect(houseForSale).toHaveProperty(["kitchen", "nice.oven"]); - expect(houseForSale).not.toHaveProperty(["kitchen", "open"]); - expect(houseForSale).toHaveProperty(["ceiling.height"], 20); - expect({ a: { b: 1 } }).toHaveProperty("a.b"); - expect({ a: [2, 3, 4] }).toHaveProperty("a.0"); - expect({ a: [2, 3, 4] }).toHaveProperty("a.1"); - expect({ a: [2, 3, 4] }).toHaveProperty("a.2"); - expect({ a: [2, 3, 4] }).toHaveProperty("a[1]"); - expect([2, 3, 4]).toHaveProperty("1"); - expect([2, 3, 4]).toHaveProperty("[1]"); - expect([2, [6, 9], 4]).toHaveProperty("1.1"); - expect([2, [6, 9], 4]).toHaveProperty("1[1]"); - expect([2, [6, 9], 4]).toHaveProperty("[1].1"); - expect([2, [6, 9], 4]).toHaveProperty("[1][1]"); - expect([2, [6, 9], 4]).toHaveProperty([0], 2); - expect({ a: { b: 1 } }).toHaveProperty("a.b"); - expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a.2.1.b"); - expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a"); - expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a[2][1].b"); - expect({ a: [1, 2, [3, { b: 1 }]] }).toHaveProperty("a[2][1]"); - expect({ a: [1, 2, [3, { b: 1 }]] }).not.toHaveProperty("a[2][1].c"); - expect("test").toHaveProperty("length"); - expect({}).toHaveProperty("constructor"); - expect({}).toHaveProperty("constructor.name"); - expect({}).toHaveProperty("constructor.name", "Object"); - expect(new Date()).toHaveProperty("getTime"); -}); - -test("toHaveProperty() - all", () => { - expect({ a: 1 }).toHaveProperty("a"); - expect({ a: 1 }).toHaveProperty("a", 1); - expect({ a: 1 }).not.toHaveProperty("b"); - expect({ a: 1 }).not.toHaveProperty("a", 2); - - // test with object with property "a" with all types of values (including undefined) - expect({ a: undefined }).toHaveProperty("a"); - expect({ a: null }).toHaveProperty("a"); - expect({ a: 0 }).toHaveProperty("a"); - expect({ a: false }).toHaveProperty("a"); - expect({ a: "" }).toHaveProperty("a"); - expect({ a: {} }).toHaveProperty("a"); - expect({ a: [] }).toHaveProperty("a"); - expect({ a: () => {} }).toHaveProperty("a"); - - // test with object with property "a" with all types of values (including undefined) - expect({ a: undefined }).toHaveProperty("a", undefined); - expect({ a: null }).toHaveProperty("a", null); - expect({ a: 0 }).toHaveProperty("a", 0); - expect({ a: false }).toHaveProperty("a", false); - expect({ a: "" }).toHaveProperty("a", ""); - expect({ a: {} }).toHaveProperty("a", {}); - expect({ a: [] }).toHaveProperty("a", []); - expect({ a: () => {} }).not.toHaveProperty("a", () => {}); - - // test with object with property "a" with all types of values (including undefined) - - expect({ a: undefined }).not.toHaveProperty("a", null); - expect({ a: null }).not.toHaveProperty("a", undefined); - expect({ a: 0 }).not.toHaveProperty("a", null); - expect({ a: false }).not.toHaveProperty("a", null); - expect({ a: "" }).not.toHaveProperty("a", null); - expect({ a: {} }).not.toHaveProperty("a", null); - expect({ a: [] }).not.toHaveProperty("a", null); - expect({ a: () => {} }).not.toHaveProperty("a", null); - - expect({ a: undefined }).not.toHaveProperty("a", 0); - expect({ a: null }).not.toHaveProperty("a", 0); - expect({ a: 0 }).not.toHaveProperty("a", 1); - expect({ a: false }).not.toHaveProperty("a", 0); - expect({ a: "" }).not.toHaveProperty("a", 0); - expect({ a: {} }).not.toHaveProperty("a", 0); - expect({ a: [] }).not.toHaveProperty("a", 0); - expect({ a: () => {} }).not.toHaveProperty("a", 0); - - expect({ a: undefined }).not.toHaveProperty("a", false); - expect({ a: null }).not.toHaveProperty("a", false); - expect({ a: 0 }).not.toHaveProperty("a", false); - expect({ a: false }).not.toHaveProperty("a", true); - expect({ a: "" }).not.toHaveProperty("a", false); - expect({ a: {} }).not.toHaveProperty("a", false); - expect({ a: [] }).not.toHaveProperty("a", false); - expect({ a: () => {} }).not.toHaveProperty("a", false); - - expect({ a: undefined }).not.toHaveProperty("a", ""); - expect({ a: null }).not.toHaveProperty("a", ""); - expect({ a: 0 }).not.toHaveProperty("a", ""); - expect({ a: false }).not.toHaveProperty("a", ""); - expect({ a: "" }).not.toHaveProperty("a", "a"); - expect({ a: {} }).not.toHaveProperty("a", ""); - expect({ a: [] }).not.toHaveProperty("a", ""); - expect({ a: () => {} }).not.toHaveProperty("a", ""); - - expect({ a: undefined }).not.toHaveProperty("a", {}); - expect({ a: null }).not.toHaveProperty("a", {}); - expect({ a: 0 }).not.toHaveProperty("a", {}); - expect({ a: false }).not.toHaveProperty("a", {}); - expect({ a: "" }).not.toHaveProperty("a", {}); - expect({ a: {} }).not.toHaveProperty("a", { a: 1 }); - expect({ a: [] }).not.toHaveProperty("a", {}); - expect({ a: () => {} }).not.toHaveProperty("a", {}); - - // test object with property "a" with value set, map, string - expect({ a: new Set([1, 2, 3]) }).toHaveProperty("a", new Set([3, 2, 1])); - expect({ a: new Map([{ a: 1 }, { b: 2 }, { c: 3 }]) }).toHaveProperty("a", new Map([{ c: 3 }, { b: 2 }, { a: 1 }])); - expect({ a: new String("a") }).toHaveProperty("a", new String("a")); - expect({ a: new String("a") }).not.toHaveProperty("a", "a"); - expect({ a: new String("a") }).not.toHaveProperty("a", "b"); - expect({ a: new String("a") }).not.toHaveProperty("a", new String("b")); - expect({ a: new String("a") }).not.toHaveProperty("a", new Number(1)); - expect({ a: new String("a") }).not.toHaveProperty("a", new Boolean(true)); - expect({ a: new String("a") }).not.toHaveProperty("a", new Boolean(false)); - expect({ a: new String("a") }).not.toHaveProperty("a", new Object()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Function()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Date()); - expect({ a: new String("a") }).not.toHaveProperty("a", new RegExp()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Error()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Promise(() => {})); - expect({ a: new String("a") }).not.toHaveProperty("a", new WeakSet()); - expect({ a: new String("a") }).not.toHaveProperty("a", new WeakMap()); - expect({ a: new String("a") }).not.toHaveProperty("a", Symbol("a")); - expect({ a: new String("a") }).not.toHaveProperty("a", new Int8Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Uint8Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Uint8ClampedArray()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Int16Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Uint16Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Int32Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Uint32Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Float32Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new Float64Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new BigInt64Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new BigUint64Array()); - expect({ a: new String("a") }).not.toHaveProperty("a", new ArrayBuffer()); - expect({ a: new String("a") }).not.toHaveProperty("a", new SharedArrayBuffer()); - expect({ a: new String("a") }).not.toHaveProperty("a", new DataView(new ArrayBuffer(1))); - - // test property equality with sets, maps, objects, arrays, and String - expect({ a: new Set([1, 2, 3]) }).toHaveProperty("a", new Set([1, 2, 3])); - expect({ a: new Map([{ a: 1 }, { b: 2 }, { c: 3 }]) }).toHaveProperty("a", new Map([{ a: 1 }, { b: 2 }, { c: 3 }])); - expect({ a: { a: 1, b: 2, c: 3 } }).toHaveProperty("a", { a: 1, b: 2, c: 3 }); - expect({ a: [1, 2, 3] }).toHaveProperty("a", [1, 2, 3]); - expect({ a: "a" }).toHaveProperty("a", "a"); - expect({ a: new String("a") }).toHaveProperty("a", new String("a")); - expect({ a: new String("a") }).not.toHaveProperty("a", "a"); -}); - -test("toHaveProperty() - null or undefined", () => { - expect(null).not.toHaveProperty("length"); - expect(undefined).not.toHaveProperty("length"); -}); - -test("toBe()", () => { - const a = 1; - const b = 1; - expect(a).toBe(a); - expect(a).toBe(b); - expect(a).toBe(1); - expect(1).toBe(a); - expect(b).toBe(a); - - const c = { a: 1 }; - const d = { a: 1 }; - expect(c).toBe(c); - expect(c).not.toBe(d); - expect(c).not.toBe({ a: 1 }); - expect({ a: 1 }).not.toBe(c); - expect(d).not.toBe(c); - - expect(1).toBe(1); - // expect(1).not.toBe(1); - - expect(1).not.toBe(2); - expect(1).not.toBe("1"); - expect("hello test").toBe("hello test"); - expect("hello test").not.toBe("hello test2"); -}); - -test("toHaveLength()", () => { - expect({ length: Number.MAX_SAFE_INTEGER }).toHaveLength(Number.MAX_SAFE_INTEGER); - expect("123").toHaveLength(3); - expect([1, 2, 3]).toHaveLength(3); - expect([1, 2, 3]).not.toHaveLength(2); - expect("123").not.toHaveLength(2); - expect({ length: 3 }).toHaveLength(3); - expect({ length: 3 }).not.toHaveLength(2); - expect({ length: 3 }).not.toHaveLength(Number.MAX_SAFE_INTEGER); - expect({ length: Number.MAX_SAFE_INTEGER }).not.toHaveLength(Number.MAX_SAFE_INTEGER - 1); - expect({ length: 3.3 }).not.toHaveLength(3); - expect("123").not.toHaveLength(-0); -}); - -test("toHaveLength() extended", () => { - // Headers - expect(new Headers()).toHaveLength(0); - expect(new Headers({ a: "1" })).toHaveLength(1); - - // FormData - const form = new FormData(); - expect(form).toHaveLength(0); - form.append("a", "1"); - expect(form).toHaveLength(1); - - // URLSearchParams - expect(new URLSearchParams()).toHaveLength(0); - expect(new URLSearchParams("a=1")).toHaveLength(1); - expect(new URLSearchParams([["a", "1"]])).toHaveLength(1); - - // files - const thisFile = Bun.file(import.meta.path); - const thisFileSize = thisFile.size; - - expect(thisFile).toHaveLength(thisFileSize); - expect(thisFile).toHaveLength(Bun.file(import.meta.path).size); - - // empty file should have length 0 - writeFileSync("/tmp/empty.txt", ""); - expect(Bun.file("/tmp/empty.txt")).toHaveLength(0); - - // if a file doesn't exist, it should throw (not return 0 size) - expect(() => expect(Bun.file("/does-not-exist/file.txt")).toHaveLength(0)).toThrow(); - - // Blob - expect(new Blob([1, 2, 3])).toHaveLength(3); - expect(new Blob()).toHaveLength(0); - - // Set - expect(new Set()).toHaveLength(0); - expect(new Set([1, 2, 3])).toHaveLength(3); - - // Map - expect(new Map()).toHaveLength(0); - expect(new Map([["a", 1]])).toHaveLength(1); - - // WeakMap - expect(new WeakMap([[globalThis, 1]])).toHaveLength(1); -}); - -test("toContain()", () => { - const s1 = new String("123"); - expect(s1).not.toContain("12"); - const s2 = "123"; - expect(s2).toContain("12"); - - expect("test").toContain("es"); - expect("test").toContain("est"); - // expect("test").not.toContain("test"); - expect(["test", "es"]).toContain("es"); - expect("").toContain(""); - expect([""]).toContain(""); - - expect(["lemon", "lime"]).not.toContain("orange"); - expect("citrus fruits").toContain("fruit"); - - const a = new Uint16Array([1, 2, 3]); - expect(a).toContain(2); - expect(a).not.toContain(4); - expect([2, "2335", 5, true, false, null, undefined]).toContain(5); - expect([2, "2335", 5, true, false, null, undefined]).toContain("2335"); - expect([2, "2335", 5, true, false, null, undefined]).toContain(true); - expect([2, "2335", 5, true, false, null, undefined]).toContain(false); - expect([2, "2335", 5, true, false, null, undefined]).toContain(null); - expect([2, "2335", 5, true, false, null, undefined]).toContain(undefined); - expect([2, "2335", 5, true, false, null, undefined]).not.toContain(3); - expect([2, "2335", 5, true, false, null, undefined]).not.not.not.toContain(3); - - // expect([4, 5, 6]).not.toContain(5); - - expect([]).not.toContain([]); -}); - -test("toBeEven()", () => { - expect(1).not.toBeEven(); - expect(2).toBeEven(); - expect(3).not.toBeEven(); - expect(3.1).not.toBeEven(); - expect(2.1).not.toBeEven(); - expect(4).toBeEven(); - expect(5).not.toBeEven(); - expect(6).toBeEven(); - expect(0).toBeEven(); - expect(-8).toBeEven(); - expect(-0).toBeEven(); - expect(NaN).not.toBeEven(); - expect([]).not.toBeEven(); - expect([1, 2]).not.toBeEven(); - expect({}).not.toBeEven(); - expect(() => {}).not.toBeEven(); - expect("").not.toBeEven(); - expect("string").not.toBeEven(); - expect(undefined).not.toBeEven(); - expect(Math.floor(Date.now() / 1000) * 2).toBeEven(); // Slight fuzz by using timestamp times 2 - expect(Math.floor(Date.now() / 1000) * 4 - 1).not.toBeEven(); - expect(4.0e1).toBeEven(); - expect(6.2e1).toBeEven(); - expect(6.3e1).not.toBeEven(); - expect(6.33e1).not.toBeEven(); - expect(3.3e-1).not.toBeEven(); //throw - expect(0.3).not.toBeEven(); //throw - expect(0.4).not.toBeEven(); - expect(1).not.toBeEven(); - expect(0).toBeEven(); - expect(2.0).toBeEven(); - expect(NaN).not.toBeEven(); - expect(2n).toBeEven(); // BigInt at this time not supported in jest-extended - expect(3n).not.toBeEven(); - expect(9007199254740990).toBeEven(); // manual typical max safe -1 // not int? - expect(9007199254740990n).toBeEven(); // manual typical max safe -1 as bigint - expect(Number.MAX_SAFE_INTEGER - 1).toBeEven(); // not int? - expect(Number.MAX_SAFE_INTEGER).not.toBeEven(); - expect(BigInt(Number.MAX_SAFE_INTEGER) - 1n).toBeEven(); - expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeEven(); - expect(BigInt(Number.MAX_VALUE - 1)).toBeEven(); - expect(Number.MIN_SAFE_INTEGER + 1).toBeEven(); // not int? - expect(Number.MIN_SAFE_INTEGER).not.toBeEven(); - expect(BigInt(Number.MIN_SAFE_INTEGER) + 1n).toBeEven(); - expect(BigInt(Number.MIN_SAFE_INTEGER)).not.toBeEven(); - expect(4 / Number.NEGATIVE_INFINITY).toBeEven(); // as in IEEE-754: + / -inf => neg zero - expect(5 / Number.NEGATIVE_INFINITY).toBeEven(); - expect(-7 / Number.NEGATIVE_INFINITY).toBeEven(); // as in IEEE-754: - / -inf => zero - expect(-8 / Number.NEGATIVE_INFINITY).toBeEven(); - expect(new WebAssembly.Global({ value: "i32", mutable: false }, 4).value).toBeEven(); - expect(new WebAssembly.Global({ value: "i32", mutable: false }, 3).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "i32", mutable: true }, 2).value).toBeEven(); - expect(new WebAssembly.Global({ value: "i32", mutable: true }, 1).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "i64", mutable: true }, -9223372036854775808n).value).toBeEven(); - expect(new WebAssembly.Global({ value: "i64", mutable: false }, -9223372036854775808n).value).toBeEven(); - expect(new WebAssembly.Global({ value: "i64", mutable: true }, 9223372036854775807n).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "i64", mutable: false }, 9223372036854775807n).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "f32", mutable: true }, 42.0).value).toBeEven(); - expect(new WebAssembly.Global({ value: "f32", mutable: false }, 42.0).value).toBeEven(); - expect(new WebAssembly.Global({ value: "f64", mutable: true }, 42.0).value).toBeEven(); - expect(new WebAssembly.Global({ value: "f64", mutable: false }, 42.0).value).toBeEven(); - expect(new WebAssembly.Global({ value: "f32", mutable: true }, 43.0).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "f32", mutable: false }, 43.0).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "f64", mutable: true }, 43.0).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "f64", mutable: false }, 43.0).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "f32", mutable: true }, 4.3).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "f32", mutable: false }, 4.3).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "f64", mutable: true }, 4.3).value).not.toBeEven(); - expect(new WebAssembly.Global({ value: "f64", mutable: false }, 4.3).value).not.toBeEven(); - // did not seem to support SIMD v128 type yet (which is not in W3C specs for JS but is a valid global type) - // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:false}, -170141183460469231731687303715884105728n).value).toBeEven(); - // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:true}, -170141183460469231731687303715884105728n).value).toBeEven(); - // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:true}, 170141183460469231731687303715884105727n).value).not.toBeEven(); - // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:false}, 170141183460469231731687303715884105727n).value).not.toBeEven(); - // FUTURE: with uintv128: expect(new WebAssembly.Global({value:'v128', mutable:false}, 340282366920938463463374607431768211456n).value).toThrow(); -}); - -test("toBeTruthy()", () => { - expect("test").toBeTruthy(); - expect(true).toBeTruthy(); - expect(1).toBeTruthy(); - expect({}).toBeTruthy(); - expect([]).toBeTruthy(); - expect(() => {}).toBeTruthy(); - // expect(() => {}).not.toBeTruthy(); - expect(0.5).toBeTruthy(); - expect(new Map()).toBeTruthy(); - - expect("").not.toBeTruthy(); - expect(0).not.toBeTruthy(); - expect(-0).not.toBeTruthy(); - expect(NaN).not.toBeTruthy(); - expect(0n).not.toBeTruthy(); - expect(0.0e1).not.toBeTruthy(); - expect(false).not.toBeTruthy(); - expect(null).not.toBeTruthy(); - expect(undefined).not.toBeTruthy(); -}); - -test("toBeUndefined()", () => { - expect(undefined).toBeUndefined(); - // expect(undefined).not.toBeUndefined(); - - expect(null).not.toBeUndefined(); - expect(null).not.not.not.toBeUndefined(); - expect(0).not.toBeUndefined(); - expect("hello defined").not.toBeUndefined(); -}); - -test("toBeNaN()", () => { - expect(NaN).toBeNaN(); - // expect(NaN).not.toBeNaN(); - - expect(0).not.toBeNaN(); - expect("hello not NaN").not.toBeNaN(); -}); - -test("toBeNull()", () => { - expect(null).toBeNull(); - // expect(null).not.toBeNull(); - - expect(undefined).not.toBeNull(); - expect(0).not.toBeNull(); - expect("hello not null").not.toBeNull(); -}); - -test("toBeDefined()", () => { - expect(0).toBeDefined(); - expect("hello defined").toBeDefined(); - expect(null).toBeDefined(); - // expect(null).not.toBeDefined(); - - expect(undefined).not.toBeDefined(); -}); - -test("toBeFalsy()", () => { - expect("").toBeFalsy(); - expect(0).toBeFalsy(); - expect(-0).toBeFalsy(); - expect(NaN).toBeFalsy(); - expect(0n).toBeFalsy(); - expect(false).toBeFalsy(); - expect(null).toBeFalsy(); - expect(undefined).toBeFalsy(); - // expect(undefined).not.toBeFalsy(); - - expect("hello not falsy").not.toBeFalsy(); - expect("hello not falsy").not.not.not.toBeFalsy(); - expect(1).not.toBeFalsy(); - expect(true).not.toBeFalsy(); - expect({}).not.toBeFalsy(); - expect([]).not.toBeFalsy(); - expect(() => {}).not.toBeFalsy(); -}); - -test("toBeGreaterThan()", () => { - expect(3n).toBeGreaterThan(2); - expect(Number.MAX_VALUE).not.toBeGreaterThan(Number.MAX_VALUE); - expect(1).not.toBeGreaterThan(BigInt(Number.MAX_VALUE)); - expect(1).not.toBeGreaterThan(Number.MAX_SAFE_INTEGER); - expect(1).not.toBeGreaterThan(BigInt(Number.MAX_SAFE_INTEGER)); - expect(Number.MAX_SAFE_INTEGER).not.toBeGreaterThan(Number.MAX_SAFE_INTEGER); - expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeGreaterThan(BigInt(Number.MAX_SAFE_INTEGER)); - - expect(Infinity).toBeGreaterThan(-Infinity); - expect(-Infinity).not.toBeGreaterThan(Infinity); - - expect(NaN).not.toBeGreaterThan(NaN); - expect(NaN).not.toBeGreaterThan(-Infinity); - - expect(10).toBeGreaterThan(9); - expect(10).not.toBeGreaterThan(10); - expect(10).not.toBeGreaterThan(11); - expect(10).not.toBeGreaterThan(Infinity); - expect(10).toBeGreaterThan(-Infinity); - expect(10).not.toBeGreaterThan(NaN); - expect(10).toBeGreaterThan(0); - expect(10).toBeGreaterThan(-0); - expect(10).toBeGreaterThan(0.1); - expect(10).toBeGreaterThan(-0.1); - expect(10).toBeGreaterThan(0.9); - expect(10).toBeGreaterThan(-0.9); - expect(10).toBeGreaterThan(1); - expect(10).toBeGreaterThan(-1); - // switch the order - expect(9).not.toBeGreaterThan(10); - expect(10).not.toBeGreaterThan(10); - expect(11).toBeGreaterThan(10); - expect(Infinity).toBeGreaterThan(10); - expect(-Infinity).not.toBeGreaterThan(10); - expect(NaN).not.toBeGreaterThan(10); - expect(0).not.toBeGreaterThan(10); - expect(-0).not.toBeGreaterThan(10); - expect(0.1).not.toBeGreaterThan(10); - expect(-0.1).not.toBeGreaterThan(10); - expect(0.9).not.toBeGreaterThan(10); - expect(-0.9).not.toBeGreaterThan(10); - expect(1).not.toBeGreaterThan(10); - expect(-1).not.toBeGreaterThan(10); - - // same tests but use bigints - expect(10n).toBeGreaterThan(9n); - expect(10n).not.toBeGreaterThan(10n); - expect(10n).not.toBeGreaterThan(11n); - expect(10n).not.toBeGreaterThan(Infinity); - expect(10n).toBeGreaterThan(-Infinity); - expect(10n).not.toBeGreaterThan(NaN); - expect(10n).toBeGreaterThan(0n); - expect(10n).toBeGreaterThan(-0n); - expect(10n).toBeGreaterThan(1n); - expect(10n).toBeGreaterThan(-1n); - // switch the order - expect(9n).not.toBeGreaterThan(10n); - expect(10n).not.toBeGreaterThan(10n); - expect(11n).toBeGreaterThan(10n); - expect(Infinity).toBeGreaterThan(10n); - expect(-Infinity).not.toBeGreaterThan(10n); - expect(NaN).not.toBeGreaterThan(10n); - expect(0n).not.toBeGreaterThan(10n); - expect(-0n).not.toBeGreaterThan(10n); - expect(1n).not.toBeGreaterThan(10n); - expect(-1n).not.toBeGreaterThan(10n); - - // use bigints and numbers - expect(10n).toBeGreaterThan(9); - expect(10n).not.toBeGreaterThan(10); - expect(10n).not.toBeGreaterThan(11); - expect(10n).not.toBeGreaterThan(Infinity); - expect(10n).toBeGreaterThan(-Infinity); - expect(10n).not.toBeGreaterThan(NaN); - expect(10n).toBeGreaterThan(0); - expect(10n).toBeGreaterThan(-0); - expect(10n).toBeGreaterThan(0.1); - expect(10n).toBeGreaterThan(-0.1); - expect(10n).toBeGreaterThan(0.9); - expect(10n).toBeGreaterThan(-0.9); - expect(10n).toBeGreaterThan(1); - expect(10n).toBeGreaterThan(-1); - // switch the order - expect(9n).not.toBeGreaterThan(10); - expect(10n).not.toBeGreaterThan(10); - expect(11n).toBeGreaterThan(10); - expect(Infinity).toBeGreaterThan(10n); - expect(-Infinity).not.toBeGreaterThan(10n); - expect(NaN).not.toBeGreaterThan(10n); - expect(0n).not.toBeGreaterThan(10); - expect(-0n).not.toBeGreaterThan(10); - expect(1n).not.toBeGreaterThan(10); - expect(-1n).not.toBeGreaterThan(10); - - expect(1n).not.toBeGreaterThan(1); - expect(1n).not.toBeGreaterThan(Number.MAX_SAFE_INTEGER); - expect(1n).not.toBeGreaterThan(Number.MAX_VALUE); - expect(1).not.toBeGreaterThan(1n); - expect(Number.MAX_SAFE_INTEGER).toBeGreaterThan(1n); - expect(Number.MAX_VALUE).toBeGreaterThan(1n); - - expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeGreaterThan(1n); - expect(BigInt(Number.MAX_VALUE)).toBeGreaterThan(1n); - expect(1n).not.toBeGreaterThan(BigInt(Number.MAX_SAFE_INTEGER)); - expect(1n).not.toBeGreaterThan(BigInt(Number.MAX_VALUE)); - - expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeGreaterThan(1); - expect(BigInt(Number.MAX_VALUE)).toBeGreaterThan(1); - expect(1).not.toBeGreaterThan(BigInt(Number.MAX_SAFE_INTEGER)); -}); - -test("toBeGreaterThanOrEqual()", () => { - expect(Number.MAX_VALUE).toBeGreaterThanOrEqual(Number.MAX_VALUE); - expect(1).not.toBeGreaterThanOrEqual(Number.MAX_SAFE_INTEGER); - expect(1).not.toBeGreaterThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); - expect(1).not.toBeGreaterThanOrEqual(BigInt(Number.MAX_VALUE)); - expect(Number.MAX_SAFE_INTEGER).toBeGreaterThanOrEqual(Number.MAX_SAFE_INTEGER); - expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeGreaterThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); - - expect(Infinity).toBeGreaterThanOrEqual(-Infinity); - expect(-Infinity).not.toBeGreaterThanOrEqual(Infinity); - - expect(NaN).not.toBeGreaterThanOrEqual(NaN); - expect(NaN).not.toBeGreaterThanOrEqual(-Infinity); - - expect(10).toBeGreaterThanOrEqual(9); - expect(10).toBeGreaterThanOrEqual(10); - expect(10).not.toBeGreaterThanOrEqual(11); - expect(10).not.toBeGreaterThanOrEqual(Infinity); - expect(10).toBeGreaterThanOrEqual(-Infinity); - expect(10).not.toBeGreaterThanOrEqual(NaN); - expect(10).toBeGreaterThanOrEqual(0); - expect(10).toBeGreaterThanOrEqual(-0); - expect(10).toBeGreaterThanOrEqual(0.1); - expect(10).toBeGreaterThanOrEqual(-0.1); - expect(10).toBeGreaterThanOrEqual(0.9); - expect(10).toBeGreaterThanOrEqual(-0.9); - expect(10).toBeGreaterThanOrEqual(1); - expect(10).toBeGreaterThanOrEqual(-1); - // switch the order - expect(9).not.toBeGreaterThanOrEqual(10); - expect(10).toBeGreaterThanOrEqual(10); - expect(11).toBeGreaterThanOrEqual(10); - expect(Infinity).toBeGreaterThanOrEqual(10); - expect(-Infinity).not.toBeGreaterThanOrEqual(10); - expect(NaN).not.toBeGreaterThanOrEqual(10); - expect(0).not.toBeGreaterThanOrEqual(10); - expect(-0).not.toBeGreaterThanOrEqual(10); - expect(0.1).not.toBeGreaterThanOrEqual(10); - expect(-0.1).not.toBeGreaterThanOrEqual(10); - expect(0.9).not.toBeGreaterThanOrEqual(10); - expect(-0.9).not.toBeGreaterThanOrEqual(10); - expect(1).not.toBeGreaterThanOrEqual(10); - expect(-1).not.toBeGreaterThanOrEqual(10); - - // same tests but use bigints - expect(10n).toBeGreaterThanOrEqual(9n); - expect(10n).toBeGreaterThanOrEqual(10n); - expect(10n).not.toBeGreaterThanOrEqual(11n); - expect(10n).not.toBeGreaterThanOrEqual(Infinity); - expect(10n).toBeGreaterThanOrEqual(-Infinity); - expect(10n).not.toBeGreaterThanOrEqual(NaN); - expect(10n).toBeGreaterThanOrEqual(0n); - expect(10n).toBeGreaterThanOrEqual(-0n); - expect(10n).toBeGreaterThanOrEqual(1n); - expect(10n).toBeGreaterThanOrEqual(-1n); - // switch the order - expect(9n).not.toBeGreaterThanOrEqual(10n); - expect(10n).toBeGreaterThanOrEqual(10n); - expect(11n).toBeGreaterThanOrEqual(10n); - expect(Infinity).toBeGreaterThanOrEqual(10n); - expect(-Infinity).not.toBeGreaterThanOrEqual(10n); - expect(NaN).not.toBeGreaterThanOrEqual(10n); - expect(0n).not.toBeGreaterThanOrEqual(10n); - expect(-0n).not.toBeGreaterThanOrEqual(10n); - expect(1n).not.toBeGreaterThanOrEqual(10n); - expect(-1n).not.toBeGreaterThanOrEqual(10n); - - // use bigints and numbers - expect(10n).toBeGreaterThanOrEqual(9); - expect(10n).toBeGreaterThanOrEqual(10); - expect(10n).not.toBeGreaterThanOrEqual(11); - expect(10n).not.toBeGreaterThanOrEqual(Infinity); - expect(10n).toBeGreaterThanOrEqual(-Infinity); - expect(10n).not.toBeGreaterThanOrEqual(NaN); - expect(10n).toBeGreaterThanOrEqual(0); - expect(10n).toBeGreaterThanOrEqual(-0); - expect(10n).toBeGreaterThanOrEqual(0.1); - expect(10n).toBeGreaterThanOrEqual(-0.1); - expect(10n).toBeGreaterThanOrEqual(0.9); - expect(10n).toBeGreaterThanOrEqual(-0.9); - expect(10n).toBeGreaterThanOrEqual(1); - expect(10n).toBeGreaterThanOrEqual(-1); - // switch the order - expect(9n).not.toBeGreaterThanOrEqual(10); - expect(10n).toBeGreaterThanOrEqual(10); - expect(11n).toBeGreaterThanOrEqual(10); - expect(Infinity).toBeGreaterThanOrEqual(10n); - expect(-Infinity).not.toBeGreaterThanOrEqual(10n); - expect(NaN).not.toBeGreaterThanOrEqual(10n); - expect(0n).not.toBeGreaterThanOrEqual(10); - expect(-0n).not.toBeGreaterThanOrEqual(10); - expect(1n).not.toBeGreaterThanOrEqual(10); - expect(-1n).not.toBeGreaterThanOrEqual(10); - - expect(1n).toBeGreaterThanOrEqual(1); - expect(1n).not.toBeGreaterThanOrEqual(Number.MAX_SAFE_INTEGER); - expect(1n).not.toBeGreaterThanOrEqual(Number.MAX_VALUE); - expect(1).toBeGreaterThanOrEqual(1n); - expect(Number.MAX_SAFE_INTEGER).toBeGreaterThanOrEqual(1n); - expect(Number.MAX_VALUE).toBeGreaterThanOrEqual(1n); - - expect(1).not.toBeGreaterThanOrEqual(BigInt(Number.MAX_VALUE)); -}); - -test("toBeLessThan()", () => { - expect(3n).not.toBeLessThan(2); - expect(Number.MAX_VALUE).not.toBeLessThan(Number.MAX_VALUE); - expect(1).toBeLessThan(BigInt(Number.MAX_VALUE)); - expect(1).toBeLessThan(Number.MAX_SAFE_INTEGER); - expect(1).toBeLessThan(BigInt(Number.MAX_SAFE_INTEGER)); - expect(Number.MAX_SAFE_INTEGER).not.toBeLessThan(Number.MAX_SAFE_INTEGER); - expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThan(BigInt(Number.MAX_SAFE_INTEGER)); - - expect(Number.MAX_VALUE).not.toBeLessThan(BigInt(Number.MAX_VALUE)); - - expect(NaN).not.toBeLessThan(NaN); - expect(NaN).not.toBeLessThan(-Infinity); - - expect(10).not.toBeLessThan(9); - expect(10).not.toBeLessThan(10); - expect(10).toBeLessThan(11); - expect(10).toBeLessThan(Infinity); - expect(10).not.toBeLessThan(-Infinity); - expect(10).not.toBeLessThan(NaN); - expect(10).not.toBeLessThan(0); - expect(10).not.toBeLessThan(-0); - expect(10).not.toBeLessThan(0.1); - expect(10).not.toBeLessThan(-0.1); - expect(10).not.toBeLessThan(0.9); - expect(10).not.toBeLessThan(-0.9); - expect(10).not.toBeLessThan(1); - expect(10).not.toBeLessThan(-1); - // switch the order - expect(9).toBeLessThan(10); - expect(10).not.toBeLessThan(10); - expect(11).not.toBeLessThan(10); - expect(Infinity).not.toBeLessThan(10); - expect(-Infinity).toBeLessThan(10); - expect(NaN).not.toBeLessThan(10); - expect(0).toBeLessThan(10); - expect(-0).toBeLessThan(10); - expect(0.1).toBeLessThan(10); - expect(-0.1).toBeLessThan(10); - expect(0.9).toBeLessThan(10); - expect(-0.9).toBeLessThan(10); - expect(1).toBeLessThan(10); - expect(-1).toBeLessThan(10); - - // same tests but use bigints - expect(10n).not.toBeLessThan(9n); - expect(10n).not.toBeLessThan(10n); - expect(10n).toBeLessThan(11n); - expect(10n).toBeLessThan(Infinity); - expect(10n).not.toBeLessThan(-Infinity); - expect(10n).not.toBeLessThan(NaN); - expect(10n).not.toBeLessThan(0n); - expect(10n).not.toBeLessThan(-0n); - expect(10n).not.toBeLessThan(1n); - expect(10n).not.toBeLessThan(-1n); - // switch the order - expect(9n).toBeLessThan(10n); - expect(10n).not.toBeLessThan(10n); - expect(11n).not.toBeLessThan(10n); - expect(Infinity).not.toBeLessThan(10n); - expect(-Infinity).toBeLessThan(10n); - expect(NaN).not.toBeLessThan(10n); - expect(0n).toBeLessThan(10n); - expect(-0n).toBeLessThan(10n); - expect(1n).toBeLessThan(10n); - expect(-1n).toBeLessThan(10n); - - // use bigints and numbers - expect(10n).not.toBeLessThan(9); - expect(10n).not.toBeLessThan(10); - expect(10n).toBeLessThan(11); - expect(10n).toBeLessThan(Infinity); - expect(10n).not.toBeLessThan(-Infinity); - expect(10n).not.toBeLessThan(NaN); - expect(10n).not.toBeLessThan(0); - expect(10n).not.toBeLessThan(-0); - expect(10n).not.toBeLessThan(0.1); - expect(10n).not.toBeLessThan(-0.1); - expect(10n).not.toBeLessThan(0.9); - expect(10n).not.toBeLessThan(-0.9); - expect(10n).not.toBeLessThan(1); - expect(10n).not.toBeLessThan(-1); - // switch the order - expect(9n).toBeLessThan(10); - expect(10n).not.toBeLessThan(10); - expect(11n).not.toBeLessThan(10); - expect(Infinity).not.toBeLessThan(10n); - expect(-Infinity).toBeLessThan(10n); - expect(NaN).not.toBeLessThan(10n); - expect(0n).toBeLessThan(10); - expect(-0n).toBeLessThan(10); - expect(1n).toBeLessThan(10); - expect(-1n).toBeLessThan(10); - - expect(1n).not.toBeLessThan(1); - expect(1n).toBeLessThan(Number.MAX_SAFE_INTEGER); - expect(1n).toBeLessThan(Number.MAX_VALUE); - expect(1).not.toBeLessThan(1n); - expect(Number.MAX_SAFE_INTEGER).not.toBeLessThan(1n); - expect(Number.MAX_VALUE).not.toBeLessThan(1n); - - expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThan(1n); - expect(BigInt(Number.MAX_VALUE)).not.toBeLessThan(1n); - expect(1n).toBeLessThan(BigInt(Number.MAX_SAFE_INTEGER)); - expect(1n).toBeLessThan(BigInt(Number.MAX_VALUE)); - - expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThan(1); - expect(BigInt(Number.MAX_VALUE)).not.toBeLessThan(1); - expect(1).toBeLessThan(BigInt(Number.MAX_SAFE_INTEGER)); -}); - -test("toBeLessThanOrEqual()", () => { - expect(3n).not.toBeLessThanOrEqual(2); - expect(Number.MAX_VALUE).toBeLessThanOrEqual(Number.MAX_VALUE); - expect(1).toBeLessThanOrEqual(BigInt(Number.MAX_VALUE)); - expect(1).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER); - expect(1).toBeLessThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); - expect(Number.MAX_SAFE_INTEGER).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER); - expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeLessThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); - - expect(Number.MAX_VALUE).toBeLessThanOrEqual(BigInt(Number.MAX_VALUE)); - expect(BigInt(Number.MAX_VALUE)).toBeLessThanOrEqual(Number.MAX_VALUE); - - expect(NaN).not.toBeLessThanOrEqual(NaN); - expect(NaN).not.toBeLessThanOrEqual(-Infinity); - - expect(10).not.toBeLessThanOrEqual(9); - expect(10).toBeLessThanOrEqual(10); - expect(10).toBeLessThanOrEqual(11); - expect(10).toBeLessThanOrEqual(Infinity); - expect(10).not.toBeLessThanOrEqual(-Infinity); - expect(10).not.toBeLessThanOrEqual(NaN); - expect(10).not.toBeLessThanOrEqual(0); - expect(10).not.toBeLessThanOrEqual(-0); - expect(10).not.toBeLessThanOrEqual(0.1); - expect(10).not.toBeLessThanOrEqual(-0.1); - expect(10).not.toBeLessThanOrEqual(0.9); - expect(10).not.toBeLessThanOrEqual(-0.9); - expect(10).not.toBeLessThanOrEqual(1); - expect(10).not.toBeLessThanOrEqual(-1); - // switch the order - expect(9).toBeLessThanOrEqual(10); - expect(10).toBeLessThanOrEqual(10); - expect(11).not.toBeLessThanOrEqual(10); - expect(Infinity).not.toBeLessThanOrEqual(10); - expect(-Infinity).toBeLessThanOrEqual(10); - expect(NaN).not.toBeLessThanOrEqual(10); - expect(0).toBeLessThanOrEqual(10); - expect(-0).toBeLessThanOrEqual(10); - expect(0.1).toBeLessThanOrEqual(10); - expect(-0.1).toBeLessThanOrEqual(10); - expect(0.9).toBeLessThanOrEqual(10); - expect(-0.9).toBeLessThanOrEqual(10); - expect(1).toBeLessThanOrEqual(10); - expect(-1).toBeLessThanOrEqual(10); - - // same tests but use bigints - expect(10n).not.toBeLessThanOrEqual(9n); - expect(10n).toBeLessThanOrEqual(10n); - expect(10n).toBeLessThanOrEqual(11n); - expect(10n).toBeLessThanOrEqual(Infinity); - expect(10n).not.toBeLessThanOrEqual(-Infinity); - expect(10n).not.toBeLessThanOrEqual(NaN); - expect(10n).not.toBeLessThanOrEqual(0n); - expect(10n).not.toBeLessThanOrEqual(-0n); - expect(10n).not.toBeLessThanOrEqual(1n); - expect(10n).not.toBeLessThanOrEqual(-1n); - // switch the order - expect(9n).toBeLessThanOrEqual(10n); - expect(10n).toBeLessThanOrEqual(10n); - expect(11n).not.toBeLessThanOrEqual(10n); - expect(Infinity).not.toBeLessThanOrEqual(10n); - expect(-Infinity).toBeLessThanOrEqual(10n); - expect(NaN).not.toBeLessThanOrEqual(10n); - expect(0n).toBeLessThanOrEqual(10n); - expect(-0n).toBeLessThanOrEqual(10n); - expect(1n).toBeLessThanOrEqual(10n); - expect(-1n).toBeLessThanOrEqual(10n); - - // use bigints and numbers - expect(10n).not.toBeLessThanOrEqual(9); - expect(10n).toBeLessThanOrEqual(10); - expect(10n).toBeLessThanOrEqual(11); - expect(10n).toBeLessThanOrEqual(Infinity); - expect(10n).not.toBeLessThanOrEqual(-Infinity); - expect(10n).not.toBeLessThanOrEqual(NaN); - expect(10n).not.toBeLessThanOrEqual(0); - expect(10n).not.toBeLessThanOrEqual(-0); - expect(10n).not.toBeLessThanOrEqual(0.1); - expect(10n).not.toBeLessThanOrEqual(-0.1); - expect(10n).not.toBeLessThanOrEqual(0.9); - expect(10n).not.toBeLessThanOrEqual(-0.9); - expect(10n).not.toBeLessThanOrEqual(1); - expect(10n).not.toBeLessThanOrEqual(-1); - // switch the order - expect(9n).toBeLessThanOrEqual(10); - expect(10n).toBeLessThanOrEqual(10); - expect(11n).not.toBeLessThanOrEqual(10); - expect(Infinity).not.toBeLessThanOrEqual(10n); - expect(-Infinity).toBeLessThanOrEqual(10n); - expect(NaN).not.toBeLessThanOrEqual(10n); - expect(0n).toBeLessThanOrEqual(10); - expect(-0n).toBeLessThanOrEqual(10); - expect(1n).toBeLessThanOrEqual(10); - expect(-1n).toBeLessThanOrEqual(10); - - expect(1n).toBeLessThanOrEqual(1); - expect(1n).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER); - expect(1n).toBeLessThanOrEqual(Number.MAX_VALUE); - expect(1).toBeLessThanOrEqual(1n); - expect(Number.MAX_SAFE_INTEGER).not.toBeLessThanOrEqual(1n); - expect(Number.MAX_VALUE).not.toBeLessThanOrEqual(1n); - - expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThanOrEqual(1n); - expect(BigInt(Number.MAX_VALUE)).not.toBeLessThanOrEqual(1n); - expect(1n).toBeLessThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); - expect(1n).toBeLessThanOrEqual(BigInt(Number.MAX_VALUE)); - - expect(BigInt(Number.MAX_SAFE_INTEGER)).not.toBeLessThanOrEqual(1); - expect(BigInt(Number.MAX_VALUE)).not.toBeLessThanOrEqual(1); - expect(1).toBeLessThanOrEqual(BigInt(Number.MAX_SAFE_INTEGER)); -}); - -test("toBeOdd()", () => { - expect(1).toBeOdd(); - expect(2).not.toBeOdd(); - expect(3).toBeOdd(); - expect(3.1).not.toBeOdd(); - expect(2.1).not.toBeOdd(); - expect(4).not.toBeOdd(); - expect(5).toBeOdd(); - expect(6).not.toBeOdd(); - expect(0).not.toBeOdd(); - expect(-8).not.toBeOdd(); - expect(-0).not.toBeOdd(); - expect(NaN).not.toBeOdd(); - expect([]).not.toBeOdd(); - // SHOULD FAIL: expect([]).toBeOdd(); - expect([1, 2]).not.toBeOdd(); - expect({}).not.toBeOdd(); - expect(() => {}).not.toBeOdd(); - expect("").not.toBeOdd(); - expect("string").not.toBeOdd(); - expect(undefined).not.toBeOdd(); - expect(Math.floor(Date.now() / 1000) * 2 - 1).toBeOdd(); // Slight fuzz by using timestamp times 2 - expect(Math.floor(Date.now() / 1000) * 4 - 1).toBeOdd(); - expect(4.0e1).not.toBeOdd(); - expect(6.2e1).not.toBeOdd(); - expect(6.3e1).toBeOdd(); - expect(6.33e1).not.toBeOdd(); - expect(3.2e-3).not.toBeOdd(); - expect(0.3).not.toBeOdd(); - expect(0.4).not.toBeOdd(); - expect(1).toBeOdd(); - expect(0).not.toBeOdd(); - expect(2.0).not.toBeOdd(); - expect(NaN).not.toBeOdd(); - expect(2n).not.toBeOdd(); // BigInt at this time not supported in jest-extended - expect(3n).toBeOdd(); - expect(9007199254740990).not.toBeOdd(); // manual typical max safe -1 - expect(9007199254740991).toBeOdd(); - expect(9007199254740990n).not.toBeOdd(); // manual typical max safe -1 as bigint - expect(9007199254740991n).toBeOdd(); - expect(Number.MAX_SAFE_INTEGER - 1).not.toBeOdd(); - expect(Number.MAX_SAFE_INTEGER).toBeOdd(); - expect(BigInt(Number.MAX_SAFE_INTEGER) - 1n).not.toBeOdd(); - expect(BigInt(Number.MAX_SAFE_INTEGER)).toBeOdd(); - expect(Number.MIN_SAFE_INTEGER + 1).not.toBeOdd(); - expect(Number.MIN_SAFE_INTEGER).toBeOdd(); - expect(BigInt(Number.MIN_SAFE_INTEGER) + 1n).not.toBeOdd(); - expect(BigInt(Number.MIN_SAFE_INTEGER)).toBeOdd(); - expect(4 / Number.NEGATIVE_INFINITY).not.toBeOdd(); // in IEEE-754: + / -inf => neg zero - expect(5 / Number.NEGATIVE_INFINITY).not.toBeOdd(); - expect(-7 / Number.NEGATIVE_INFINITY).not.toBeOdd(); // in IEEE-754: - / -inf => zero - expect(-8 / Number.NEGATIVE_INFINITY).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "i32", mutable: false }, 4).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "i32", mutable: false }, 3).value).toBeOdd(); - expect(new WebAssembly.Global({ value: "i32", mutable: true }, 2).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "i32", mutable: true }, 1).value).toBeOdd(); - expect(new WebAssembly.Global({ value: "i64", mutable: true }, -9223372036854775808n).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "i64", mutable: false }, -9223372036854775808n).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "i64", mutable: true }, 9223372036854775807n).value).toBeOdd(); - expect(new WebAssembly.Global({ value: "i64", mutable: false }, 9223372036854775807n).value).toBeOdd(); - expect(new WebAssembly.Global({ value: "f32", mutable: true }, 42.0).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "f32", mutable: false }, 42.0).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "f64", mutable: true }, 42.0).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "f64", mutable: false }, 42.0).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "f32", mutable: true }, 43.0).value).toBeOdd(); - expect(new WebAssembly.Global({ value: "f32", mutable: false }, 43.0).value).toBeOdd(); - expect(new WebAssembly.Global({ value: "f64", mutable: true }, 43.0).value).toBeOdd(); - expect(new WebAssembly.Global({ value: "f64", mutable: false }, 43.0).value).toBeOdd(); - expect(new WebAssembly.Global({ value: "f32", mutable: true }, 4.3).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "f32", mutable: false }, 4.3).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "f64", mutable: true }, 4.3).value).not.toBeOdd(); - expect(new WebAssembly.Global({ value: "f64", mutable: false }, 4.3).value).not.toBeOdd(); - // did not seem to support SIMD v128 type yet - // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:false}, 42).value).not.toBeOdd(); - // FUTURE: expect(new WebAssembly.Global({value:'v128', mutable:true}, 42).value).not.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) {} diff --git a/test/package.json b/test/package.json index 7722e0888..a9b8db913 100644 --- a/test/package.json +++ b/test/package.json @@ -4,6 +4,7 @@ "@types/dedent": "^0.7.0" }, "dependencies": { + "@prisma/client": "4.15.0", "@swc/core": "^1.3.38", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", @@ -13,15 +14,15 @@ "esbuild": "^0.17.16", "express": "^4.18.2", "iconv-lite": "^0.6.3", + "jest-extended": "^4.0.0", "lodash": "^4.17.21", - "svelte": "^3.55.1", - "typescript": "^5.0.2", - "undici": "^5.20.0", + "prisma": "4.15.0", "socket.io": "^4.6.1", "socket.io-client": "^4.6.1", "supertest": "^6.1.6", - "@prisma/client": "4.15.0", - "prisma": "4.15.0" + "svelte": "^3.55.1", + "typescript": "^5.0.2", + "undici": "^5.20.0" }, "private": true, "scripts": { diff --git a/test/snippets/package-json-exports/_node_modules_copy/exact/package.json b/test/snippets/package-json-exports/_node_modules_copy/exact/package.json index e69de29bb..0967ef424 100644 --- a/test/snippets/package-json-exports/_node_modules_copy/exact/package.json +++ b/test/snippets/package-json-exports/_node_modules_copy/exact/package.json @@ -0,0 +1 @@ +{} |