"use strict";

var sinon = require("sinon");
var events = require("../../").Runner.constants;
var helpers = require("./helpers");
var reporters = require("../../").reporters;

var Base = reporters.Base;
var List = reporters.List;
var createMockRunner = helpers.createMockRunner;
var makeRunReporter = helpers.createRunReporterFunction;

var EVENT_RUN_BEGIN = events.EVENT_RUN_BEGIN;
var EVENT_RUN_END = events.EVENT_RUN_END;
var EVENT_TEST_BEGIN = events.EVENT_TEST_BEGIN;
var EVENT_TEST_FAIL = events.EVENT_TEST_FAIL;
var EVENT_TEST_PASS = events.EVENT_TEST_PASS;
var EVENT_TEST_PENDING = events.EVENT_TEST_PENDING;

describe("List reporter", function () {
  var runReporter = makeRunReporter(List);
  var expectedTitle = "some title";
  var expectedDuration = 100;
  var noop = function () {};
  var test = {
    fullTitle: function () {
      return expectedTitle;
    },
    duration: expectedDuration,
    slow: noop,
  };

  beforeEach(function () {
    sinon.stub(Base, "useColors").value(false);
  });

  afterEach(function () {
    sinon.restore();
  });

  describe("event handlers", function () {
    describe("on 'start' and 'test' events", function () {
      it("should write expected newline and title", function () {
        var runner = createMockRunner(
          "start test",
          EVENT_RUN_BEGIN,
          EVENT_TEST_BEGIN,
          null,
          test,
        );
        var options = {};
        var fakeThis = {
          epilogue: noop,
        };
        var stdout = runReporter(fakeThis, runner, options);
        sinon.restore();

        var startString = "\n";
        var testString = "    " + expectedTitle + ": ";
        var expectedArray = [startString, testString];
        expect(stdout, "to equal", expectedArray);
      });
    });

    describe("on 'pending' event", function () {
      it("should write expected title", function () {
        var runner = createMockRunner(
          "pending test",
          EVENT_TEST_PENDING,
          null,
          null,
          test,
        );
        var options = {};
        var fakeThis = {
          epilogue: noop,
        };
        var stdout = runReporter(fakeThis, runner, options);
        sinon.restore();

        expect(stdout[0], "to equal", "  - " + expectedTitle + "\n");
      });
    });

    describe("on 'pass' event", function () {
      var crStub;

      beforeEach(function () {
        crStub = sinon.stub(Base.cursor, "CR").callsFake(noop);
      });

      it("should call cursor CR", function () {
        var runner = createMockRunner(
          "pass",
          EVENT_TEST_PASS,
          null,
          null,
          test,
        );
        var options = {};
        var fakeThis = {
          epilogue: noop,
        };
        runReporter(fakeThis, runner, options);
        sinon.restore();

        expect(crStub.called, "to be true");
      });

      it("should write expected symbol, title, and duration", function () {
        var expectedOkSymbol = "OK";
        sinon.stub(Base.symbols, "ok").value(expectedOkSymbol);

        var runner = createMockRunner(
          "pass",
          EVENT_TEST_PASS,
          null,
          null,
          test,
        );
        var options = {};
        var fakeThis = {
          epilogue: noop,
        };
        var stdout = runReporter(fakeThis, runner, options);
        sinon.restore();

        expect(
          stdout[0],
          "to be",
          "  " +
            expectedOkSymbol +
            " " +
            expectedTitle +
            ": " +
            expectedDuration +
            "ms\n",
        );
      });
    });

    describe("on 'fail' event", function () {
      var crStub;

      beforeEach(function () {
        crStub = sinon.stub(Base.cursor, "CR").callsFake(noop);
      });

      it("should call cursor CR", function () {
        var runner = createMockRunner(
          "fail",
          EVENT_TEST_FAIL,
          null,
          null,
          test,
        );
        var options = {};
        var fakeThis = {
          epilogue: noop,
        };
        runReporter(fakeThis, runner, options);
        sinon.restore();

        expect(crStub.called, "to be true");
      });

      it("should write expected error number and title", function () {
        var expectedErrorCount = 1;
        var runner = createMockRunner(
          "fail",
          EVENT_TEST_FAIL,
          null,
          null,
          test,
        );
        var options = {};
        var fakeThis = {
          epilogue: noop,
        };
        var stdout = runReporter(fakeThis, runner, options);
        sinon.restore();

        expect(
          stdout[0],
          "to be",
          "  " + expectedErrorCount + ") " + expectedTitle + "\n",
        );
      });

      it("should immediately construct fail strings", function () {
        var actual = { a: "actual" };
        var expected = { a: "expected" };
        var checked = false;
        var err;
        test = {};

        var runner = createMockRunner(
          "fail",
          EVENT_TEST_FAIL,
          null,
          null,
          test,
        );
        runner.on = runner.once = function (event, callback) {
          if (
            !checked &&
            event === "fail" &&
            callback.toString().includes("stringifyDiffObjs") // target correct fail event callback
          ) {
            err = new Error("fake failure object with actual/expected");
            err.actual = actual;
            err.expected = expected;
            err.showDiff = true;
            callback(test, err);
            checked = true;
          }
        };
        var options = {};
        var fakeThis = {
          epilogue: noop,
        };
        runReporter(fakeThis, runner, options);
        sinon.restore();

        expect(typeof err.actual, "to be", "string");
        expect(typeof err.expected, "to be", "string");
      });
    });

    describe("on 'end' event", function () {
      it("should call epilogue", function () {
        var runner = createMockRunner("end", EVENT_RUN_END);
        var options = {};
        var fakeThis = {
          epilogue: sinon.spy(),
        };
        runReporter(fakeThis, runner, options);
        sinon.restore();

        expect(fakeThis.epilogue.calledOnce, "to be true");
      });
    });
  });
});
