/*jslint onevar: false, eqeqeq: false*/
/*globals document sinon buster*/
/**
 * @author Christian Johansen (christian@cjohansen.no)
 * @license BSD
 *
 * Copyright (c) 2010-2012 Christian Johansen
 */
"use strict";

if (typeof require == "function" && typeof module == "object") {
    var buster = require("./runner");
    var sinon = require("../lib/sinon");
}

buster.testCase("sinon", {
    ".wrapMethod": {
        setUp: function () {
            this.method = function () {};
            this.object = { method: this.method };
        },

        "is function": function () {
            assert.isFunction(sinon.wrapMethod);
        },

        "throws if first argument is not object": function () {
            assert.exception(function () {
                sinon.wrapMethod();
            }, "TypeError");
        },

        "throws if object defines property but is not function": function () {
            this.object.prop = 42;
            var object = this.object;

            assert.exception(function () {
                sinon.wrapMethod(object, "prop", function () {});
            }, "TypeError");
        },

        "throws if object does not define property": function () {
            var object = this.object;

            assert.exception(function () {
                sinon.wrapMethod(object, "prop", function () {});
            });
        },

        "throws if third argument is missing": function () {
            var object = this.object;

            assert.exception(function () {
                sinon.wrapMethod(object, "method");
            }, "TypeError");
        },

        "throws if third argument is not function": function () {
            var object = this.object;

            assert.exception(function () {
                sinon.wrapMethod(object, "method", {});
            }, "TypeError");
        },

        "replaces object method": function () {
            sinon.wrapMethod(this.object, "method", function () {});

            refute.same(this.method, this.object.method);
            assert.isFunction(this.object.method);
        },

        "throws if method is already wrapped": function () {
            var object = { method: function () {} };
            sinon.wrapMethod(object, "method", function () {});

            assert.exception(function () {
                sinon.wrapMethod(object, "method", function () {});
            }, "TypeError");
        },

        "throws if method is already a spy": function () {
            var object = { method: sinon.spy() };

            assert.exception(function () {
                sinon.wrapMethod(object, "method", function () {});
            }, "TypeError");
        },

        "in browser": {
            requiresSupportFor: {
                "window object": typeof window !== "undefined"
            },

            "does not throw if object is window object": function () {
                window.sinonTestMethod = function () {};
                try {
                    refute.exception(function () {
                        sinon.wrapMethod(window, "sinonTestMethod", function () {});
                    });
                } finally {
                    // IE 8 does not support delete on global properties.
                    window.sinonTestMethod = undefined;
                }
            }
        },

        "mirrors function properties": function () {
            var object = { method: function () {} };
            object.method.prop = 42;

            sinon.wrapMethod(object, "method", function () {});

            assert.equals(object.method.prop, 42);
        },

        "does not mirror and overwrite existing properties": function () {
            var object = { method: function () {} };
            object.method.called = 42;

            sinon.stub(object, "method");

            assert.isFalse(object.method.called);
        }
    },

    "wrapped method": {
        setUp: function () {
            this.method = function () {};
            this.object = { method: this.method };
        },

        "defines restore method": function () {
            sinon.wrapMethod(this.object, "method", function () {});

            assert.isFunction(this.object.method.restore);
        },

        "returns wrapper": function () {
            var wrapper = sinon.wrapMethod(this.object, "method", function () {});

            assert.same(this.object.method, wrapper);
        },

        "restore brings back original method": function () {
            sinon.wrapMethod(this.object, "method", function () {});
            this.object.method.restore();

            assert.same(this.object.method, this.method);
        }
    },

    "wrapped prototype method": {
        setUp: function () {
            this.type = function () {};
            this.type.prototype.method = function () {};
            this.object = new this.type();
        },

        "wrap adds owned property": function () {
            var wrapper = sinon.wrapMethod(this.object, "method", function () {});

            assert.same(this.object.method, wrapper);
            assert(this.object.hasOwnProperty("method"));
        },

        "restore removes owned property": function () {
            sinon.wrapMethod(this.object, "method", function () {});
            this.object.method.restore();

            assert.same(this.object.method, this.type.prototype.method);
            assert.isFalse(this.object.hasOwnProperty("method"));
        }
    },

    "deepEqual": {
        "passes null": function () {
            assert(sinon.deepEqual(null, null));
        },

        "fails null and object": function () {
            assert.isFalse(sinon.deepEqual(null, {}));
        },

        "fails object and null": function () {
            assert.isFalse(sinon.deepEqual({}, null));
        },

        "fails error and object": function () {
            assert.isFalse(sinon.deepEqual(new Error(), {}));
        },

        "fails object and error": function () {
            assert.isFalse(sinon.deepEqual({}, new Error()));
        },

        "fails regexp and object": function () {
            assert.isFalse(sinon.deepEqual(/.*/, {}));
        },

        "fails object and regexp": function () {
            assert.isFalse(sinon.deepEqual({}, /.*/));
        },

        "passes primitives": function () {
            assert(sinon.deepEqual(1, 1));
        },

        "passes same object": function () {
            var object = {};

            assert(sinon.deepEqual(object, object));
        },

        "passes same function": function () {
            var func = function () {};

            assert(sinon.deepEqual(func, func));
        },

        "passes same array": function () {
            var arr = [];

            assert(sinon.deepEqual(arr, arr));
        },

        "passes equal arrays": function () {
            var arr1 = [1, 2, 3, "hey", "there"];
            var arr2 = [1, 2, 3, "hey", "there"];

            assert(sinon.deepEqual(arr1, arr2));
        },

        "passes equal objects": function () {
            var obj1 = { a: 1, b: 2, c: 3, d: "hey", e: "there" };
            var obj2 = { b: 2, c: 3, a: 1, d: "hey", e: "there" };

            assert(sinon.deepEqual(obj1, obj2));
        },

        "in browsers": {
            requiresSupportFor: {
                "document object": typeof document !== "undefined"
            },

            "passes same DOM elements": function () {
                var element = document.createElement("div");

                assert(sinon.deepEqual(element, element));
            },

            "fails different DOM elements": function () {
                var element = document.createElement("div");
                var el = document.createElement("div");

                assert.isFalse(sinon.deepEqual(element, el));
            },

            "does not modify DOM elements when comparing them": function () {
                var el = document.createElement("div");
                document.body.appendChild(el);
                sinon.deepEqual(el, {});

                assert.same(el.parentNode, document.body);
                assert.equals(el.childNodes.length, 0);
            }
        },

        "passes deep objects": function () {
            var func = function () {};

            var obj1 = {
                a: 1,
                b: 2,
                c: 3,
                d: "hey",
                e: "there",
                f: func,
                g: {
                    a1: [1, 2, "3", {
                        prop: [func, "b"]
                    }]
                }
            };

            var obj2 = {
                a: 1,
                b: 2,
                c: 3,
                d: "hey",
                e: "there",
                f: func,
                g: {
                    a1: [1, 2, "3", {
                        prop: [func, "b"]
                    }]
                }
            };

            assert(sinon.deepEqual(obj1, obj2));
        }
    },

    "extend": {
        "copies all properties": function () {
            var object1 = {
                prop1: null,
                prop2: false
            };

            var object2 = {
                prop3: "hey",
                prop4: 4
            };

            var result = sinon.extend({}, object1, object2);

            var expected = {
                prop1: null,
                prop2: false,
                prop3: "hey",
                prop4: 4
            };

            assert.equals(result, expected);
        }
    },

    "Function.prototype.toString": {
        "returns function's displayName property": function () {
            var fn = function () {};
            fn.displayName = "Larry";

            assert.equals(sinon.functionToString.call(fn), "Larry");
        },

        "guesses name from last call's this object": function () {
            var obj = {};
            obj.doStuff = sinon.spy();
            obj.doStuff.call({});
            obj.doStuff();

            assert.equals(sinon.functionToString.call(obj.doStuff), "doStuff");
        },

        "guesses name from any call where property can be located": function () {
            var obj = {}, otherObj = { id: 42 };
            obj.doStuff = sinon.spy();
            obj.doStuff.call({});
            obj.doStuff();
            obj.doStuff.call(otherObj);

            assert.equals(sinon.functionToString.call(obj.doStuff), "doStuff");
        }
    },

    "config": {
        "gets copy of default config": function () {
            var config = sinon.getConfig();

            refute.same(config, sinon.defaultConfig);
            assert.equals(config.injectIntoThis, sinon.defaultConfig.injectIntoThis);
            assert.equals(config.injectInto, sinon.defaultConfig.injectInto);
            assert.equals(config.properties, sinon.defaultConfig.properties);
            assert.equals(config.useFakeTimers, sinon.defaultConfig.useFakeTimers);
            assert.equals(config.useFakeServer, sinon.defaultConfig.useFakeServer);
        },

        "should override specified properties": function () {
            var config = sinon.getConfig({
                properties: ["stub", "mock"],
                useFakeServer: false
            });

            refute.same(config, sinon.defaultConfig);
            assert.equals(config.injectIntoThis, sinon.defaultConfig.injectIntoThis);
            assert.equals(config.injectInto, sinon.defaultConfig.injectInto);
            assert.equals(config.properties, ["stub", "mock"]);
            assert.equals(config.useFakeTimers, sinon.defaultConfig.useFakeTimers);
            assert.isFalse(config.useFakeServer);
        }
    },

    "log": {
        "does nothing gracefully": function () {
            refute.exception(function () {
                sinon.log("Oh, hiya");
            });
        }
    },

    "format": {
        "formats with buster by default": function () {
            assert.equals(sinon.format({ id: 42 }), "{ id: 42 }");
        },

        "formats strings without quotes": function () {
            assert.equals(sinon.format("Hey"), "Hey");
        }
    },

    "typeOf": {
        "returns boolean": function () {
            assert.equals(sinon.typeOf(false), "boolean");
        },

        "returns string": function () {
            assert.equals(sinon.typeOf("Sinon.JS"), "string");
        },

        "returns number": function () {
            assert.equals(sinon.typeOf(123), "number");
        },

        "returns object": function () {
            assert.equals(sinon.typeOf({}), "object");
        },

        "returns function": function () {
            assert.equals(sinon.typeOf(function () {}), "function");
        },

        "returns undefined": function () {
            assert.equals(sinon.typeOf(undefined), "undefined");
        },

        "returns null": function () {
            assert.equals(sinon.typeOf(null), "null");
        },

        "returns array": function () {
            assert.equals(sinon.typeOf([]), "array");
        },

        "returns regexp": function () {
            assert.equals(sinon.typeOf(/.*/), "regexp");
        },

        "returns date": function () {
            assert.equals(sinon.typeOf(new Date()), "date");
        }
    }
});
