From e7840f932e02996290af1c6ddb92d03ea11239d7 Mon Sep 17 00:00:00 2001 From: Jose Luis Leon Date: Wed, 26 Jul 2023 19:06:09 -0500 Subject: [PATCH 1/2] feat(object): Add .toHave[Keys|Values|Entries] methods --- package/src/lib/ObjectAssertion.ts | 113 +++++++++++++++++++++-- package/test/lib/ObjectAssertion.test.ts | 78 ++++++++++++++++ 2 files changed, 183 insertions(+), 8 deletions(-) diff --git a/package/src/lib/ObjectAssertion.ts b/package/src/lib/ObjectAssertion.ts index f3d7b6b6..4f82d6cf 100644 --- a/package/src/lib/ObjectAssertion.ts +++ b/package/src/lib/ObjectAssertion.ts @@ -68,11 +68,11 @@ export class ObjectAssertion extends Assertion { actual: this.actual, message: `Expected the object to contain the provided key <${String(key)}>`, }); - const invertedError = new AssertionError({ actual: this.actual, message: `Expected the object NOT to contain the provided key <${String(key)}>`, }); + return this.execute({ assertWhen: this.hasOwnProp(key), error, @@ -97,11 +97,11 @@ export class ObjectAssertion extends Assertion { expected: keys, message: `Expected the object to contain all the provided keys <${prettify(keys)}>`, }); - const invertedError = new AssertionError({ actual: Object.keys(this.actual), message: `Expected the object NOT to contain all the provided keys <${prettify(keys)}>`, }); + return this.execute({ assertWhen: keys.every(key => this.hasOwnProp(key)), error, @@ -126,11 +126,11 @@ export class ObjectAssertion extends Assertion { expected: keys, message: `Expected the object to contain at least one of the provided keys <${prettify(keys)}>`, }); - const invertedError = new AssertionError({ actual: Object.keys(this.actual), message: `Expected the object NOT to contain any of the provided keys <${prettify(keys)}>`, }); + return this.execute({ assertWhen: keys.some(key => this.hasOwnProp(key)), error, @@ -138,6 +138,38 @@ export class ObjectAssertion extends Assertion { }); } + /** + * Check if the object has exactly the provided keys. + * + * @example + * ``` + * expect({ x: 1, y: 2, z: 3 }).toHaveKeys("x", "y", "z"); + * ``` + * + * @param keys the keys the object should have + * @returns the assertion instance + */ + public toHaveKeys(...keys: Array): this { + const sortedActual = Object.keys(this.actual).sort(); + const sortedKeys = [...keys].sort(); + + const error = new AssertionError({ + actual: sortedActual, + expected: sortedKeys, + message: `Expected the object to have exactly the keys <${prettify(sortedKeys)}>`, + }); + const invertedError = new AssertionError({ + actual: sortedActual, + message: `Expected the object NOT to have the keys <${prettify(sortedKeys)}>`, + }); + + return this.execute({ + assertWhen: isDeepEqual(sortedActual, sortedKeys), + error, + invertedError, + }); + } + /** * Check if the object contains the provided value. * @@ -155,11 +187,11 @@ export class ObjectAssertion extends Assertion { actual: this.actual, message: `Expected the object to contain the provided value <${prettify(value)}>`, }); - const invertedError = new AssertionError({ actual: this.actual, message: `Expected the object NOT to contain the provided value <${prettify(value)}>`, }); + return this.execute({ assertWhen: Object.values(this.actual).some(actualValue => isDeepEqual(actualValue, value)), error, @@ -184,11 +216,11 @@ export class ObjectAssertion extends Assertion { expected: values, message: `Expected the object to contain all the provided values <${prettify(values)}>`, }); - const invertedError = new AssertionError({ actual: Object.values(this.actual), message: `Expected the object NOT to contain all the provided values <${prettify(values)}>`, }); + return this.execute({ assertWhen: values .every(value => @@ -216,11 +248,11 @@ export class ObjectAssertion extends Assertion { expected: values, message: `Expected the object to contain at least one of the provided values <${prettify(values)}>`, }); - const invertedError = new AssertionError({ actual: Object.values(this.actual), message: `Expected the object NOT to contain any of the provided values <${prettify(values)}>`, }); + return this.execute({ assertWhen: values .some(value => @@ -231,6 +263,38 @@ export class ObjectAssertion extends Assertion { }); } + /** + * Check if the object has exactly the provided values. + * + * @example + * ``` + * expect({ x: 1, y: "a", z: true }).toHaveValues(1, "a", true); + * ``` + * + * @param values the values the object should have + * @returns the assertion instance + */ + public toHaveValues(...values: Array): this { + const sortedActual = Object.values(this.actual).sort(); + const sorterdValues = [...values].sort(); + + const error = new AssertionError({ + actual: sortedActual, + expected: sorterdValues, + message: `Expected the object to have exactly the values <${prettify(sorterdValues)}>`, + }); + const invertedError = new AssertionError({ + actual: sortedActual, + message: `Expected the object NOT to have the values <${prettify(sorterdValues)}>`, + }); + + return this.execute({ + assertWhen: isDeepEqual(sortedActual, sorterdValues), + error, + invertedError, + }); + } + /** * Check if the object contains the provided entry. * @@ -272,7 +336,7 @@ export class ObjectAssertion extends Assertion { * @param entries the entries that the object should contain * @returns the assertion instance */ - public toContainAllEntries(...entries: Array>): this { + public toContainAllEntries(...entries: Entry[]): this { const error = new AssertionError({ actual: Object.entries(this.actual), expected: entries, @@ -306,7 +370,7 @@ export class ObjectAssertion extends Assertion { * @param entries the entries that the object should contain * @returns the assertion instance */ - public toContainAnyEntries(...entries: Array>): this { + public toContainAnyEntries(...entries: Entry[]): this { const error = new AssertionError({ actual: Object.entries(this.actual), expected: entries, @@ -328,6 +392,39 @@ export class ObjectAssertion extends Assertion { }); } + /** + * Check if the object has exctly the provided entries. + * + * @example + * ``` + * expect({ a: 1, b: 2, c: 3 }) + * .toHaveEntries(["a", 1], ["b", 2], ["c", 3]); + * ``` + * + * @param entries the entries the object should have + * @returns the assertion instance + */ + public toHaveEntries(...entries: Entry[]): this { + const sortedActual = Object.entries(this.actual).sort(); + const sortedEntries = [...entries].sort(); + const prettyEntries = sortedEntries.map(entry => `[${prettify(entry)}]`).join(","); + const error = new AssertionError({ + actual: sortedActual, + expected: sortedEntries, + message: `Expected the object to have exactly the entries <${prettyEntries}>`, + }); + const invertedError = new AssertionError({ + actual: Object.entries(this.actual), + message: `Expected the object NOT to have the entries <${prettyEntries}>`, + }); + + return this.execute({ + assertWhen: isDeepEqual(sortedActual, sortedEntries), + error, + invertedError, + }); + } + /** * Check if the object match the provided object. * diff --git a/package/test/lib/ObjectAssertion.test.ts b/package/test/lib/ObjectAssertion.test.ts index 9765101f..b3295ae3 100644 --- a/package/test/lib/ObjectAssertion.test.ts +++ b/package/test/lib/ObjectAssertion.test.ts @@ -131,6 +131,32 @@ describe("[Unit] ObjectAssertion.test.ts", () => { }); }); + describe(".toHaveKeys", () => { + context("when the object has exactly provided keys", () => { + it("returns the assertion instance", () => { + const test = new ObjectAssertion({ x: 1, y: 2, z: 3 }); + + assert.deepStrictEqual(test.toHaveKeys("x", "y", "z"), test); + assert.throws(() => test.not.toHaveKeys("x", "y", "z"), { + message: "Expected the object NOT to have the keys ", + name: AssertionError.name, + }); + }); + }); + + context("when the object does NOT have exactly the provided keys", () => { + it("throws an assertion error", () => { + const test = new ObjectAssertion({ x: 1, y: 2, z: 3 }); + + assert.throws(() => test.toHaveKeys("x", "y"), { + message: "Expected the object to have exactly the keys ", + name: AssertionError.name, + }); + assert.deepStrictEqual(test.not.toHaveKeys("x", "z"), test); + }); + }); + }); + describe(".toContainValue", () => { context("when the object contains the provided value", () => { it("returns the assertion instance", () => { @@ -215,6 +241,32 @@ describe("[Unit] ObjectAssertion.test.ts", () => { }); }); + describe(".toHaveValues", () => { + context("when the object has exactly the provided values", () => { + it("returns the assertion instance", () => { + const test = new ObjectAssertion({ x: 1, y: "a", z: true }); + + assert.deepStrictEqual(test.toHaveValues(1, "a", true), test); + assert.throws(() => test.not.toHaveValues(1, "a", true), { + message: "Expected the object NOT to have the values <1,a,true>", + name: AssertionError.name, + }); + }); + }); + + context("when the object does NOT have exactly the provided values", () => { + it("throws an assertion error", () => { + const test = new ObjectAssertion({ x: 1, y: "a", z: true }); + + assert.throws(() => test.toHaveValues(1, "a"), { + message: "Expected the object to have exactly the values <1,a>", + name: AssertionError.name, + }); + assert.deepStrictEqual(test.not.toHaveValues(1, "a"), test); + }); + }); + }); + describe(".toContainEntry", () => { context("when the object contains the provided entry", () => { it("returns the assertion instance", () => { @@ -309,6 +361,32 @@ describe("[Unit] ObjectAssertion.test.ts", () => { }); }); + describe(".toHaveEntries", () => { + context("when the object has exactly the provided entries", () => { + it("returns the assertion instance", () => { + const test = new ObjectAssertion({ a: 1, b: 2, c: 3 }); + + assert.deepStrictEqual(test.toHaveEntries(["a", 1], ["b", 2], ["c", 3]), test); + assert.throws(() => test.not.toHaveEntries(["a", 1], ["b", 2], ["c", 3]), { + message: "Expected the object NOT to have the entries <[a,1],[b,2],[c,3]>", + name: AssertionError.name, + }); + }); + }); + + context("when the object doe NOT have exactly the provided entries", () => { + it("throws an assertion error", () => { + const test = new ObjectAssertion({ a: 1, b: 2, c: 3 }); + + assert.throws(() => test.toHaveEntries(["a", 1], ["c", 3]), { + message: "Expected the object to have exactly the entries <[a,1],[c,3]>", + name: AssertionError.name, + }); + assert.deepStrictEqual(test.not.toHaveEntries(["a", 1], ["c", 3]), test); + }); + }); + }); + describe(".toPartiallyMatch", () => { context("when the object matches the provided object", () => { it("returns the assertion instance", () => { From b2d6e812b47b13aef64a82ae8f790a9a73e1ade1 Mon Sep 17 00:00:00 2001 From: Jose Luis Leon Date: Fri, 28 Jul 2023 12:02:04 -0500 Subject: [PATCH 2/2] Fix typo --- package/src/lib/ObjectAssertion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/src/lib/ObjectAssertion.ts b/package/src/lib/ObjectAssertion.ts index 4f82d6cf..cad2efff 100644 --- a/package/src/lib/ObjectAssertion.ts +++ b/package/src/lib/ObjectAssertion.ts @@ -393,7 +393,7 @@ export class ObjectAssertion extends Assertion { } /** - * Check if the object has exctly the provided entries. + * Check if the object has exactly the provided entries. * * @example * ```