"use strict";

var assert = require("assert");
var sinon = require("sinon");
var referee = require("./referee");

describe("add", function () {
  context("when passed an invalid name argument", function () {
    it("should throw a TypeError", function () {
      var expected =
        "'name' argument must be a non-empty string matching /^[_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*$/";
      var error;
      try {
        referee.add("invalid name");
      } catch (err) {
        error = err;
      }

      assert.equal(error.message, expected);
      assert(error instanceof TypeError);
    });
  });

  context("when passed an invalid options argument", function () {
    it("should throw a TypeError", function () {
      var expected = "'options' argument must be a non-empty object";
      var options = {};
      var error;

      try {
        referee.add("isApplePie", options);
      } catch (err) {
        error = err;
      }

      assert.equal(error.message, expected);
      assert(error instanceof TypeError);
    });
  });

  context("when called with invalid assert option", function () {
    it("should throw a TypeError", function () {
      var expected =
        "'assert' option must be a Function, taking at least one argument";
      var options = {
        assert: function () {},
      };
      var error;

      try {
        referee.add("isApplePie", options);
      } catch (err) {
        error = err;
      }

      assert.equal(error.message, expected);
      assert(error instanceof TypeError);
    });
  });

  context("when called with invalid refute option", function () {
    it("should throw a TypeError", function () {
      var expected =
        "'refute' option must be a Function, taking at least one argument";
      var options = {
        assert: function (actual) {
          return actual;
        },
        refute: function () {},
      };
      var error;

      try {
        referee.add("isApplePie", options);
      } catch (err) {
        error = err;
      }

      assert.equal(error.message, expected);
      assert(error instanceof TypeError);
    });
  });

  context("when called with invalid assertMessage option", function () {
    it("should throw a TypeError", function () {
      var expected = "'assertMessage' option must be a non-empty String";
      var options = {
        assert: function (actual) {
          return actual;
        },
        refute: function (actual) {
          return actual;
        },
        assertMessage: "",
      };
      var error;

      try {
        referee.add("isApplePie", options);
      } catch (err) {
        error = err;
      }

      assert.equal(error.message, expected);
      assert(error instanceof TypeError);
    });
  });

  context("when called with invalid refuteMessage option", function () {
    it("should throw a TypeError", function () {
      var expected = "'refuteMessage' option must be a non-empty String";
      var options = {
        assert: function (actual) {
          return actual;
        },
        refute: function (actual) {
          return actual;
        },
        assertMessage: "apple pie",
        refuteMessage: "",
      };
      var error;

      try {
        referee.add("isApplePie", options);
      } catch (err) {
        error = err;
      }

      assert.equal(error.message, expected);
      assert(error instanceof TypeError);
    });
  });

  context("with assert returning a promise", function () {
    before(function () {
      if (typeof Promise === "undefined") {
        this.skip();
      }
    });

    it("returns a promise and calls referee.pass when resolved", function () {
      var then = sinon.fake();
      var options = {
        assert: function (thing) {
          return thing && new Promise(then);
        },
        assertMessage: "returning a promise",
        refuteMessage: "not returning a promise",
      };
      referee.add("returningPromise", options);

      var result = referee.assert.returningPromise(1);

      assert(result instanceof Promise);

      then.firstCall.args[0](); // Get coverage on then handler
    });
  });
});
