diff --git a/.changeset/forty-lizards-tap.md b/.changeset/forty-lizards-tap.md
new file mode 100644
index 0000000000..77d40f02b8
--- /dev/null
+++ b/.changeset/forty-lizards-tap.md
@@ -0,0 +1,5 @@
+---
+"effect": patch
+---
+
+Call Equal.equals internally in order inputs were passed.
diff --git a/.changeset/shiny-crabs-bake.md b/.changeset/shiny-crabs-bake.md
new file mode 100644
index 0000000000..ae53c02401
--- /dev/null
+++ b/.changeset/shiny-crabs-bake.md
@@ -0,0 +1,5 @@
+---
+"@effect/vitest": patch
+---
+
+Fix usage of `toMatchObject`.
diff --git a/packages/effect/src/Equal.ts b/packages/effect/src/Equal.ts
index 5523b1936b..83cb57b7ca 100644
--- a/packages/effect/src/Equal.ts
+++ b/packages/effect/src/Equal.ts
@@ -44,7 +44,13 @@ function compareBoth(self: unknown, that: unknown): boolean {
if (selfType === "object" || selfType === "function") {
if (self !== null && that !== null) {
if (isEqual(self) && isEqual(that)) {
- return Hash.hash(self) === Hash.hash(that) && self[symbol](that)
+ if (Hash.hash(self) === Hash.hash(that) && self[symbol](that)) {
+ return true
+ } else {
+ return structuralRegionState.enabled && structuralRegionState.tester
+ ? structuralRegionState.tester(self, that)
+ : false
+ }
}
}
if (structuralRegionState.enabled) {
diff --git a/packages/effect/src/Hash.ts b/packages/effect/src/Hash.ts
index f940ef936e..6dd8b3f10e 100644
--- a/packages/effect/src/Hash.ts
+++ b/packages/effect/src/Hash.ts
@@ -36,6 +36,10 @@ export interface Hash {
* @category hashing
*/
export const hash: (self: A) => number = (self: A) => {
+ if (structuralRegionState.enabled === true) {
+ return 0
+ }
+
switch (typeof self) {
case "number":
return number(self)
@@ -72,9 +76,6 @@ export const hash: (self: A) => number = (self: A) => {
* @category hashing
*/
export const random: (self: A) => number = (self) => {
- if (structuralRegionState.enabled === true) {
- return 0
- }
if (!randomHashCache.has(self)) {
randomHashCache.set(self, number(pcgr.integer(Number.MAX_SAFE_INTEGER)))
}
@@ -171,36 +172,23 @@ export const cached: {
if (arguments.length === 1) {
const self = arguments[0] as object
return function(hash: number) {
- // @ts-expect-error
- const original = self[symbol].bind(self)
- if (structuralRegionState.enabled === false) {
- Object.defineProperty(self, symbol, {
- value() {
- if (structuralRegionState.enabled === true) {
- return original()
- }
- return hash
- },
- enumerable: false
- })
- }
+ Object.defineProperty(self, symbol, {
+ value() {
+ return hash
+ },
+ enumerable: false
+ })
return hash
} as any
}
const self = arguments[0] as object
const hash = arguments[1] as number
- // @ts-expect-error
- const original = self[symbol].bind(self)
- if (structuralRegionState.enabled === false) {
- Object.defineProperty(self, symbol, {
- value() {
- if (structuralRegionState.enabled === true) {
- return original()
- }
- return hash
- },
- enumerable: false
- })
- }
+ Object.defineProperty(self, symbol, {
+ value() {
+ return hash
+ },
+ enumerable: false
+ })
+
return hash
}
diff --git a/packages/effect/src/internal/either.ts b/packages/effect/src/internal/either.ts
index 9bde0e76db..6ef2f5f55f 100644
--- a/packages/effect/src/internal/either.ts
+++ b/packages/effect/src/internal/either.ts
@@ -34,7 +34,7 @@ const RightProto = Object.assign(Object.create(CommonProto), {
_tag: "Right",
_op: "Right",
[Equal.symbol](this: Either.Right, that: unknown): boolean {
- return isEither(that) && isRight(that) && Equal.equals(that.right, this.right)
+ return isEither(that) && isRight(that) && Equal.equals(this.right, that.right)
},
[Hash.symbol](this: Either.Right) {
return Hash.combine(Hash.hash(this._tag))(Hash.hash(this.right))
@@ -52,7 +52,7 @@ const LeftProto = Object.assign(Object.create(CommonProto), {
_tag: "Left",
_op: "Left",
[Equal.symbol](this: Either.Left, that: unknown): boolean {
- return isEither(that) && isLeft(that) && Equal.equals(that.left, this.left)
+ return isEither(that) && isLeft(that) && Equal.equals(this.left, that.left)
},
[Hash.symbol](this: Either.Left) {
return Hash.combine(Hash.hash(this._tag))(Hash.hash(this.left))
diff --git a/packages/effect/src/internal/option.ts b/packages/effect/src/internal/option.ts
index 68618ef800..7d97adee2c 100644
--- a/packages/effect/src/internal/option.ts
+++ b/packages/effect/src/internal/option.ts
@@ -28,7 +28,7 @@ const SomeProto = Object.assign(Object.create(CommonProto), {
_tag: "Some",
_op: "Some",
[Equal.symbol](this: Option.Some, that: unknown): boolean {
- return isOption(that) && isSome(that) && Equal.equals(that.value, this.value)
+ return isOption(that) && isSome(that) && Equal.equals(this.value, that.value)
},
[Hash.symbol](this: Option.Some) {
return Hash.cached(this, Hash.combine(Hash.hash(this._tag))(Hash.hash(this.value)))
diff --git a/packages/effect/test/Either.test.ts b/packages/effect/test/Either.test.ts
index adb3f4d617..5727792536 100644
--- a/packages/effect/test/Either.test.ts
+++ b/packages/effect/test/Either.test.ts
@@ -326,16 +326,6 @@ describe("Either", () => {
Util.deepStrictEqual(pipe(Either.left("a"), Either.orElse(() => Either.left("b"))), Either.left("b"))
})
- it("vitest equality", () => {
- expect(Either.right(1)).toStrictEqual(Either.right(1))
- expect(Either.left(1)).toStrictEqual(Either.left(1))
-
- expect(Either.right(2)).not.toStrictEqual(Either.right(1))
- expect(Either.left(2)).not.toStrictEqual(Either.left(1))
- expect(Either.left(1)).not.toStrictEqual(Either.right(1))
- expect(Either.left(1)).not.toStrictEqual(Either.right(2))
- })
-
describe("do notation", () => {
it("Do", () => {
expectRight(Either.Do, {})
diff --git a/packages/effect/test/Exit.test.ts b/packages/effect/test/Exit.test.ts
index 8034358b6f..6f2384dc3c 100644
--- a/packages/effect/test/Exit.test.ts
+++ b/packages/effect/test/Exit.test.ts
@@ -1,4 +1,3 @@
-import * as Cause from "effect/Cause"
import * as Exit from "effect/Exit"
import { describe, expect, it } from "vitest"
@@ -74,18 +73,4 @@ describe("Exit", () => {
}`)
})
})
-
- it("vitest equality", () => {
- expect(Exit.succeed(1)).toEqual(Exit.succeed(1))
- expect(Exit.fail("failure")).toEqual(Exit.fail("failure"))
- expect(Exit.die("defect")).toEqual(Exit.die("defect"))
-
- expect(Exit.succeed(1)).not.toEqual(Exit.succeed(2))
- expect(Exit.fail("failure")).not.toEqual(Exit.fail("failure1"))
- expect(Exit.die("failure")).not.toEqual(Exit.fail("failure1"))
- expect(Exit.die("failure")).not.toEqual(Exit.fail("failure1"))
- expect(Exit.failCause(Cause.sequential(Cause.fail("f1"), Cause.fail("f2")))).not.toEqual(
- Exit.failCause(Cause.sequential(Cause.fail("f1"), Cause.fail("f3")))
- )
- })
})
diff --git a/packages/effect/test/Option.test.ts b/packages/effect/test/Option.test.ts
index 949ef69448..b8cbfc9f0b 100644
--- a/packages/effect/test/Option.test.ts
+++ b/packages/effect/test/Option.test.ts
@@ -499,14 +499,6 @@ describe("Option", () => {
expect(f(Option.some(1), Option.some(2))).toStrictEqual(Option.some(3))
})
- it("vitest equality", () => {
- expect(Option.some(2)).toStrictEqual(Option.some(2))
- expect(Option.none()).toStrictEqual(Option.none())
-
- expect(Option.some(2)).not.toStrictEqual(Option.some(1))
- expect(Option.none()).not.toStrictEqual(Option.some(1))
- })
-
describe("do notation", () => {
it("Do", () => {
expectSome(Option.Do, {})
diff --git a/packages/schema/test/Schema/Class/Class.test.ts b/packages/schema/test/Schema/Class/Class.test.ts
index 0876df05f6..22e154e58a 100644
--- a/packages/schema/test/Schema/Class/Class.test.ts
+++ b/packages/schema/test/Schema/Class/Class.test.ts
@@ -49,7 +49,7 @@ describe("Class", () => {
const schema = S.suspend(() => string)
class A extends S.Class("A")({ a: S.optional(schema) }) {}
const string = S.String
- await Util.expectDecodeUnknownSuccess(A, { a: "a" })
+ await Util.expectDecodeUnknownSuccess(A, new A({ a: "a" }))
})
it("should be a Schema", () => {
diff --git a/packages/schema/test/Schema/Class/extend.test.ts b/packages/schema/test/Schema/Class/extend.test.ts
index a3e3dd9dd4..22c259654a 100644
--- a/packages/schema/test/Schema/Class/extend.test.ts
+++ b/packages/schema/test/Schema/Class/extend.test.ts
@@ -80,7 +80,7 @@ describe("extend", () => {
S.Struct(fields).pipe(S.filter(({ a, b }) => a === b ? undefined : "a should be equal to b"))
) {}
Util.expectFields(A.fields, { ...baseFields, ...fields })
- await Util.expectDecodeUnknownSuccess(A, { base: "base", a: 1, b: 1 })
+ await Util.expectDecodeUnknownSuccess(A, new A({ base: "base", a: 1, b: 1 }))
await Util.expectDecodeUnknownFailure(
A,
{ base: "base", a: 1, b: 2 },
diff --git a/packages/vitest/src/index.ts b/packages/vitest/src/index.ts
index c1a4a5570b..562ba6f4e8 100644
--- a/packages/vitest/src/index.ts
+++ b/packages/vitest/src/index.ts
@@ -1,7 +1,7 @@
/**
* @since 1.0.0
*/
-import type { TesterContext } from "@vitest/expect"
+import type { Tester, TesterContext } from "@vitest/expect"
import * as Duration from "effect/Duration"
import * as Effect from "effect/Effect"
import * as Equal from "effect/Equal"
@@ -24,9 +24,16 @@ export type API = TestAPI<{}>
const TestEnv = TestEnvironment.TestContext.pipe(
Layer.provide(Logger.remove(Logger.defaultLogger))
)
+
/** @internal */
-function customTester(this: TesterContext, a: unknown, b: unknown) {
- return Utils.structuralRegion(() => Equal.equals(a, b), (x, y) => this.equals(x, y))
+function customTester(this: TesterContext, a: unknown, b: unknown, customTesters: Array) {
+ if (!Equal.isEqual(a) || !Equal.isEqual(b)) {
+ return undefined
+ }
+ return Utils.structuralRegion(
+ () => Equal.equals(a, b),
+ (x, y) => this.equals(x, y, customTesters.filter((t) => t !== customTester))
+ )
}
/**
diff --git a/packages/vitest/test/equality-tester.test.ts b/packages/vitest/test/equality-tester.test.ts
new file mode 100644
index 0000000000..819e04d449
--- /dev/null
+++ b/packages/vitest/test/equality-tester.test.ts
@@ -0,0 +1,79 @@
+import * as Cause from "effect/Cause"
+import * as Data from "effect/Data"
+import * as Either from "effect/Either"
+import * as Exit from "effect/Exit"
+import * as Option from "effect/Option"
+import { describe, expect, it } from "vitest"
+
+describe("toMatchObject", () => {
+ it("plain objects", () => {
+ expect({ a: 1, b: 2 }).toMatchObject({ a: 1 })
+ })
+
+ it("Data.struct", () => {
+ const alice = Data.struct({ name: "Alice", age: 30 })
+
+ expect(alice).toMatchObject(Data.struct({ name: "Alice" }))
+ })
+
+ it("option", () => {
+ expect(Option.some({ a: 1, b: 2 })).toMatchObject(Option.some({ a: 1 }))
+ expect(Option.none()).toMatchObject(Option.none())
+ expect({ x: Option.some({ a: 1, b: 2 }), y: Option.none() }).toMatchObject({ x: Option.some({ a: 1 }) })
+
+ expect(Option.none()).not.toMatchObject(Option.some({ a: 1 }))
+ expect(Option.some({ b: 1 })).not.toMatchObject(Option.some({ a: 1 }))
+ expect({ x: Option.some({ a: 1, b: 2 }), y: Option.none() }).not.toMatchObject({ x: Option.some({ b: 1 }) })
+ expect({ x: Option.none(), y: Option.none() }).not.toMatchObject({ x: Option.some({}) })
+ })
+
+ it("either", () => {
+ expect(Either.right({ a: 1, b: 2 })).toMatchObject(Either.right({ a: 1 }))
+ expect(Either.left({ a: 1, b: 2 })).toMatchObject(Either.left({ a: 1 }))
+
+ expect(Either.right({ a: 1, b: 2 })).not.toMatchObject(Either.left({ a: 1 }))
+ expect(Either.left({ a: 1, b: 2 })).not.toMatchObject(Either.right({ a: 1 }))
+ })
+
+ it("either", () => {
+ expect(Either.right({ a: 1, b: 2 })).toMatchObject(Either.right({ a: 1 }))
+ expect(Either.left({ a: 1, b: 2 })).toMatchObject(Either.left({ a: 1 }))
+
+ expect(Either.right({ a: 1, b: 2 })).not.toMatchObject(Either.left({ a: 1 }))
+ expect(Either.left({ a: 1, b: 2 })).not.toMatchObject(Either.right({ a: 1 }))
+ })
+})
+
+describe.each(["toStrictEqual", "toEqual"] as const)("%s", (matcher) => {
+ it("either", () => {
+ expect(Either.right(1))[matcher](Either.right(1))
+ expect(Either.left(1))[matcher](Either.left(1))
+
+ expect(Either.right(2)).not[matcher](Either.right(1))
+ expect(Either.left(2)).not[matcher](Either.left(1))
+ expect(Either.left(1)).not[matcher](Either.right(1))
+ expect(Either.left(1)).not[matcher](Either.right(2))
+ })
+
+ it("exit", () => {
+ expect(Exit.succeed(1))[matcher](Exit.succeed(1))
+ expect(Exit.fail("failure"))[matcher](Exit.fail("failure"))
+ expect(Exit.die("defect"))[matcher](Exit.die("defect"))
+
+ expect(Exit.succeed(1)).not[matcher](Exit.succeed(2))
+ expect(Exit.fail("failure")).not[matcher](Exit.fail("failure1"))
+ expect(Exit.die("failure")).not[matcher](Exit.fail("failure1"))
+ expect(Exit.die("failure")).not[matcher](Exit.fail("failure1"))
+ expect(Exit.failCause(Cause.sequential(Cause.fail("f1"), Cause.fail("f2")))).not[matcher](
+ Exit.failCause(Cause.sequential(Cause.fail("f1"), Cause.fail("f3")))
+ )
+ })
+
+ it("option", () => {
+ expect(Option.some(2))[matcher](Option.some(2))
+ expect(Option.none())[matcher](Option.none())
+
+ expect(Option.some(2)).not[matcher](Option.some(1))
+ expect(Option.none()).not[matcher](Option.some(1))
+ })
+})