diff --git a/Gruntfile.js b/Gruntfile.js index 956113503..cdb5b34f7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,6 +1,15 @@ "use strict"; +var fs = require('fs'); +var path = require('path'); +var http = require('http'); +var build = (function(){ + // travis testing + if (process.env && process.env.BUILD) return process.env.BUILD == 'default' ? 'all' : 'nocompat'; + // local testing + else return process.argv[2] == 'default' || process.argv[2] == null ? 'all' : 'nocompat'; +})(); -module.exports = function(grunt) { +module.exports = function(grunt){ grunt.loadNpmTasks('grunt-contrib-uglify'); require('load-grunt-tasks')(grunt); @@ -49,11 +58,14 @@ module.exports = function(grunt) { } }); - var compatBuild = ['clean:specs', 'packager:all', 'packager:specs']; - var nocompatBuild = ['clean:specs', 'packager:nocompat', 'packager:specs-nocompat']; + var compatBuild = ['clean:specs', 'packager:all', 'packager:specs', 'specsserver']; + var nocompatBuild = ['clean:specs', 'packager:nocompat', 'packager:specs-nocompat', 'specsserver']; var tasks = options.travis.build == 'default' ? compatBuild : nocompatBuild; tasks = usePhantom ? tasks.concat('karma:continuous') : tasks.concat('karma:sauceTask'); + grunt.registerTask('specsserver', function(){ + require('./Tests/httpServer.js')(build); + }); grunt.registerTask('default', compatBuild.concat('karma:continuous')); // local testing - compat build grunt.registerTask('nocompat', nocompatBuild.concat('karma:continuous')); // local testing - no compat build grunt.registerTask('default:travis', tasks); // Travis & Sauce Labs diff --git a/Source/Utilities/DOMReady.js b/Source/Utilities/DOMReady.js index 4ba7c6397..49c94a9fa 100644 --- a/Source/Utilities/DOMReady.js +++ b/Source/Utilities/DOMReady.js @@ -73,7 +73,11 @@ if (document.readyState) checks.push(function(){ return (state == 'loaded' || state == 'complete'); }); -if ('onreadystatechange' in document) document.addListener('readystatechange', check); +if ('onreadystatechange' in document){ + document.addListener('readystatechange', check); + var state = document.readyState; + if (state == 'loaded' || state == 'complete') domready(); +} else shouldPoll = true; if (shouldPoll) poll(); diff --git a/Specs/Utilities/DOMReady.js b/Specs/Utilities/DOMReady.js index 5a8d15fc5..193509e44 100644 --- a/Specs/Utilities/DOMReady.js +++ b/Specs/Utilities/DOMReady.js @@ -6,30 +6,96 @@ provides: ~ ... */ -/* todo -document.addListener = function(type, fn){ - if (this.addEventListener) this.addEventListener(type, fn, false); - else this.attachEvent('on' + type, fn); - return this; -}; - -document.removeListener = function(type, fn){ - if (this.removeEventListener) this.removeEventListener(type, fn, false); - else this.detachEvent('on' + type, fn); - return this; -}; - - -window.fireEvent = -document.fireEvent = function(type){ - if (type == 'domready') - for (var i = 0; i < domreadyCallbacks.length; ++i){ +describe("DOMReady", function(){ + + var win, frame, cb, ready; + function checkStatus(){ + ready = win && win.callbackFired; + if (ready) cb(); + return ready; } - domreadyCallbacks[i](); -}; -window.addEvent = function(){}; + function newFrame(url){ + var iframe = new IFrame({ + src: 'specsserver/' + url + }); + document.getElement('body').adopt(iframe); + return iframe; + } + + beforeEach(function(){ + cb = jasmine.createSpy('DOMReady!'); + }); + + afterEach(function(){ + frame.destroy(); + win = cb = frame = ready = null; + }); + + it('should fire DOMReady, after flushing, when the DOM is ready', function(){ + frame = newFrame('foo?assets=flush'); + frame.addEvent('load', function(){ + win = frame.contentWindow; + expect(win.moments[0]).toEqual('loading'); + expect(win.moments[1]).toEqual('loading'); + expect(win.moments[2] == 'interactive' || win.moments[2] == 'complete').toBeTruthy(); + }); + + waitsFor(function(){ + return checkStatus(); + }, "the iframe to load", 8000); + runs(function(){ + expect(cb).toHaveBeenCalled(); + }); + }); + + it('should fire DOMReady when the DOM is ready', function(){ + frame = newFrame('DOMReady/DOMReady.head.html'); + frame.addEvent('load', function(){ + win = frame.contentWindow; + }); + waitsFor(function(){ + return checkStatus(); + }, "the iframe to load", 1500); + runs(function(){ + expect(cb).toHaveBeenCalled(); + }); + }); + + it('should fire DOMReady when a new `addEvent("domready"` is added', function(){ + frame = newFrame('DOMReady/DOMReady.onAdd.html'); + frame.addEvent('load', function(){ + win = frame.contentWindow; + win.addEvent('domready', win.callback); + }); + waitsFor(function(){ + return checkStatus(); + }, "the iframe to load", 1500); + runs(function(){ + expect(cb).toHaveBeenCalled(); + }); + }); + + it('should fire when MooTools was loaded into a already-ready page', function(){ + frame = newFrame('DOMReady/DOMReady.delayed.html'); + var ready; + frame.addEvent('load', function(){ + win = frame.contentWindow; + expect(win.MooTools).toBeFalsy(); // because MooTools should not be loaded yet + var i = setInterval(function(){ + if (win.addEvent && win.callback){ + win.addEvent('domready', win.callback); + if (ready) clearInterval(i); + } + }, 50); + }); + waitsFor(function(){ + return checkStatus(); + }, "the iframe to load and MooTools to be deployed", 6000); + runs(function(){ + expect(cb).toHaveBeenCalled(); + }); + }); + +}); -var Element = this.Element || {}; -Element.Events = {}; -*/ diff --git a/Tests/DOMReady/DOMReady.delayed.html b/Tests/DOMReady/DOMReady.delayed.html new file mode 100644 index 000000000..09e644609 --- /dev/null +++ b/Tests/DOMReady/DOMReady.delayed.html @@ -0,0 +1,27 @@ + + + + + Add MooTools when page is already ready + + + + + + diff --git a/Tests/DOMReady/DOMReady.head.html b/Tests/DOMReady/DOMReady.head.html new file mode 100644 index 000000000..f3b6b2f3b --- /dev/null +++ b/Tests/DOMReady/DOMReady.head.html @@ -0,0 +1,21 @@ + + + + + Normal DOMReady scenario + + + + + + + + + diff --git a/Tests/DOMReady/DOMReady.onAdd.html b/Tests/DOMReady/DOMReady.onAdd.html new file mode 100644 index 000000000..d751f6dc3 --- /dev/null +++ b/Tests/DOMReady/DOMReady.onAdd.html @@ -0,0 +1,19 @@ + + + + + DOMReady added when page is already loaded + + + + + + + + diff --git a/Tests/assets.js b/Tests/assets.js new file mode 100644 index 000000000..3640174c9 --- /dev/null +++ b/Tests/assets.js @@ -0,0 +1,45 @@ +"use strict"; + +// custom behaviours to test +var assets = { + + flush: function(req, res, src){ + + var headString = '' + + '' + + '' + + '' + + 'Flushed page scenario' + + '' + + '' + + ''; + var bodyString = '' + + '' + + '
body added...
' + + '' + + ''; + var endString = ''; + + res.writeHead(200, {'Content-Type': 'text/html'}); + res.write(headString); + setTimeout(function() { + res.write(bodyString); + setTimeout(function() { + res.end(endString); + }, 2000); + }, 2000); + } +} + +module.exports = assets; diff --git a/Tests/gruntfile-options.js b/Tests/gruntfile-options.js index 094a18383..74f0c6cc7 100644 --- a/Tests/gruntfile-options.js +++ b/Tests/gruntfile-options.js @@ -66,7 +66,15 @@ var karmaOptions = { captureTimeout: 60000 * 2, singleRun: true, frameworks: ['jasmine', 'sinon'], - files: ['Tests/Utilities/*.js', 'mootools-*.js'], + files: [ + 'Tests/Utilities/*.js', + 'mootools-*.js', + {pattern: 'Source/**/*.*', included: false, served: true}, + {pattern: 'Tests/**/*.*', included: false, served: true} + ], + proxies: { + '/specsserver/': 'http://localhost:9000/' + }, sauceLabs: { username: process.env.SAUCE_USERNAME, accessKey: process.env.SAUCE_ACCESS_KEY, diff --git a/Tests/httpServer.js b/Tests/httpServer.js new file mode 100644 index 000000000..aa73418fd --- /dev/null +++ b/Tests/httpServer.js @@ -0,0 +1,27 @@ +"use strict"; + +var fs = require('fs'); +var path = require('path'); +var http = require('http'); +var assets = require('./assets.js'); + +function getQuery(path){ + var match = path.match(/=(\w+)/); + return match ? match[1] : null; +} + +module.exports = function(build){ + http.createServer(function(req, res){ + + var src = '/base/mootools-' + build; + var customFunction = getQuery(req.url); + + if (customFunction) return assets[customFunction].call(null, req, res, src); + var filePath = path.join(__dirname, req.url); + fs.readFile(filePath, 'utf-8', function (err, content){ + if (err) return console.log(err); + content = content.replace('mootoolsPath', src); + res.end(content); + }); + }).listen(9000); +} diff --git a/package.json b/package.json index 4e52722a8..2d873198e 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "karma-firefox-launcher": "~0.1.3", "karma-ie-launcher": "~0.1", "karma-safari-launcher": "~0.1", + "path": "^0.11.14", "js-yaml": "^3.0.2" } }