UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

627 lines (475 loc) 14.4 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2011 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Tristan Koch (tristankoch) ************************************************************************ */ /** * * @asset(qx/test/jsonp_primitive.php) * @asset(qx/test/script.js) * @asset(qx/test/xmlhttp/sample.txt) * @ignore(SCRIPT_LOADED) */ qx.Class.define("qx.test.bom.request.Script", { extend : qx.dev.unit.TestCase, include : [qx.test.io.MRemoteTest, qx.dev.unit.MRequirements, qx.dev.unit.MMock], members : { setUp: function() { var req = this.req = new qx.bom.request.Script(); this.url = this.getUrl("qx/test/script.js"); // Assume timeout after 1s in Opera (no error!) if (qx.core.Environment.get("engine.name") === "opera") { req.timeout = 1000; } }, tearDown: function() { this.getSandbox().restore(); this.req.dispose(); }, // // General // "test: create instance": function() { this.assertObject(this.req); }, "test: dispose() removes script from DOM": function() { var script; this.req.open(); this.req.send(); script = this.req._getScriptElement(); this.req.dispose(); this.assertFalse(this.isInDom(script)); }, "test: isDisposed()": function() { this.assertFalse(this.req.isDisposed()); this.req.dispose(); this.assertTrue(this.req.isDisposed()); }, "test: allow many requests with same object": function() { var count = 0, that = this; this.req.onload = function() { count += 1; if (count == 2) { that.resume(function() {}); return; } that.request(); }; this.request(); this.wait(); }, // // Event helper // "test: call event handler": function() { var req = this.req; req.onevent = this.spy(); req._emit("event"); this.assertCalled(req.onevent); }, "test: fire event": function(){ var req = this.req; var event = this.spy(); req.onevent = this.spy(); req.on("event", event); req._emit("event"); this.assertCalled(event); }, // // Properties // "test: properties indicate success when request completed": function() { var that = this, req = this.req; req.onload = function() { that.resume(function() { that.assertEquals(4, req.readyState); that.assertEquals(200, req.status); that.assertEquals("200", req.statusText); }); }; this.request(); this.wait(); }, /** * @ignore(SCRIPT_LOADED) */ "test: status indicates success when determineSuccess returns true": function() { var that = this; this.req.onload = function() { that.resume(function() { that.assertEquals(200, that.req.status); }); }; this.req.setDetermineSuccess(function() { return SCRIPT_LOADED === true; }); this.request(this.getUrl("qx/test/script.js")); this.wait(); }, // Error handling "test: properties indicate failure when request failed": function() { // Known to fail in legacy IEs if (this.isIeBelow(9)) { this.skip(); } var that = this, req = this.req; req.onerror = function() { that.resume(function() { that.assertEquals(4, req.readyState); that.assertEquals(0, req.status); that.assertNull(req.statusText); }); }; this.request("http://fail.tld"); this.wait(15000); }, "test: properties indicate failure when request timed out": function() { // Known to fail in legacy IEs if (this.isIeBelow(9)) { this.skip(); } var that = this, req = this.req; req.timeout = 25; req.ontimeout = function() { that.resume(function() { that.assertEquals(4, req.readyState); that.assertEquals(0, req.status); that.assertNull(req.statusText); }); }; this.requestPending(); this.wait(); }, "test: status indicates failure when determineSuccess returns false": function() { var that = this; this.req.onload = function() { that.resume(function() { that.assertEquals(500, that.req.status); }); }; this.req.setDetermineSuccess(function() { return false; }); this.request(); this.wait(); }, "test: reset XHR properties when reopened": function() { var req = this.req, that = this; req.onload = function() { that.resume(function() { req.open("GET", "/url"); that.assertIdentical(1, req.readyState); that.assertIdentical(0, req.status); that.assertIdentical("", req.statusText); }); }; this.request(); this.wait(); }, // // open() // "test: open() stores URL": function() { this.req.open("GET", this.url); this.assertEquals(this.url, this.req._getUrl()); }, // // send() // "test: send() adds script element to DOM": function() { var req = this.req; // Helper triggers send() this.request(); this.assert(this.isInDom(req._getScriptElement()), "Script element not in DOM"); }, "test: send() sets script src to URL": function() { this.request(); this.assertMatch(this.req._getScriptElement().src, /qx\/test\/script.js$/); }, "test: send() with data": function() { this.skip(); }, // // abort() // "test: abort() removes script element": function() { var req = this.req; this.requestPending(); req.abort(); this.assertFalse(this.isInDom(req._getScriptElement()), "Script element in DOM"); }, "test: abort() makes request not fire load": function() { var req = this.req; this.spy(req, "onload"); if (this.isIe()) { this.request(this.noCache(this.url)); } else { this.request(); } req.abort(); this.wait(300, function() { this.assertNotCalled(req.onload); }, this); }, // // setRequestHeader() // "test: setRequestHeader() throws error when other than OPENED": function() { var req = this.req; this.assertException(function() { req.setRequestHeader(); }, null, "Invalid state"); }, "test: setRequestHeader() appends to URL": function() { var req = this.req; req.open("GET", "/affe"); req.setRequestHeader("key1", "value1"); req.setRequestHeader("key2", "value2"); this.assertMatch(req._getUrl(), /key1=value1/); this.assertMatch(req._getUrl(), /key2=value2/); }, // // Event handlers // "test: call onload": function() { // More precisely, the request completes when the browser // has loaded and parsed the script var that = this; this.req.onload = function() { that.resume(function() {}); }; this.request(); this.wait(); }, "test: call onreadystatechange and have appropriate readyState": function() { var req = this.req, readyStates = [], that = this; req.onreadystatechange = function() { readyStates.push(req.readyState); if (req.readyState === 4) { that.resume(function() { that.assertArrayEquals([1, 2, 3, 4], readyStates); }); } }; if (this.isIe()) { this.request(this.noCache(this.url)); } else { this.request(); } this.wait(); }, // Error handling "test: call onloadend on network error": function() { var that = this; this.req.onloadend = function() { that.resume(function() {}); }; this.request("http://fail.tld"); this.wait(15000); }, "test: call onloadend when request completes": function() { var that = this; this.req.onloadend = function() { that.resume(function() {}); }; this.request(); this.wait(); }, "test: not call onload when loading failed because of network error": function() { // Known to fail in IE < 9, // i.e. all browsers using onreadystatechange event handlerattribute // // After a short delay, readyState progresses to "loaded" even // though the resource could not be loaded. if (this.isIeBelow(9)) { this.skip(); } var that = this; this.req.onload = function() { that.resume(function() { throw Error("Called onload"); }); }; this.req.onerror = function() { that.resume(); }; this.request("http://fail.tld"); this.wait(15000); }, "test: call onerror on network error": function() { // Known to fail in legacy IEs if (this.isIeBelow(9)) { this.skip(); } var that = this; this.req.onerror = function() { that.resume(function() {}); }; this.request("http://fail.tld"); this.wait(15000); }, "test: call onerror on invalid script": function() { // Known to fail in all browsers tested // Native "error" event not fired for script element. // // A possible work-around is to listen to the global "error" // event dispatched on the window. this.skip(); var that = this; this.req.onerror = function() { that.resume(function() {}); }; // Invalid JavaScript this.request(this.getUrl("qx/test/xmlhttp/sample.txt")); this.wait(); }, "test: not call onerror when request exceeds timeout limit": function() { var req = this.req; // Known to fail in browsers not supporting the error event // because timeouts are used to fake the "error" if (!this.supportsErrorHandler()) { this.skip(); } this.spy(req, "onerror"); req.timeout = 25; this.requestPending(); this.wait(20, function() { this.assertNotCalled(req.onerror); }, this); }, "test: call ontimeout when request exceeds timeout limit": function() { // Known to fail in legacy IEs if (this.isIeBelow(9)) { this.skip(); } var that = this; this.req.timeout = 25; this.req.ontimeout = function() { that.resume(function() {}); }; this.requestPending(); this.wait(); }, "test: not call ontimeout when request is within timeout limit": function() { var req = this.req, that = this; this.spy(req, "ontimeout"); req.onload = function() { that.resume(function() { // Assert that onload() cancels timeout that.wait(350, function() { that.assertNotCalled(req.ontimeout); }); }); }; req.timeout = 300; this.request(); this.wait(); }, "test: call onabort when request was aborted": function() { var req = this.req; this.spy(req, "onabort"); this.request(); req.abort(); this.assertCalled(req.onabort); }, // // Clean-Up // "test: remove script from DOM when request completed": function() { var script, that = this; this.req.onload = function() { that.resume(function() { script = this.req._getScriptElement(); that.assertFalse(that.isInDom(script)); }); }; this.request(); this.wait(); }, "test: remove script from DOM when request failed": function() { var script, that = this; // In IE < 9, "load" is fired instead of "error" this.req.onerror = this.req.onload = function() { that.resume(function() { script = this.req._getScriptElement(); that.assertFalse(that.isInDom(script)); }); }; this.request("http://fail.tld"); this.wait(15000); }, "test: remove script from DOM when request timed out": function() { // Known to fail in legacy IEs if (this.isIeBelow(9)) { this.skip(); } var script, that = this; this.req.timeout = 25; this.req.ontimeout = function() { that.resume(function() { script = that.req._getScriptElement(); that.assertFalse(that.isInDom(script)); }); }; this.requestPending(); this.wait(); }, request: function(customUrl) { this.req.open("GET", customUrl || this.url, true); this.req.send(); }, requestPending: function(sleep) { this.require(["php"]); var url = this.noCache(this.getUrl("qx/test/jsonp_primitive.php")); // In legacy browser, a long running script request blocks subsequent requests // even if the script element is removed. Keep duration very low to work around. // // Sleep 50ms url += "&sleep=" + (sleep || 50); this.request(url); }, isInDom: function(elem) { return elem.parentNode ? true : false; }, isIe: function(version) { return (qx.core.Environment.get("engine.name") === "mshtml"); }, isIeBelow: function(version) { return qx.core.Environment.get("engine.name") === "mshtml" && qx.core.Environment.get("browser.documentmode") < version; }, supportsErrorHandler: function() { var isLegacyIe = qx.core.Environment.get("engine.name") === "mshtml" && qx.core.Environment.get("browser.documentmode") < 9; var isOpera = qx.core.Environment.get("engine.name") === "opera"; return !(isLegacyIe || isOpera); }, noCache: function(url) { return url + "?nocache=" + (new Date()).valueOf(); }, skip: function(msg) { throw new qx.dev.unit.RequirementError(null, msg); } } });