"use strict";

var runMochaJSON = require("./helpers").runMochaJSON;
var invokeMocha = require("./helpers").invokeMocha;
var MULTIPLE_DONE = require("../../lib/error-constants").constants
  .MULTIPLE_DONE;

describe("multiple calls to done()", function () {
  var res;
  describe("from a spec", function () {
    before(function (done) {
      runMochaJSON("multiple-done", function (err, result) {
        res = result;
        done(err);
      });
    });

    it("results in failure", function () {
      expect(res, "to have failed test count", 1)
        .and("to have passed test count", 1)
        .and("to have pending test count", 0)
        .and("to have failed");
    });

    it("throws a descriptive error", function () {
      expect(res, "to have failed with error", {
        message:
          /done\(\) called multiple times in test <should fail in a test-case> \(of root suite\) of file.+multiple-done\.fixture\.js/,
        code: MULTIPLE_DONE,
      });
    });
  });

  describe("with error passed on second call", function () {
    before(function (done) {
      runMochaJSON("multiple-done-with-error", function (err, result) {
        res = result;
        done(err);
      });
    });

    it("results in failure", function () {
      expect(res, "to have failed test count", 1)
        .and("to have passed test count", 1)
        .and("to have pending test count", 0)
        .and("to have failed");
    });

    it("should throw a descriptive error", function () {
      expect(res, "to have failed with error", {
        message:
          /done\(\) called multiple times in test <should fail in a test-case> \(of root suite\) of file.+multiple-done-with-error\.fixture\.js; in addition, done\(\) received error: Error: second error/,
        code: MULTIPLE_DONE,
      });
    });
  });

  describe("with multiple specs", function () {
    before(function (done) {
      runMochaJSON("multiple-done-specs", function (err, result) {
        res = result;
        done(err);
      });
    });

    it("results in failure", function () {
      expect(res, "to have failed test count", 1)
        .and("to have passed test count", 2)
        .and("to have pending test count", 0)
        .and("to have failed");
    });

    it("correctly attributes the error", function () {
      expect(res.failures[0], "to satisfy", {
        fullTitle: "suite test1",
        err: {
          message:
            /done\(\) called multiple times in test <suite test1> of file.+multiple-done-specs\.fixture\.js/,
          code: MULTIPLE_DONE,
        },
      });
    });
  });

  describe("from a before hook", function () {
    before(function (done) {
      runMochaJSON("multiple-done-before", function (err, result) {
        res = result;
        done(err);
      });
    });

    it("results in failure", function () {
      expect(res, "to have failed test count", 1)
        .and("to have passed test count", 1)
        .and("to have pending test count", 0)
        .and("to have failed");
    });

    it("correctly attributes the error", function () {
      expect(res.failures[0], "to satisfy", {
        fullTitle: 'suite1 "before all" hook in "suite1"',
        err: {
          message:
            /done\(\) called multiple times in hook <suite1 "before all" hook in "suite1"> of file.+multiple-done-before\.fixture\.js/,
        },
      });
    });
  });

  describe('from a "before each" hook', function () {
    before(function (done) {
      runMochaJSON("multiple-done-before-each", function (err, result) {
        res = result;
        done(err);
      });
    });

    it("results in a failure", function () {
      expect(res, "to have failed test count", 2)
        .and("to have passed test count", 2)
        .and("to have pending test count", 0)
        .and("to have exit code", 2);
    });

    it("correctly attributes the errors", function () {
      expect(res.failures[0], "to equal", res.failures[1]).and("to satisfy", {
        fullTitle: 'suite1 "before each" hook in "suite1"',
        err: {
          message:
            /done\(\) called multiple times in hook <suite1 "before each" hook in "suite1"> of file.+multiple-done-before-each\.fixture\.js/,
          multiple: [
            {
              code: "ERR_MOCHA_MULTIPLE_DONE",
            },
          ],
        },
      });
    });
  });

  describe("when done() called asynchronously", function () {
    before(function (done) {
      // we can't be sure that mocha won't fail with an uncaught exception here, which would cause any JSON
      // output to be befouled; we need to run "raw" and capture STDERR
      invokeMocha(
        require.resolve("./fixtures/multiple-done-async.fixture.js"),
        function (err, result) {
          res = result;
          done(err);
        },
        "pipe",
      );
    });

    it("results in error", function () {
      expect(res, "to satisfy", {
        code: expect.it("to be greater than", 0),
        output:
          /done\(\) called multiple times in test <should fail in an async test case> \(of root suite\) of file.+multiple-done-async\.fixture\.js/,
      });
    });
  });
});
