jest-extended icon indicating copy to clipboard operation
jest-extended copied to clipboard

Testing for type or null

Open satheler opened this issue 5 years ago • 6 comments

Add toBeObjectContainingOrNull matcher like:

const expect = require("expect");

const okObject = {
    message: () => "Ok",
    pass: true
};

expect.extend({
    toBeTypeOrNull(received, argument) {
        if (received === null)
            return okObject;
        if (expect(received).toEqual(expect.any(argument))) {
            return okObject;
        } else {
            return {
                message: () => `expected ${received} to be ${argument} type or null`,
                pass: false
            };
        }
    },
    toBeObjectContainingOrNull(received, argument) {
        if (received === null)
            return okObject;

        const pass = expect(received).toEqual(expect.objectContaining(argument));
        if (pass) {
            return okObject;
        } else {
            return {
                message: () => `expected ${received} to be ${argument} type or null`,
                pass: false
            };
        }
    }
});

module.exports = { expect };

Then you can use toBeObjectContainingOrNull as follows:

const userImageSchema = {
    displayName: expect.any(String),
    image: expect.toBeObjectContainingOrNull({
        type: "Buffer",
        data: expect.any(Array)
    }),
    orgs: expect.any(Array)
};

I see this in https://stackoverflow.com/a/51507754/7414646

satheler avatar Sep 05 '19 02:09 satheler

We use something similar for verifying properties which are optional. Would be good addition to the library.

cdavid15 avatar Dec 14 '19 08:12 cdavid15

+1 on this

bodinsamuel avatar Jan 21 '21 19:01 bodinsamuel

Meanwhile, you can do this:

image: expect.objectContaining({ type: "Buffer", data: expect.any(Array) }) || null,

makitocode avatar Feb 24 '21 21:02 makitocode

+1 on this

H0JLuk avatar May 08 '21 20:05 H0JLuk

@makitocode your solution almost worked for me but I had to alter as following

expect(casper).toEqual(
      expect.objectContaining({ casperDescription: null }) ||
        expect.objectContaining({ casperDescription: expect.any(String) }),
    );

michaelCaleyWhaley avatar Jun 14 '23 13:06 michaelCaleyWhaley

This copies the Any asymmetric matcher in the expect library to allow a null or undefined object.

export class Nullable extends AsymmetricMatcher<any> {
  constructor(sample) {
    if (typeof sample === "undefined") {
      throw new TypeError(
        "nullable() expects to be passed a constructor function."
      );
    }
    super(sample);
  }

  asymmetricMatch(other) {
    if (other === null || other === undefined) {
      return true;
    }
    if (this.sample == String) {
      return typeof other == "string" || other instanceof String;
    }
    if (this.sample == Number) {
      return typeof other == "number" || other instanceof Number;
    }
    if (this.sample == Function) {
      return typeof other == "function" || other instanceof Function;
    }
    if (this.sample == Boolean) {
      return typeof other == "boolean" || other instanceof Boolean;
    }
    if (this.sample == BigInt) {
      return typeof other == "bigint" || other instanceof BigInt;
    }
    if (this.sample == Symbol) {
      return typeof other == "symbol" || other instanceof Symbol;
    }
    if (this.sample == Object) {
      return typeof other == "object";
    }
    return other instanceof this.sample;
  }

  toString() {
    return "Nullable";
  }

  getExpectedType() {
    if (this.sample == String) {
      return "string";
    }
    if (this.sample == Number) {
      return "number";
    }
    if (this.sample == Function) {
      return "function";
    }
    if (this.sample == Object) {
      return "object";
    }
    if (this.sample == Boolean) {
      return "boolean";
    }
    return fnNameFor(this.sample);
  }

  toAsymmetricMatcher() {
    return `Nullable<${fnNameFor(this.sample)}>`;
  }
}

function fnNameFor(func) {
  if (func.name) {
    return func.name;
  }
  const matches = Function.prototype.toString
    .call(func)
    .match(/^(?:async)?\s*function\s*\*?\s*([\w$]+)\s*\(/);
  return matches ? matches[1] : "<anonymous>";
}

export const nullable = (
  sample
) => new Nullable(sample);

Usage

  expect(sample).toMatchObject({
    modified_at: expect.any(String),
    created_at: expect.any(String),
    start_date: nullable(String),
  });

njmaeff avatar Jul 11 '23 18:07 njmaeff