YUI.add('jsonp-tests', function(Y) {

var suite = new Y.Test.Suite("Y.JSONPRequest and Y.jsonp with jsonp-url"),
    // Dirty dirty hack to future proof duck type for onload where
    // possible
    onScriptLoad = (function () {
        var onload = true,
            onrsc = true;

        return function (node, callback) {
            var loaded = false;

            if (onload) {
                node.onload = function () {
                    // prefer onload to onreadystatechange
                    onload = true;
                    this.onload = this.onreadystatechange = onrsc = null;
                    if (!loaded) {
                        callback();
                        loaded = true;
                    }
                };
            }
            if (onrsc) {
                node.onreadystatechange = function () {
                    if (!('loaded|complete'.indexOf(this.readyState) % 7)) {
                        // assume no onload support until onload says so.
                        // this leaks one onload function.
                        this.onreadystatechange = null;
                        // just in case onload fired first (which it shouldn't)
                        if (onrsc && !loaded) {
                            onload = false;
                            callback();
                            loaded = true;
                        }
                    }
                };
            }
        };
    })();

suite.add(new Y.Test.Case({
    name: "send",

    "config charset should be set via Y.Get.script": function () {
        var test = this,
            scripts = Y.all('script')._nodes,
            newScript;

        Y.jsonp("server/service.php?callback={callback}", function () {});

        newScript = Y.Array.filter(Y.all('script')._nodes, function (s) {
            return Y.Array.indexOf(scripts, s) === -1;
        })[0];

        Y.Assert.areSame("utf-8", newScript.charset);

        scripts.push(newScript);

        Y.jsonp("server/service.php?callback={callback}", {
            on: {
                success: function () {}
            },
            charset: "GBK"
        });

        newScript = Y.Array.filter(Y.all('script')._nodes, function (s) {
            return Y.Array.indexOf(scripts, s) === -1;
        })[0];

        Y.Assert.areSame("GBK", newScript.getAttribute("charset"));

        // to allow JSONP the chance to clean up the callback registry
        // before other tests begin.  Race condition, yes.  The alternative
        // was to wait() and resume() in the success callback, but for some
        // reason Test.Runner cleared the test's wait timer before the
        // success callback, so either the test fails because resume() is
        // called when Test.Runner doesn't think it's waiting, OR the test
        // fails if resume() is only called if Test.Runner thinks it is waiting
        // (which it doesn't) so resume() is never called.
        test.wait(function () {}, 1000);
    },

    "config attributes should be set via Y.Get.script": function () {
        var test = this,
            scripts = Y.all('script')._nodes,
            newScript;

        Y.jsonp("server/service.php?callback={callback}", {
            on: {
                success: function () { test.resume(); }
            },
            attributes: {
                // passing an attribute that is less likely to be skipped over
                // by browser whitelisting (if they do that now or will later)
                language: "javascript"
            }
        });

        newScript = Y.Array.filter(Y.all('script')._nodes, function (s) {
            return Y.Array.indexOf(scripts, s) === -1;
        })[0];

        Y.Assert.areSame("javascript", newScript.getAttribute("language"));

        // to allow JSONP the chance to clean up the callback registry before
        // other tests begin.
        test.wait();
    }
}));

suite.add(new Y.Test.Case({
    name : "callbacks",
        
    _should: {
        ignore: {
            // Get (v3.3) doesn't support onerror in recent webkit
            "failure handler in callback object should execute": true
        }
    },

    "callback function as second arg should be success handler": function () {
        var self = this;

        Y.jsonp("server/service.php?&callback={callback}", function (json) {
            //console.log(Y.Object.keys(YUI.Env.JSONP), "callback function as second arg should be success handler");
            self.resume(function () {
                Y.Assert.isObject(json);
            });
        });

        self.wait();
    },

    "success handler in callback object should execute": function () {
        var self = this;

        Y.jsonp("server/service.php?&callback={callback}", {
            on: {
                success: function (json) {
                    //console.log(Y.Object.keys(YUI.Env.JSONP), "success handler in callback object should execute");
                    self.resume(function () {
                        Y.Assert.isObject(json);
                    });
                }
            }
        });

        self.wait();
    },

    "failure handler in callback object should execute": function () {
        var self = this;

        Y.jsonp("server/404.php?&callback={callback}", {
            on: {
                success: function (json) {
                    self.resume(function () {
                        Y.Assert.fail("Success handler called from 404 response");
                    });
                },
                failure: function () {
                    //console.log("failure handler in callback object should execute");
                    // Pass
                    self.resume(function () {});
                }
            }
        });

        self.wait();
    },

    "failure handler in callback object should not execute for successful io": function () {
        var self = this;

        Y.jsonp("server/service.php?&callback={callback}", {
            on: {
                success: function (json) {
                    //console.log(Y.Object.keys(YUI.Env.JSONP), "failure handler in callback object should not execute for successful io");
                    // Pass
                    self.resume(function () {});
                },
                failure: function () {
                    self.resume(function () {
                        Y.Assert.fail("Failure handler called after successful response");
                    });
                }
            }
        });

        self.wait();
    },

    "test multiple send() from an instance of Y.JSONPRequest": function () {
        var self = this,
            count = 0,
            service;

        service = new Y.JSONPRequest("server/service.php?callback={callback}", {
            on: {
                success: function (json) {
                    //console.log(Y.Object.keys(YUI.Env.JSONP), "test multiple send() from an instance of Y.JSONPRequest");
                    if (++count === 3) {
                        self.resume(function () {
                            // Pass
                            Y.Assert.areSame(count, 3);
                        });
                    }
                }
            }
        });

        service.send().send().send();

        this.wait();
    }

    // failure for bogus response data (not yet implemented)
    // missing {callback} (not sure how to test. No callback would be attached.
    //      Maybe have the service create a property on YUI and have the test
    //      look for that after a time?)
    // missing success handler (logs a warning)
    // JSONPRequest + send
    // JSONPRequest + send with config overrides (not yet implemented)
}));

suite.add(new Y.Test.Case({
    name : "context and args"
        
}));

suite.add(new Y.Test.Case({
    name : "format"
        
}));

suite.add(new Y.Test.Case({
    name : "allowCache",
        
    "allowCache should preserve the same callback": function () {
        var test = this,
            remaining = 2,
            callback,
            jsonp = new Y.JSONPRequest('server/service.php?&callback={callback}', {
                allowCache: true,
                on: {
                    success: function (data) {
                        //console.log(Y.Object.keys(YUI.Env.JSONP), "allowCache should preserve the same callback");
                        if (callback) {
                            if (callback !== data.callback) {
                                test.resume(function () {
                                    Y.Assert.areSame(callback, data.callback, "callback proxy name should be reused");
                                });
                            } else if (--remaining) {
                                jsonp.send();
                            } else {
                                test.resume(function () {
                                    // Pass
                                });
                            }
                        } else {
                            callback = data.callback;
                            jsonp.send();
                        }

                    }
                }
            });

        jsonp.send();

        this.wait();
    },

    "allowCache should not clear proxy if another send() is pending response":
    function () {
        var test = this,
            callbacks = [],
            jsonp = new Y.JSONPRequest('server/service.php?&callback={callback}', {
                allowCache: true,
                on: {
                    success: function (data) {
                        //console.log(Y.Object.keys(YUI.Env.JSONP), "allowCache should not clear proxy if another send() is pending response");
                        callbacks.push(data.callback);

                        if (callbacks.length > 2) {
                            test.resume(function () {
                                Y.Assert.areSame(callbacks[0], callbacks[1]);
                                Y.Assert.areSame(callbacks[1], callbacks[2]);
                                Y.Assert.isUndefined(YUI.Env.JSONP[callbacks[0]]);
                            });
                        } else if (!YUI.Env.JSONP[data.callback.split(/\./).pop()]) {
                            test.resume(function () {
                                Y.Assert.fail("proxy cleared prematurely");
                            });
                        }

                    }
                }
            });

        jsonp.send();
        jsonp.send();
        jsonp.send();

        this.wait();
    }

}));

suite.add(new Y.Test.Case({
    name : "timeout",
        
    "timeout should not flush the global proxy": function () {
        var test = this,
            timeoutCalled = false,
            jsonpProxies = Y.Object.keys(YUI.Env.JSONP).length,
            jsonp = new Y.JSONPRequest('server/service.php?&wait=2&callback={callback}', {
                timeout: 1000,
                on: {
                    success: function (data) {
                        //console.log(Y.Object.keys(YUI.Env.JSONP), "timeout should not flush the global proxy");
                        test.resume(function () {
                            Y.Assert.fail("Success callback executed after timeout");
                        });
                    },
                    timeout: function () {
                        //console.log(Y.Object.keys(YUI.Env.JSONP), "timeout should not flush the global proxy (timeout)");
                        timeoutCalled = true;
                    }
                }
            }),
            scripts = Y.all('script')._nodes,
            newScript;

        Y.Assert.areSame(0, jsonpProxies, "Whar these leavings from?");

        jsonp.send();

        // Success is measured by the success callback NOT being executed,
        // but we're in an async operation, so I need something to trigger
        // a test.resume(..).  So I'm finding the added script tag and
        // hooking a separate onload handler to it, which would execute after
        // the success handler if it is called due to callbacks executing in
        // subscription order.  Not pretty, but better than a longer setTimeout?
        newScript = Y.Array.filter(Y.all('script')._nodes, function (s) {
            return Y.Array.indexOf(scripts, s) === -1;
        })[0];

        onScriptLoad(newScript, function () {
            //console.log("__yui_wait: " + test.__yui_wait + " (should be a setTimeout int)");
            // If the success callback is triggered, it will resume the test,
            // and clear the wait() timeout, so having another resume() here
            // will just blow up.  The test has already failed if !_waiting
            if (Y.Test.Runner._waiting) {
                test.resume(function () {
                    Y.Assert.isTrue(timeoutCalled);
                    Y.Assert.areSame(jsonpProxies, Y.Object.keys(YUI.Env.JSONP).length);
                });
            }
        });

        test.wait(3000);
    }

    /*
    ,

    "timeout should not flush the global proxy across multiple send calls": function () {
        // README
        // Stubbed from the test above.  This test needs to contact the
        // serverat the same url, but get varying delays.  This means to
        // properly test, the server needs to behave randomly, and this test
        // needs to iterate until that random behavior matches the expected
        // test behavior.  Which is icky.
        var test = this,
            timeoutCalled = false,
            jsonp = new Y.JSONPRequest('server/service.php?wait=2&callback={callback}', {
                timeout: 1000,
                on: {
                    success: function (data) {
                        test.resume(function () {
                            Y.Assert.fail("Success callback executed after timeout");
                        });
                    },
                    timeout: function () {
                        timeoutCalled = true;
                    }
                }
            }),
            scripts = Y.all('script')._nodes,
            newScript;

        jsonp.send();

        newScript = Y.Array.filter(Y.all('script')._nodes, function (s) {
            return Y.Array.indexOf(scripts, s) === -1;
        })[0];

        Y.on('load', function () {
            test.resume(function () {
                Y.Assert.isTrue(timeoutCalled);
            });
        }, newScript);

        test.wait(3000);
    }
    */
}));

Y.Test.Runner.add(suite);


}, '@VERSION@' ,{requires:['jsonp', 'test', 'array-extras']});
