// Copyright 2018-2025 the Deno authors. MIT license.

// TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file ban-types prefer-primordials

import {
  AssertionError,
  AssertionErrorConstructorOptions,
} from "ext:deno_node/assertion_error.ts";
import * as asserts from "ext:deno_node/_util/std_asserts.ts";
import { inspect } from "node:util";
import {
  ERR_AMBIGUOUS_ARGUMENT,
  ERR_INVALID_ARG_TYPE,
  ERR_INVALID_ARG_VALUE,
  ERR_INVALID_RETURN_VALUE,
  ERR_MISSING_ARGS,
} from "ext:deno_node/internal/errors.ts";
import { isDeepEqual } from "ext:deno_node/internal/util/comparisons.ts";
import { primordials } from "ext:core/mod.js";
import { CallTracker } from "ext:deno_node/internal/assert/calltracker.js";
import { deprecate } from "node:util";

const {
  ArrayPrototypeIndexOf,
  ArrayPrototypeJoin,
  ArrayPrototypeSlice,
  ObjectPrototypeIsPrototypeOf,
  StringPrototypeIndexOf,
  StringPrototypeSlice,
  StringPrototypeSplit,
} = primordials;

function innerFail(obj: {
  actual?: unknown;
  expected?: unknown;
  message?: string | Error;
  operator?: string;
}) {
  if (obj.message instanceof Error) {
    throw obj.message;
  }

  throw new AssertionError({
    actual: obj.actual,
    expected: obj.expected,
    message: obj.message,
    operator: obj.operator,
  });
}

interface ExtendedAssertionErrorConstructorOptions
  extends AssertionErrorConstructorOptions {
  generatedMessage?: boolean;
}

// TODO(uki00a): This function is a workaround for setting the `generatedMessage` property flexibly.
function createAssertionError(
  options: ExtendedAssertionErrorConstructorOptions,
): AssertionError {
  const error = new AssertionError(options);
  if (options.generatedMessage) {
    error.generatedMessage = true;
  }
  return error;
}

/** Converts the std assertion error to node.js assertion error */
function toNode(
  fn: () => void,
  opts?: {
    actual: unknown;
    expected: unknown;
    message?: string | Error;
    operator?: string;
    stackStartFn?: Function;
  },
) {
  const { operator, message, actual, expected, stackStartFn } = opts || {};
  try {
    fn();
  } catch (e) {
    if (e instanceof asserts.AssertionError) {
      if (typeof message === "string") {
        throw new AssertionError({
          operator,
          message,
          actual,
          expected,
          stackStartFn,
        });
      } else if (message instanceof Error) {
        throw message;
      } else {
        throw new AssertionError({
          operator,
          actual,
          expected,
          stackStartFn,
        });
      }
    }
    throw e;
  }
}

function assert(actual: unknown, message?: string | Error): asserts actual {
  if (arguments.length === 0) {
    throw new AssertionError({
      message: "No value argument passed to `assert.ok()`",
    });
  }
  if (actual) {
    return;
  }

  equal(actual, true, message);
}
const ok = assert;

function throws(
  fn: () => void,
  error?: RegExp | Function | Error,
  message?: string,
) {
  // Check arg types
  if (typeof fn !== "function") {
    throw new ERR_INVALID_ARG_TYPE("fn", "function", fn);
  }
  if (
    typeof error === "object" && error !== null &&
    Object.getPrototypeOf(error) === Object.prototype &&
    Object.keys(error).length === 0
  ) {
    // error is an empty object
    throw new ERR_INVALID_ARG_VALUE(
      "error",
      error,
      "may not be an empty object",
    );
  }
  if (typeof message === "string") {
    if (
      !(error instanceof RegExp) && typeof error !== "function" &&
      !(error instanceof Error) && typeof error !== "object"
    ) {
      throw new ERR_INVALID_ARG_TYPE("error", [
        "Function",
        "Error",
        "RegExp",
        "Object",
      ], error);
    }
  } else {
    if (
      typeof error !== "undefined" && typeof error !== "string" &&
      !(error instanceof RegExp) && typeof error !== "function" &&
      !(error instanceof Error) && typeof error !== "object"
    ) {
      throw new ERR_INVALID_ARG_TYPE("error", [
        "Function",
        "Error",
        "RegExp",
        "Object",
      ], error);
    }
  }

  // Checks test function
  try {
    fn();
  } catch (e) {
    if (
      validateThrownError(e, error, message, {
        operator: throws,
      })
    ) {
      return;
    }
  }
  if (message) {
    let msg = `Missing expected exception: ${message}`;
    if (typeof error === "function" && error?.name) {
      msg = `Missing expected exception (${error.name}): ${message}`;
    }
    throw new AssertionError({
      message: msg,
      operator: "throws",
      actual: undefined,
      expected: error,
      stackStartFn: throws,
    });
  } else if (typeof error === "string") {
    // Use case of throws(fn, message)
    throw new AssertionError({
      message: `Missing expected exception: ${error}`,
      operator: "throws",
      actual: undefined,
      expected: undefined,
      stackStartFn: throws,
    });
  } else if (typeof error === "function" && error?.prototype !== undefined) {
    throw new AssertionError({
      message: `Missing expected exception (${error.name}).`,
      operator: "throws",
      actual: undefined,
      expected: error,
      stackStartFn: throws,
    });
  } else {
    throw new AssertionError({
      message: "Missing expected exception.",
      operator: "throws",
      actual: undefined,
      expected: error,
      stackStartFn: throws,
    });
  }
}

function doesNotThrow(
  fn: () => void,
  message?: string,
): void;
function doesNotThrow(
  fn: () => void,
  error?: Function,
  message?: string | Error,
): void;
function doesNotThrow(
  fn: () => void,
  error?: RegExp,
  message?: string,
): void;
function doesNotThrow(
  fn: () => void,
  expected?: Function | RegExp | string,
  message?: string | Error,
) {
  // Check arg type
  if (typeof fn !== "function") {
    throw new ERR_INVALID_ARG_TYPE("fn", "function", fn);
  } else if (
    !(expected instanceof RegExp) && typeof expected !== "function" &&
    typeof expected !== "string" && typeof expected !== "undefined"
  ) {
    throw new ERR_INVALID_ARG_TYPE("expected", ["Function", "RegExp"], fn);
  }

  // Checks test function
  try {
    fn();
  } catch (e) {
    gotUnwantedException(e, expected, message, doesNotThrow);
  }
}

function equal(
  actual: unknown,
  expected: unknown,
  message?: string | Error,
) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("actual", "expected");
  }

  if (actual == expected) {
    return;
  }

  if (Number.isNaN(actual) && Number.isNaN(expected)) {
    return;
  }

  if (typeof message === "string") {
    throw new AssertionError({
      message,
    });
  } else if (message instanceof Error) {
    throw message;
  }

  toNode(
    () => asserts.assertStrictEquals(actual, expected),
    {
      message: message || `${actual} == ${expected}`,
      operator: "==",
      actual,
      expected,
      stackStartFn: equal,
    },
  );
}
function notEqual(
  actual: unknown,
  expected: unknown,
  message?: string | Error,
) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("actual", "expected");
  }

  if (Number.isNaN(actual) && Number.isNaN(expected)) {
    throw new AssertionError({
      message: `${actual} != ${expected}`,
      operator: "!=",
      actual,
      expected,
    });
  }
  if (actual != expected) {
    return;
  }

  if (typeof message === "string") {
    throw new AssertionError({
      message,
    });
  } else if (message instanceof Error) {
    throw message;
  }

  toNode(
    () => asserts.assertNotStrictEquals(actual, expected),
    {
      message: message || `${actual} != ${expected}`,
      operator: "!=",
      actual,
      expected,
    },
  );
}
function strictEqual(
  actual: unknown,
  expected: unknown,
  message?: string | Error,
) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("actual", "expected");
  }

  toNode(
    () => asserts.assertStrictEquals(actual, expected),
    { message, operator: "strictEqual", actual, expected },
  );
}
function notStrictEqual(
  actual: unknown,
  expected: unknown,
  message?: string | Error,
) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("actual", "expected");
  }

  toNode(
    () => asserts.assertNotStrictEquals(actual, expected),
    { message, actual, expected, operator: "notStrictEqual" },
  );
}

function deepEqual(
  actual: unknown,
  expected: unknown,
  message?: string | Error,
) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("actual", "expected");
  }

  if (!isDeepEqual(actual, expected)) {
    innerFail({ actual, expected, message, operator: "deepEqual" });
  }
}
function notDeepEqual(
  actual: unknown,
  expected: unknown,
  message?: string | Error,
) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("actual", "expected");
  }

  if (isDeepEqual(actual, expected)) {
    innerFail({ actual, expected, message, operator: "notDeepEqual" });
  }
}
function deepStrictEqual(
  actual: unknown,
  expected: unknown,
  message?: string | Error,
) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("actual", "expected");
  }

  if (!asserts.equal(actual, expected)) {
    throw new AssertionError({
      message,
      actual,
      expected,
      operator: "deepStrictEqual",
    });
  }
}

function notDeepStrictEqual(
  actual: unknown,
  expected: unknown,
  message?: string | Error,
) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("actual", "expected");
  }

  toNode(
    () => asserts.assertNotEquals(actual, expected),
    { message, actual, expected, operator: "deepNotStrictEqual" },
  );
}

let warned = false;

function fail(
  actual?: string | Error,
  expected?: unknown,
  message?: string | Error,
  operator?: string,
  stackStartFn?: Function,
): never {
  const argsLen = arguments.length;

  let internalMessage = false;
  if (actual == null && argsLen <= 1) {
    internalMessage = true;
    message = "Failed";
  } else if (argsLen === 1) {
    message = actual;
    actual = undefined;
  } else {
    if (warned === false) {
      warned = true;
      // deno-lint-ignore no-process-global
      process.emitWarning(
        "assert.fail() with more than one argument is deprecated. " +
          "Please use assert.strictEqual() instead or only pass a message.",
        "DeprecationWarning",
        "DEP0094",
      );
    }
    if (argsLen === 2) {
      operator = "!=";
    }
  }

  if (message instanceof Error) throw message;

  const errArgs = {
    actual,
    expected,
    operator: operator === undefined ? "fail" : operator,
    stackStartFn: stackStartFn || fail,
    message,
  };
  const err = new AssertionError(errArgs);
  if (internalMessage) {
    err.generatedMessage = true;
  }
  throw err;
}

function match(actual: string, regexp: RegExp, message?: string | Error) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("actual", "regexp");
  }
  if (!(regexp instanceof RegExp)) {
    throw new ERR_INVALID_ARG_TYPE("regexp", "RegExp", regexp);
  }

  toNode(
    () => asserts.assertMatch(actual, regexp),
    { message, actual, expected: regexp, operator: "match" },
  );
}

function doesNotMatch(
  string: string,
  regexp: RegExp,
  message?: string | Error,
) {
  if (arguments.length < 2) {
    throw new ERR_MISSING_ARGS("string", "regexp");
  }
  if (!(regexp instanceof RegExp)) {
    throw new ERR_INVALID_ARG_TYPE("regexp", "RegExp", regexp);
  }
  if (typeof string !== "string") {
    if (message instanceof Error) {
      throw message;
    }
    throw new AssertionError({
      message: message ||
        `The "string" argument must be of type string. Received type ${typeof string} (${
          inspect(string)
        })`,
      actual: string,
      expected: regexp,
      operator: "doesNotMatch",
    });
  }

  toNode(
    () => asserts.assertNotMatch(string, regexp),
    { message, actual: string, expected: regexp, operator: "doesNotMatch" },
  );
}

function strict(actual: unknown, message?: string | Error): asserts actual {
  if (arguments.length === 0) {
    throw new AssertionError({
      message: "No value argument passed to `assert.ok()`",
    });
  }
  assert(actual, message);
}

function rejects(
  // deno-lint-ignore no-explicit-any
  asyncFn: Promise<any> | (() => Promise<any>),
  error?: RegExp | Function | Error,
): Promise<void>;

function rejects(
  // deno-lint-ignore no-explicit-any
  asyncFn: Promise<any> | (() => Promise<any>),
  message?: string,
): Promise<void>;

// Intentionally avoid using async/await because test-assert-async.js requires it
function rejects(
  // deno-lint-ignore no-explicit-any
  asyncFn: Promise<any> | (() => Promise<any>),
  error?: RegExp | Function | Error | string,
  message?: string,
) {
  let promise: Promise<void>;
  if (typeof asyncFn === "function") {
    try {
      promise = asyncFn();
    } catch (err) {
      // If `asyncFn` throws an error synchronously, this function returns a rejected promise.
      return Promise.reject(err);
    }

    if (!isValidThenable(promise)) {
      return Promise.reject(
        new ERR_INVALID_RETURN_VALUE(
          "instance of Promise",
          "promiseFn",
          promise,
        ),
      );
    }
  } else if (!isValidThenable(asyncFn)) {
    return Promise.reject(
      new ERR_INVALID_ARG_TYPE("promiseFn", ["function", "Promise"], asyncFn),
    );
  } else {
    promise = asyncFn;
  }

  function onFulfilled() {
    let message = "Missing expected rejection";
    if (typeof error === "string") {
      message += `: ${error}`;
    } else if (typeof error === "function" && error.prototype !== undefined) {
      message += ` (${error.name}).`;
    } else {
      message += ".";
    }
    return Promise.reject(createAssertionError({
      message,
      operator: "rejects",
      generatedMessage: true,
    }));
  }

  // deno-lint-ignore camelcase
  function rejects_onRejected(e: Error) { // TODO(uki00a): In order to `test-assert-async.js` pass, intentionally adds `rejects_` as a prefix.
    if (
      validateThrownError(e, error, message, {
        operator: rejects,
        validationFunctionName: "validate",
      })
    ) {
      return;
    }
  }

  return promise.then(onFulfilled, rejects_onRejected);
}

function doesNotReject(
  // deno-lint-ignore no-explicit-any
  asyncFn: Promise<any> | (() => Promise<any>),
  error?: RegExp | Function,
): Promise<void>;

function doesNotReject(
  // deno-lint-ignore no-explicit-any
  asyncFn: Promise<any> | (() => Promise<any>),
  message?: string,
): Promise<void>;

// Intentionally avoid using async/await because test-assert-async.js requires it
function doesNotReject(
  // deno-lint-ignore no-explicit-any
  asyncFn: Promise<any> | (() => Promise<any>),
  error?: RegExp | Function | string,
  message?: string,
) {
  // deno-lint-ignore no-explicit-any
  let promise: Promise<any>;
  if (typeof asyncFn === "function") {
    try {
      const value = asyncFn();
      if (!isValidThenable(value)) {
        return Promise.reject(
          new ERR_INVALID_RETURN_VALUE(
            "instance of Promise",
            "promiseFn",
            value,
          ),
        );
      }
      promise = value;
    } catch (e) {
      // If `asyncFn` throws an error synchronously, this function returns a rejected promise.
      return Promise.reject(e);
    }
  } else if (!isValidThenable(asyncFn)) {
    return Promise.reject(
      new ERR_INVALID_ARG_TYPE("promiseFn", ["function", "Promise"], asyncFn),
    );
  } else {
    promise = asyncFn;
  }

  return promise.then(
    () => {},
    (e) => gotUnwantedException(e, error, message, doesNotReject),
  );
}

function gotUnwantedException(
  // deno-lint-ignore no-explicit-any
  e: any,
  expected: RegExp | Function | string | null | undefined,
  message: string | Error | null | undefined,
  operator: Function,
): never {
  if (typeof expected === "string") {
    // The use case of doesNotThrow(fn, message);
    throw new AssertionError({
      message:
        `Got unwanted exception: ${expected}\nActual message: "${e.message}"`,
      operator: operator.name,
    });
  } else if (
    typeof expected === "function" && expected.prototype !== undefined
  ) {
    // The use case of doesNotThrow(fn, Error, message);
    if (e instanceof expected) {
      let msg = `Got unwanted exception: ${e.constructor?.name}`;
      if (message) {
        msg += ` ${String(message)}`;
      }
      throw new AssertionError({
        message: msg,
        operator: operator.name,
      });
    } else if (expected.prototype instanceof Error) {
      throw e;
    } else {
      const result = expected(e);
      if (result === true) {
        let msg = `Got unwanted rejection.\nActual message: "${e.message}"`;
        if (message) {
          msg += ` ${String(message)}`;
        }
        throw new AssertionError({
          message: msg,
          operator: operator.name,
        });
      }
    }
    throw e;
  } else {
    if (message) {
      throw new AssertionError({
        message: `Got unwanted exception: ${message}\nActual message: "${
          e ? e.message : String(e)
        }"`,
        operator: operator.name,
      });
    }
    throw new AssertionError({
      message: `Got unwanted exception.\nActual message: "${
        e ? e.message : String(e)
      }"`,
      operator: operator.name,
    });
  }
}

/**
 * Throws `value` if the value is not `null` or `undefined`.
 *
 * @param err
 */
// deno-lint-ignore no-explicit-any
function ifError(err: any) {
  if (err !== null && err !== undefined) {
    let message = "ifError got unwanted exception: ";

    if (typeof err === "object" && typeof err.message === "string") {
      if (err.message.length === 0 && err.constructor) {
        message += err.constructor.name;
      } else {
        message += err.message;
      }
    } else {
      message += inspect(err);
    }

    const newErr = new AssertionError({
      actual: err,
      expected: null,
      operator: "ifError",
      message,
      stackStartFn: ifError,
    });

    // Make sure we actually have a stack trace!
    const origStack = err.stack;

    if (typeof origStack === "string") {
      // This will remove any duplicated frames from the error frames taken
      // from within `ifError` and add the original error frames to the newly
      // created ones.
      const origStackStart = StringPrototypeIndexOf(origStack, "\n    at");
      if (origStackStart !== -1) {
        const originalFrames = StringPrototypeSplit(
          StringPrototypeSlice(origStack, origStackStart + 1),
          "\n",
        );
        // Filter all frames existing in err.stack.
        let newFrames = StringPrototypeSplit(newErr.stack, "\n");
        for (const errFrame of originalFrames) {
          // Find the first occurrence of the frame.
          const pos = ArrayPrototypeIndexOf(newFrames, errFrame);
          if (pos !== -1) {
            // Only keep new frames.
            newFrames = ArrayPrototypeSlice(newFrames, 0, pos);
            break;
          }
        }
        const stackStart = ArrayPrototypeJoin(newFrames, "\n");
        const stackEnd = ArrayPrototypeJoin(originalFrames, "\n");
        newErr.stack = `${stackStart}\n${stackEnd}`;
      }
    }

    throw newErr;
  }
}

interface ValidateThrownErrorOptions {
  operator: Function;
  validationFunctionName?: string;
}

function validateThrownError(
  // deno-lint-ignore no-explicit-any
  e: any,
  error: RegExp | Function | Error | string | null | undefined,
  message: string | undefined | null,
  options: ValidateThrownErrorOptions,
): boolean {
  if (typeof error === "string") {
    if (message != null) {
      throw new ERR_INVALID_ARG_TYPE(
        "error",
        ["Object", "Error", "Function", "RegExp"],
        error,
      );
    } else if (typeof e === "object" && e !== null) {
      if (e.message === error) {
        throw new ERR_AMBIGUOUS_ARGUMENT(
          "error/message",
          `The error message "${e.message}" is identical to the message.`,
        );
      }
    } else if (e === error) {
      throw new ERR_AMBIGUOUS_ARGUMENT(
        "error/message",
        `The error "${e}" is identical to the message.`,
      );
    }
    message = error;
    error = undefined;
  }
  if (error?.prototype !== undefined && e instanceof error) {
    return true;
  }
  if (
    typeof error === "function" &&
    (error === Error || ObjectPrototypeIsPrototypeOf(Error, error))
  ) {
    // error is a constructor
    if (e instanceof error) {
      return true;
    }
    throw createAssertionError({
      message:
        `The error is expected to be an instance of "${error.name}". Received "${e?.constructor?.name}"\n\nError message:\n\n${e?.message}`,
      actual: e,
      expected: error,
      operator: options.operator.name,
      generatedMessage: true,
    });
  }
  if (error instanceof Function) {
    const received = error(e);
    if (received === true) {
      return true;
    }
    throw createAssertionError({
      message: `The ${
        options.validationFunctionName
          ? `"${options.validationFunctionName}" validation`
          : "validation"
      } function is expected to return "true". Received ${
        inspect(received)
      }\n\nCaught error:\n\n${e}`,
      actual: e,
      expected: error,
      operator: options.operator.name,
      generatedMessage: true,
    });
  }
  if (error instanceof RegExp) {
    if (error.test(String(e))) {
      return true;
    }
    throw createAssertionError({
      message:
        `The input did not match the regular expression ${error.toString()}. Input:\n\n'${
          String(e)
        }'\n`,
      actual: e,
      expected: error,
      operator: options.operator.name,
      generatedMessage: true,
    });
  }
  if (typeof error === "object" && error !== null) {
    const keys = Object.keys(error);
    if (error instanceof Error) {
      keys.push("name", "message");
    }
    for (const k of keys) {
      if (e == null) {
        throw createAssertionError({
          message: message || "object is expected to thrown, but got null",
          actual: e,
          expected: error,
          operator: options.operator.name,
          generatedMessage: message == null,
        });
      }

      if (typeof e === "string") {
        throw createAssertionError({
          message: message ||
            `object is expected to thrown, but got string: ${e}`,
          actual: e,
          expected: error,
          operator: options.operator.name,
          generatedMessage: message == null,
        });
      }
      if (typeof e === "number") {
        throw createAssertionError({
          message: message ||
            `object is expected to thrown, but got number: ${e}`,
          actual: e,
          expected: error,
          operator: options.operator.name,
          generatedMessage: message == null,
        });
      }
      if (!(k in e)) {
        throw createAssertionError({
          message: message || `A key in the expected object is missing: ${k}`,
          actual: e,
          expected: error,
          operator: options.operator.name,
          generatedMessage: message == null,
        });
      }
      const actual = e[k];
      // deno-lint-ignore no-explicit-any
      const expected = (error as any)[k];
      if (typeof actual === "string" && expected instanceof RegExp) {
        match(actual, expected);
      } else {
        deepStrictEqual(actual, expected);
      }
    }
    return true;
  }
  if (typeof error === "undefined") {
    return true;
  }
  throw createAssertionError({
    message: `Invalid expectation: ${error}`,
    operator: options.operator.name,
    generatedMessage: true,
  });
}

// deno-lint-ignore no-explicit-any
function isValidThenable(maybeThennable: any): boolean {
  if (!maybeThennable) {
    return false;
  }

  if (maybeThennable instanceof Promise) {
    return true;
  }

  const isThenable = typeof maybeThennable.then === "function" &&
    typeof maybeThennable.catch === "function";

  return isThenable && typeof maybeThennable !== "function";
}

const CallTracker_ = deprecate(
  CallTracker,
  "assert.CallTracker is deprecated.",
  "DEP0173",
);

Object.assign(strict, {
  AssertionError,
  CallTracker: CallTracker_,
  deepEqual: deepStrictEqual,
  deepStrictEqual,
  doesNotMatch,
  doesNotReject,
  doesNotThrow,
  equal: strictEqual,
  fail,
  ifError,
  match,
  notDeepEqual: notDeepStrictEqual,
  notDeepStrictEqual,
  notEqual: notStrictEqual,
  notStrictEqual,
  ok,
  rejects,
  strict,
  strictEqual,
  throws,
});

export default Object.assign(assert, {
  AssertionError,
  CallTracker: CallTracker_,
  deepEqual,
  deepStrictEqual,
  doesNotMatch,
  doesNotReject,
  doesNotThrow,
  equal,
  fail,
  ifError,
  match,
  notDeepEqual,
  notDeepStrictEqual,
  notEqual,
  notStrictEqual,
  ok,
  rejects,
  strict,
  strictEqual,
  throws,
});

export {
  AssertionError,
  CallTracker_ as CallTracker,
  deepEqual,
  deepStrictEqual,
  doesNotMatch,
  doesNotReject,
  doesNotThrow,
  equal,
  fail,
  ifError,
  match,
  notDeepEqual,
  notDeepStrictEqual,
  notEqual,
  notStrictEqual,
  ok,
  rejects,
  strict,
  strictEqual,
  throws,
};
