Skip to content

Commit

Permalink
feat(object): Add .toHave[Keys|Values|Entries] methods (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoseLion authored Jul 28, 2023
1 parent 6e07131 commit 97425dd
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 8 deletions.
113 changes: 105 additions & 8 deletions package/src/lib/ObjectAssertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
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,
Expand All @@ -97,11 +97,11 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
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,
Expand All @@ -126,18 +126,50 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
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,
invertedError,
});
}

/**
* 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<keyof T>): 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.
*
Expand All @@ -155,11 +187,11 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
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,
Expand All @@ -184,11 +216,11 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
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 =>
Expand Down Expand Up @@ -216,11 +248,11 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
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 =>
Expand All @@ -231,6 +263,38 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
});
}

/**
* 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<T[keyof T]>): 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.
*
Expand Down Expand Up @@ -272,7 +336,7 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
* @param entries the entries that the object should contain
* @returns the assertion instance
*/
public toContainAllEntries(...entries: Array<Entry<T>>): this {
public toContainAllEntries(...entries: Entry<T>[]): this {
const error = new AssertionError({
actual: Object.entries(this.actual),
expected: entries,
Expand Down Expand Up @@ -306,7 +370,7 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
* @param entries the entries that the object should contain
* @returns the assertion instance
*/
public toContainAnyEntries(...entries: Array<Entry<T>>): this {
public toContainAnyEntries(...entries: Entry<T>[]): this {
const error = new AssertionError({
actual: Object.entries(this.actual),
expected: entries,
Expand All @@ -328,6 +392,39 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
});
}

/**
* Check if the object has exactly 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<T>[]): 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.
*
Expand Down
78 changes: 78 additions & 0 deletions package/test/lib/ObjectAssertion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <x,y,z>",
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 <x,y>",
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", () => {
Expand Down Expand Up @@ -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", () => {
Expand Down Expand Up @@ -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", () => {
Expand Down

0 comments on commit 97425dd

Please sign in to comment.