"use strict";

var testHelper = require("../test-helper");
var captureArgs = require("../test-helper/capture-args");

var obj = { id: 42 };

testHelper.assertionTests("assert", "equals", function(pass, fail, msg, error) {
    var func = function() {};
    var arr = [];
    var date = new Date();
    var sameDate = new Date(date.getTime());
    var anotherDate = new Date(date.getTime() - 10);

    pass("when comparing object to itself", obj, obj);
    pass("when comparing strings", "Hey", "Hey");
    pass("when comparing numbers", 32, 32);
    pass("when comparing booleans", false, false);
    pass("when comparing null", null, null);
    fail("when comparing undefined", undefined, undefined);
    pass("when comparing function to itself", func, func);
    fail(
        "when comparing functions",
        function() {},
        function() {}
    );
    pass("when comparing array to itself", arr, arr);
    pass("when comparing date objects with same date", date, sameDate);
    fail("when comparing date objects with different dates", date, anotherDate);
    fail("when comparing date objects to null", date, null);
    fail("when comparing strings and numbers with coercion", "4", 4);
    fail("when comparing numbers and strings with coercion", 4, "4");
    // eslint-disable-next-line no-new-wrappers
    fail("when comparing number object with coercion", 32, new Number(32));
    // eslint-disable-next-line no-new-wrappers
    fail(
        "when comparing number object reverse with coercion",
        // eslint-disable-next-line no-new-wrappers
        new Number(32),
        32
    );
    fail("when comparing falsy values with coercion", 0, "");
    fail("when comparing falsy values reverse with coercion", "", 0);
    // eslint-disable-next-line no-new-wrappers
    fail("when comparing string boxing with coercion", "4", new String("4"));
    // eslint-disable-next-line no-new-wrappers
    fail(
        "when comparing string boxing reverse with coercion",
        // eslint-disable-next-line no-new-wrappers
        new String("4"),
        "4"
    );
    pass("when comparing NaN to NaN", NaN, NaN);
    fail("when comparing -0 to +0", -0, +0);
    fail(
        "when comparing objects with different own properties",
        { id: 42 },
        { id: 42, di: 24 }
    );
    fail(
        "when comparing objects with different own properties #2",
        { id: undefined },
        { di: 24 }
    );
    fail(
        "when comparing objects with different own properties #3",
        { id: 24 },
        { di: undefined }
    );
    pass("when comparing objects with one property", { id: 42 }, { id: 42 });
    pass(
        "when comparing objects with one object property",
        { obj: { id: 42 } },
        { obj: { id: 42 } }
    );
    fail(
        "when comparing objects with one property with different values",
        { id: 42 },
        { id: 24 }
    );

    var deepObject = {
        id: 42,
        name: "Hey",
        sayIt: function() {
            return this.name;
        },

        child: {
            speaking: function() {}
        }
    };

    pass("when comparing complex objects", deepObject, {
        sayIt: deepObject.sayIt,
        child: { speaking: deepObject.child.speaking },
        id: 42,
        name: "Hey"
    });

    pass(
        "when comparing arrays",
        [1, 2, "Hey there", func, { id: 42, prop: [2, 3] }],
        [1, 2, "Hey there", func, { id: 42, prop: [2, 3] }]
    );

    pass("when comparing regexp literals", /a/, /a/);
    pass(
        "when comparing regexp objects",
        new RegExp("[a-z]+"),
        new RegExp("[a-z]+")
    );

    var re1 = new RegExp("[a-z]+");
    var re2 = new RegExp("[a-z]+");
    re2.id = 42;

    fail("when comparing nested array with shallow array", [["hey"]], ["hey"]);
    fail("when comparing regexp objects with custom properties", re1, re2);
    fail("when comparing different objects", { id: 42 }, {});
    fail("when comparing object to null", {}, null);
    fail("when comparing object to undefined", {}, undefined);
    fail("when comparing object to false", {}, false);
    fail("when comparing false to object", false, {});
    fail("when comparing object to true", {}, true);
    fail("when comparing true to object", true, {});
    fail("when comparing 'empty' object to date", {}, new Date());
    // eslint-disable-next-line no-new-wrappers
    fail("when comparing 'empty' object to string object", {}, new String());
    // eslint-disable-next-line no-new-wrappers
    fail("when comparing 'empty' object to number object", {}, new Number());
    fail("when comparing 'empty' object to empty array", {}, []);

    var arrayLike = { length: 4, "0": 1, "1": 2, "2": {}, "3": [] };

    pass("when comparing empty arguments to empty array", captureArgs(), []);

    fail("when comparing empty array to empty arguments", [], captureArgs());

    pass(
        "when comparing arguments with elements to array with equal elements",
        captureArgs(1, 2, {}, []),
        [1, 2, {}, []]
    );

    pass(
        "when comparing arguments to array like object",
        captureArgs(1, 2, {}, []),
        arrayLike
    );

    msg(
        "fail with understandable message",
        "[assert.equals] {  } expected to be equal to Hey",
        {},
        "Hey"
    );

    msg(
        "fail with custom message",
        "[assert.equals] Here: {  } expected to be equal to Hey",
        {},
        "Hey",
        "Here:"
    );

    msg(
        "fail for multi-line strings",
        "[assert.equals] Yo!\\nMultiline expected to be equal to Yo!\\nHey",
        "Yo!\nMultiline",
        "Yo!\nHey"
    );

    msg(
        "fail for multi-line strings with more than one newline",
        "[assert.equals] Yo!\\nMulti-\\nline expected to be equal to Yo!\\nHey",
        "Yo!\nMulti-\nline",
        "Yo!\nHey"
    );

    msg(
        "fail with regular message for one-line strings",
        "[assert.equals] Yo expected to be equal to Hey",
        "Yo",
        "Hey"
    );
    error(
        "when comparing strings",
        {
            code: "ERR_ASSERTION",
            actual: "foo",
            expected: "bar",
            operator: "assert.equals"
        },
        "foo",
        "bar"
    );
    error(
        "when comparing different objects",
        {
            code: "ERR_ASSERTION",
            actual: { id: 42 },
            expected: {},
            operator: "assert.equals"
        },
        { id: 42 },
        {}
    );
});

testHelper.assertionTests("refute", "equals", function(pass, fail, msg, error) {
    fail("when comparing object to itself", obj, obj);
    fail("when comparing strings", "Hey", "Hey");
    fail("when comparing numbers", 32, 32);
    fail("when comparing booleans", false, false);
    fail("when comparing null", null, null);
    fail("when comparing undefined", undefined, undefined);

    var func = function() {};
    var arr = [];
    var date = new Date();
    var sameDate = new Date(date.getTime());
    var anotherDate = new Date(date.getTime() - 10);

    fail("when comparing function to itself", func, func);
    pass(
        "when comparing functions",
        function() {},
        function() {}
    );
    fail("when comparing array to itself", arr, arr);
    fail("when comparing date objects with same date", date, sameDate);
    pass("when comparing date objects with different dates", date, anotherDate);
    pass("when comparing date objects to null", new Date(), null);
    pass("when comparing string with number with coercion", "4", 4);
    pass("when comparing number with string with coercion", 32, "32");
    pass("when comparing with coercion", 0, "");
    pass(
        "when comparing objects with different own properties",
        { id: 42 },
        { id: 42, di: 24 }
    );
    pass(
        "when comparing objects with different own properties #2",
        { id: undefined },
        { di: 24 }
    );
    pass(
        "when comparing objects with different own properties #3",
        { id: 24 },
        { di: undefined }
    );
    fail("when comparing objects with one property", { id: 42 }, { id: 42 });
    fail(
        "when comparing objects with one object property",
        { obj: { id: 42 } },
        { obj: { id: 42 } }
    );
    pass(
        "when comparing objects with one property with different values",
        { id: 42 },
        { id: 24 }
    );
    fail("when comparing NaN to NaN", NaN, NaN);
    pass("when comparing -0 to +0", -0, +0);

    var deepObject = {
        id: 42,
        name: "Hey",
        sayIt: function() {
            return this.name;
        },

        child: {
            speaking: function() {}
        }
    };

    fail("when comparing complex objects", deepObject, {
        sayIt: deepObject.sayIt,
        child: { speaking: deepObject.child.speaking },
        id: 42,
        name: "Hey"
    });

    var arr1 = [1, 2, "Hey there", func, { id: 42, prop: [2, 3] }];
    var arr2 = [1, 2, "Hey there", func, { id: 42, prop: [2, 3] }];

    fail("when comparing arrays", arr1, arr2);
    fail("when comparing regexp literals", /a/, /a/);

    fail(
        "when comparing regexp objects",
        new RegExp("[a-z]+"),
        new RegExp("[a-z]+")
    );

    var re1 = new RegExp("[a-z]+");
    var re2 = new RegExp("[a-z]+");
    re2.id = 42;

    pass("when comparing regexp objects with custom properties", re1, re2);
    pass("when comparing different objects", obj, {});
    pass("when comparing object to null", {}, null);
    pass("when comparing null to object", {}, null);
    fail("when comparing object to undefined", {}, undefined);
    pass("when comparing undefined to object", undefined, {});
    pass("when comparing object to false", {}, false);
    pass("when comparing false to object", false, {});
    pass("when comparing object to true", {}, true);
    pass("when comparing true to object", true, {});
    pass("when comparing 'empty' object to date", {}, new Date());
    // eslint-disable-next-line no-new-wrappers
    pass("when comparing 'empty' object to string object", {}, new String());
    // eslint-disable-next-line no-new-wrappers
    pass("when comparing 'empty' object to number object", {}, new Number());
    pass("when comparing 'empty' object to empty array", {}, []);
    pass("when comparing multi-line strings", "Hey\nHo", "Yo\nNo");

    var arrayLike = { length: 4, "0": 1, "1": 2, "2": {}, "3": [] };

    fail(
        "when comparing arguments with elements to array with equal elements",
        captureArgs(1, 2, {}, []),
        [1, 2, {}, []]
    );

    fail("when comparing empty arguments to empty array", captureArgs(), []);

    pass("when comparing empty array to empty arguments", [], captureArgs());

    fail(
        "when comparing arguments to array like object",
        captureArgs(1, 2, {}, []),
        arrayLike
    );

    msg(
        "fail with understandable message",
        "[refute.equals] {  } expected not to be equal to {  }",
        {},
        {}
    );

    msg(
        "fail with custom message",
        "[refute.equals] Eh? {  } expected not to be equal to {  }",
        {},
        {},
        "Eh?"
    );
    error(
        "when comparing strings",
        {
            code: "ERR_ASSERTION",
            operator: "refute.equals"
        },
        "foo",
        "foo"
    );
    error(
        "when comparing multi-line strings",
        {
            code: "ERR_ASSERTION",
            operator: "refute.equals"
        },
        "foo\nbar",
        "foo\nbar"
    );
    error(
        "when comparing different objects",
        {
            code: "ERR_ASSERTION",
            operator: "refute.equals"
        },
        { id: 42 },
        { id: 42 }
    );
});
