diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/fastdom-promised-test.js | 48 | ||||
-rw-r--r-- | test/fastdom-sandbox-test.js | 87 | ||||
-rw-r--r-- | test/fastdom-strict-test.js | 78 | ||||
-rw-r--r-- | test/fastdom-test.js | 389 | ||||
-rw-r--r-- | test/index.html | 29 | ||||
-rw-r--r-- | test/karma.conf.js | 56 | ||||
-rw-r--r-- | test/setup.js | 18 | ||||
-rw-r--r-- | test/test.clear.js | 110 | ||||
-rw-r--r-- | test/test.defer.js | 176 | ||||
-rw-r--r-- | test/test.set.js | 319 |
10 files changed, 658 insertions, 652 deletions
diff --git a/test/fastdom-promised-test.js b/test/fastdom-promised-test.js new file mode 100644 index 0000000..959800e --- /dev/null +++ b/test/fastdom-promised-test.js @@ -0,0 +1,48 @@ +/*global suite, setup, test, assert, sinon, fastdomPromised*/ +/*jshint maxlen:false*/ + +suite('fastdom-promised', function() { + var fastdom; + + setup(function() { + fastdom = window.fastdom.extend(fastdomPromised); + }); + + test('it returns a Promise that resolves after the task is run', function(done) { + var spy = sinon.spy(); + + fastdom.measure(spy) + .then(function() { + sinon.assert.calledOnce(spy); + done(); + }); + }); + + test('promises can be returned from tasks', function() { + var spy1 = sinon.spy(); + var spy2 = sinon.spy(); + + return fastdom.measure(function() { + spy1(); + return fastdom.mutate(spy2); + }) + + .then(function() { + sinon.assert.calledOnce(spy1); + sinon.assert.calledOnce(spy2); + assert.isTrue(spy1.calledBefore(spy2)); + }); + }); + + test('calling `fastdom.clear(promise)` works', function(done) { + var spy = sinon.spy(); + var task = fastdom.measure(spy); + + fastdom.clear(task); + + requestAnimationFrame(function() { + sinon.assert.notCalled(spy); + done(); + }); + }); +}); diff --git a/test/fastdom-sandbox-test.js b/test/fastdom-sandbox-test.js new file mode 100644 index 0000000..43b6626 --- /dev/null +++ b/test/fastdom-sandbox-test.js @@ -0,0 +1,87 @@ +/*global suite, setup, test, assert, sinon, fastdomSandbox, fastdomPromised*/ +/* jshint maxlen:false */ + +suite('fastdom-sandbox', function() { + var raf = window.requestAnimationFrame; + var fastdom; + + setup(function() { + fastdom = new window.fastdom.constructor(); + fastdom = fastdom.extend(window.fastdomSandbox); + }); + + test('It works as normal', function(done) { + var sandbox = fastdom.sandbox(); + + sandbox.measure(function() { + sandbox.mutate(function() { + done(); + }); + }); + }); + + test('Its possible to clear all sandbox jobs', function(done) { + var sandbox = fastdom.sandbox(); + var spy = sinon.spy(); + + sandbox.measure(spy); + sandbox.mutate(spy); + + fastdom.measure(function() { + fastdom.mutate(function() { + assert.isTrue(spy.notCalled); + done(); + }); + }); + + sandbox.clear(); + }); + + test('It clears individual tasks', function(done) { + var sandbox = fastdom.sandbox(); + var spy = sinon.spy(); + + var task = sandbox.measure(spy); + sandbox.clear(task); + sandbox.measure(function() { + assert.isTrue(spy.notCalled); + done(); + }); + }); + + test('it works with fastdom-promised', function(done) { + var myFastdom = fastdom + .extend(fastdomPromised) + .extend(fastdomSandbox); + + var sandbox = myFastdom.sandbox(); + var spy1 = sinon.spy(); + var spy2 = sinon.spy(); + + sandbox.measure(spy1) + .then(function() { + return sandbox.mutate(spy2); + }) + + .then(function() { + sinon.assert.calledOnce(spy1); + sinon.assert.calledOnce(spy2); + + spy1.reset(); + spy2.reset(); + + sandbox.measure(spy1); + sandbox.measure(spy2); + sandbox.clear(); + + raf(function() { + console.log(3); + sinon.assert.notCalled(spy1); + sinon.assert.notCalled(spy2); + done(); + }); + }) + + .catch(done); + }); +}); diff --git a/test/fastdom-strict-test.js b/test/fastdom-strict-test.js new file mode 100644 index 0000000..688e8b2 --- /dev/null +++ b/test/fastdom-strict-test.js @@ -0,0 +1,78 @@ +/*global suite, setup, suiteSetup, suiteTeardown, teardown, test, assert, fastdomPromised*/ +/*jshint maxlen:false*/ + +suite('fastdom-strict', function() { + var fastdom; + var el; + + suiteSetup(function(done) { + var script = document.createElement('script'); + script.src = '/base/fastdom-strict.js'; + document.head.appendChild(script); + script.onload = function() { + fastdom = window.fastdom.extend(fastdomPromised); + done(); + }; + }); + + suiteTeardown(function() { + fastdom.strict(false); + }); + + setup(function() { + return fastdom.mutate(function() { + el = document.createElement('div'); + el.style.height = '100px'; + el.style.width = '100px'; + document.body.appendChild(el); + }); + }); + + teardown(function() { + return fastdom.mutate(function() { + el.remove(); + }); + }); + + test('measuring throws outside of fastdom', function() { + assert.throws(function() { + return el.clientWidth; + }); + }); + + test('measuring does not throws inside `fastdom.measure()`', function() { + return fastdom.measure(function() { + return el.clientWidth; + }); + }); + + test('mutating throws outside of fastdom', function() { + assert.throws(function() { + el.innerHTML = 'foo'; + }); + }); + + test('mutating does not throws inside `fastdom.mutate()`', function() { + return fastdom.mutate(function() { + el.innerHTML = 'foo'; + }); + }); + + test('it can be disabled and enabled', function(done) { + fastdom.strict(false); + + assert.doesNotThrow(function() { + return el.clientWidth; + }); + + fastdom.strict(true); + + assert.throws(function() { + return el.clientWidth; + }); + + fastdom.measure(function() { + el.clientWidth; + }).then(done); + }); +}); diff --git a/test/fastdom-test.js b/test/fastdom-test.js new file mode 100644 index 0000000..f28dff6 --- /dev/null +++ b/test/fastdom-test.js @@ -0,0 +1,389 @@ +/*jshint maxlen:false*/ +/*global suite, setup, teardown, test, assert, sinon, fastdomSandbox, fastdomPromised*/ + +suite('fastdom', function() { + var raf = window.requestAnimationFrame; + var fastdom; + + setup(function() { + fastdom = new window.fastdom.constructor(); + }); + + test('it runs reads before writes', function(done) { + var read = sinon.spy(function() { + assert(!write.called); + }); + + var write = sinon.spy(function() { + assert(read.called); + done(); + }); + + fastdom.measure(read); + fastdom.mutate(write); + }); + + test('it calls all reads together, followed by all writes', function(done) { + var read1 = sinon.spy(); + var read2 = sinon.spy(); + var write1 = sinon.spy(); + var write2 = sinon.spy(); + + // Assign unsorted + fastdom.measure(read1); + fastdom.mutate(write1); + fastdom.measure(read2); + fastdom.mutate(write2); + + // After the queue has been emptied + // check the callbacks were called + // in the correct order. + raf(function() { + assert(read1.calledBefore(read2)); + assert(read2.calledBefore(write1)); + assert(write1.calledBefore(write2)); + done(); + }); + }); + + test('it calls a read in the same frame if scheduled inside a read callback', function(done) { + var cb = sinon.spy(); + + fastdom.measure(function() { + + // Schedule a callback for *next* frame + raf(cb); + + // Schedule a read callback + // that should be run in the + // current frame checking that + // the RAF callback has not + // yet been fired. + fastdom.measure(function() { + assert(!cb.called); + done(); + }, this); + }, this); + }); + + test('it calls a write in the same frame if scheduled inside a read callback', function(done) { + var cb = sinon.spy(); + + fastdom.measure(function() { + + // Schedule a callback for *next* frame + raf(cb); + + // Schedule a read callback + // that should be run in the + // current frame checking that + // the RAF callback has not + // yet been fired. + fastdom.mutate(function() { + assert(!cb.called); + done(); + }, this); + }, this); + }); + + test('it calls a read in the *next* frame if scheduled inside a write callback', function(done) { + var cb = sinon.spy(); + + fastdom.mutate(function() { + + // Schedule a callback for *next* frame + raf(cb); + + // Schedule a read that should be + // called in the next frame, meaning + // the test callback should have already + // been called. + fastdom.measure(function() { + assert(cb.called); + done(); + }, this); + }, this); + }); + + test('it does not request a new frame when a write is requested inside a nested read', function(done) { + var callback = sinon.spy(); + + fastdom.mutate(function() { + fastdom.measure(function() { + + // Schedule a callback for *next* frame + raf(callback); + + // Schedule a read callback + // that should be run in the + // current frame checking that + // the RAF callback has not + // yet been fired. + fastdom.mutate(function() { + assert(!callback.called); + done(); + }); + }); + }); + }); + + test('it schedules a new frame when a read is requested in a nested write', function(done) { + fastdom.raf = sinon.spy(fastdom, 'raf'); + + fastdom.measure(function() { + fastdom.mutate(function() { + fastdom.measure(function(){ + + // Should have scheduled a new frame + assert(fastdom.raf.calledTwice); + done(); + }); + }); + }); + }); + + test('it runs nested reads in the same frame', function(done) { + sinon.spy(fastdom, 'raf'); + + fastdom.measure(function() { + fastdom.measure(function() { + fastdom.measure(function() { + fastdom.measure(function() { + + // Should not have scheduled a new frame + sinon.assert.calledOnce(fastdom.raf); + done(); + }); + }); + }); + }); + }); + + test('it runs nested writes in the same frame', function(done) { + fastdom.raf = sinon.spy(fastdom, 'raf'); + + fastdom.mutate(function() { + fastdom.mutate(function() { + fastdom.mutate(function() { + fastdom.mutate(function() { + + // Should not have scheduled a new frame + sinon.assert.calledOnce(fastdom.raf); + done(); + }); + }); + }); + }); + }); + + test('it calls a "read" callback with the given context', function(done) { + fastdom.measure(function() { + assert.equal(this.foo, 'bar'); + done(); + }, { foo: 'bar' }); + }); + + test('it calls a "write" callback with the given context', function(done) { + fastdom.mutate(function() { + assert.equal(this.foo, 'bar'); + done(); + }, { foo: 'bar' }); + }); + + test('it has an empty job hash when batch complete', function(done) { + var ran = 0; + + fastdom.measure(function(){ ran += 1; }); + fastdom.measure(function(){ ran += 2; }); + fastdom.mutate(function(){ ran += 4; }); + fastdom.mutate(function(){ ran += 8; }); + + // Check there are four jobs stored + assert.equal(ran, 0); + + raf(function() { + assert.equal(ran, 15); + done(); + }); + }); + + test('it maintains correct context if single method is registered twice', function(done) { + var ctx1 = { foo: 'bar' }; + var ctx2 = { bar: 'baz' }; + + function shared() {} + + var spy1 = sinon.spy(shared); + var spy2 = sinon.spy(shared); + + fastdom.measure(spy1, ctx1); + fastdom.measure(spy2, ctx2); + + raf(function() { + assert(spy1.calledOn(ctx1)); + assert(spy2.calledOn(ctx2)); + done(); + }); + }); + + test('it runs .catch() handler on error if one has been registered', function(done) { + fastdom.catch = sinon.spy(); + + fastdom.measure(function() { throw 'err1'; }); + fastdom.mutate(function() { throw 'err2'; }); + + raf(function() { + raf(function() { + assert(fastdom.catch.calledTwice, 'twice'); + assert(fastdom.catch.getCall(0).calledWith('err1'), 'bla'); + assert(fastdom.catch.getCall(1).calledWith('err2'), 'bl2'); + done(); + }); + }); + }); + + suite('exceptions', function() { + + // temporarily disable mocha error detection + setup(function() { + this.onerror = window.onerror; + window.onerror = null; + }); + + // re-enable mocha error detection + teardown(function() { + window.onerror = this.onerror; + }); + + test('it flushes remaining tasks in next frame if prior task throws', function(done) { + var spy = sinon.spy(); + + fastdom.measure(function() { throw new Error('error'); }); + fastdom.measure(spy); + + raf(function() { + sinon.assert.notCalled(spy); + raf(function() { + sinon.assert.calledOnce(spy); + done(); + }); + }); + }); + }); + + test('it stops rAF loop once frame queue is empty', function(done) { + var callback = sinon.spy(); + + sinon.spy(fastdom, 'raf'); + fastdom.measure(callback); + + raf(function() { + assert(callback.called); + assert(fastdom.raf.calledOnce); + done(); + }); + }); + + suite('clear', function() { + test('it does not run "read" job if cleared (sync)', function(done) { + var read = sinon.spy(); + var id = fastdom.measure(read); + fastdom.clear(id); + + raf(function() { + raf(function() { + assert(!read.called); + done(); + }); + }); + }); + + test('it fails silently if job not found in queue', function(done) { + var read = sinon.spy(); + var read2 = sinon.spy(); + + var id = fastdom.measure(read); + fastdom.clear(id); + + raf(function() { + assert(!read2.called); + done(); + }); + }); + + test('it does not run "write" job if cleared (async)', function(done) { + var read = sinon.spy(); + var write = sinon.spy(); + + var id = fastdom.mutate(write); + fastdom.measure(function() { + fastdom.clear(id); + + raf(function() { + assert(!read.called); + done(); + }); + }); + }); + + test('it does not run "write" job if cleared', function(done) { + var write = sinon.spy(); + var id = fastdom.mutate(write); + + fastdom.clear(id); + + raf(function() { + assert(!write.called); + done(); + }); + }); + + test('it removes reference to the job if cleared', function(done) { + var write = sinon.spy(); + var id = fastdom.mutate(2, write); + + fastdom.clear(id); + + raf(function() { + raf(function() { + raf(function() { + assert(!write.called); + done(); + }); + }); + }); + }); + }); + + suite('FastDom#extend()', function() { + test('it has the properties of given object', function() { + var fastdom2 = fastdom.extend({ prop: 'foo' }); + assert.equal(fastdom2.prop, 'foo'); + }); + + test('it can extend an extension', function() { + var fastdom2 = fastdom.extend({ prop: 'foo' }); + var fastdom3 = fastdom2.extend({ prop: 'bar' }); + + assert.equal(fastdom2.prop, 'foo'); + assert.equal(fastdom3.prop, 'bar'); + + assert.equal(fastdom2.fastdom, fastdom); + assert.equal(fastdom3.fastdom, fastdom2); + }); + + test('it throws if argument is not object', function() { + assert.throws(function() { + fastdom.extend(); + }); + + assert.throws(function() { + fastdom.extend('oopsie'); + }); + + assert.throws(function() { + fastdom.extend(999); + }); + }); + }); +}); diff --git a/test/index.html b/test/index.html deleted file mode 100644 index f757410..0000000 --- a/test/index.html +++ /dev/null @@ -1,29 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <meta charset="utf-8"> - <title>Mocha Tests</title> - <link rel="stylesheet" href="../node_modules/mocha/mocha.css" /> -</head> -<body> - <div id="mocha"></div> - <script src="../node_modules/mocha/mocha.js"></script> - <script src="../node_modules/chai/chai.js"></script> - <script src="../node_modules/sinon/lib/sinon.js"></script> - <script src="../node_modules/sinon/lib/sinon/match.js"></script> - <script src="../node_modules/sinon/lib/sinon/spy.js"></script> - <script src="../node_modules/sinon/lib/sinon/stub.js"></script> - <script>mocha.setup('tdd')</script> - <script src="../index.js"></script> - <script src="setup.js"></script> - <script src="test.set.js"></script> - <script src="test.clear.js"></script> - <script src="test.defer.js"></script> - <script> - mocha.checkLeaks(); - - if (window.mochaPhantomJS) mochaPhantomJS.run(); - else mocha.run(); - </script> -</body> -</html>
\ No newline at end of file diff --git a/test/karma.conf.js b/test/karma.conf.js new file mode 100644 index 0000000..d639b36 --- /dev/null +++ b/test/karma.conf.js @@ -0,0 +1,56 @@ +'use strict'; + +module.exports = function(config) { + config.set({ + basePath: '..', + + browsers: [ + 'chrome', + 'Firefox' + ], + + frameworks: [ + 'mocha', + 'chai-sinon' + ], + + reporters: [ + 'mocha', + 'coverage' + ], + + coverageReporter: { + type : 'lcov', + dir : 'test/', + subdir: 'coverage' + }, + + preprocessors: { + 'fastdom.js': ['coverage'], + 'extensions/*.js': ['coverage'] + }, + + client: { + captureConsole: true, + mocha: { ui: 'tdd' } + }, + + customLaunchers: { + chrome: { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }, + + files: [ + 'fastdom.js', + 'extensions/fastdom-promised.js', + 'extensions/fastdom-sandbox.js', + 'test/fastdom-sandbox-test.js', + 'test/fastdom-promised-test.js', + 'test/fastdom-strict-test.js', + 'test/fastdom-test.js', + { pattern: 'fastdom-strict.js', included: false } + ] + }); +}; diff --git a/test/setup.js b/test/setup.js deleted file mode 100644 index 0844695..0000000 --- a/test/setup.js +++ /dev/null @@ -1,18 +0,0 @@ - -// RequestAnimationFrame Polyfill -var raf = window.requestAnimationFrame - || window.webkitRequestAnimationFrame - || window.mozRequestAnimationFrame - || function(cb) { window.setTimeout(cb, 1000 / 60); }; - -// Make constructor -var FastDom = fastdom.constructor; - -// Alias chai.assert -var assert = chai.assert; - -function objectLength(object) { - var l = 0; - for (var key in object) l++; - return l; -}
\ No newline at end of file diff --git a/test/test.clear.js b/test/test.clear.js deleted file mode 100644 index 1230b16..0000000 --- a/test/test.clear.js +++ /dev/null @@ -1,110 +0,0 @@ - -suite('clear', function(){ - - test('Should not run "read" job if cleared (sync)', function(done) { - var fastdom = new FastDom(); - var read = sinon.spy(); - - var id = fastdom.read(read); - fastdom.clear(id); - - raf(function() { - assert(!read.called); - done(); - }); - }); - - test('Should fail silently if job not found in queue', function(done) { - var fastdom = new FastDom(); - var read = sinon.spy(); - var read2 = sinon.spy(); - - var id = fastdom.read(read); - fastdom.clear(id); - - raf(function() { - assert(!read2.called); - done(); - }); - }); - - test('Should not run "write" job if cleared (async)', function(done) { - var fastdom = new FastDom(); - var read = sinon.spy(); - var write = sinon.spy(); - - var id = fastdom.write(write); - fastdom.read(function() { - fastdom.clear(id); - - raf(function() { - assert(!read.called); - done(); - }); - }); - }); - - test('Should not run "write" job if cleared', function(done) { - var fastdom = new FastDom(); - var write = sinon.spy(); - var id = fastdom.write(write); - - fastdom.clear(id); - - raf(function() { - assert(!write.called); - done(); - }); - }); - - test('Should not run "defer" job if cleared', function(done) { - var fastdom = new FastDom(); - var callback = sinon.spy(); - var id = fastdom.defer(3, callback); - - fastdom.clear(id); - - raf(function() { - raf(function() { - raf(function() { - raf(function() { - assert(!callback.called); - done(); - }); - }); - }); - }); - }); - - test('Should remove reference to the job if cleared', function(done) { - var fastdom = new FastDom(); - var write = sinon.spy(); - var id = fastdom.write(2, write); - - fastdom.clear(id); - - raf(function() { - raf(function() { - raf(function() { - assert(!write.called); - assert(!fastdom.batch.hash[id]); - done(); - }); - }); - }); - }); - - test('Should accept String ids', function(done) { - var fastdom = new FastDom(); - var read = sinon.spy(); - - var id = fastdom.read(read); - - fastdom.clear(id.toString()); - - raf(function() { - assert(!read.called); - done(); - }); - }); -});
\ No newline at end of file diff --git a/test/test.defer.js b/test/test.defer.js deleted file mode 100644 index 89b5729..0000000 --- a/test/test.defer.js +++ /dev/null @@ -1,176 +0,0 @@ - -suite('defer', function(){ - - test('Should run the job after the specified number of frames', function(done) { - var fastdom = new FastDom(); - var job = sinon.spy(); - - fastdom.defer(3, job); - - raf(function() { - assert(!job.called); - raf(function() { - assert(!job.called); - raf(function() { - assert(job.called); - done(); - }); - }); - }); - }); - - test('Should call a deferred callback with the given context', function(done) { - var fastdom = new FastDom(); - var cb = sinon.spy(); - var ctx = { foo: 'bar' }; - - fastdom.defer(2, function() { - assert.equal(this.foo, 'bar'); - done(); - }, ctx); - }); - - test('Should run work at next frame if frames argument not supplied.', function(done) { - var fastdom = new FastDom(); - var callback1 = sinon.spy(); - var callback2 = sinon.spy(); - - fastdom.defer(callback1); - - raf(function() { - assert(callback1.called); - done(); - }); - }); - - test('Should run each job on a different frame.', function(done) { - var fastdom = new FastDom(); - var callback1 = sinon.spy(); - var callback2 = sinon.spy(); - var callback3 = sinon.spy(); - - fastdom.defer(callback1); - fastdom.defer(callback2); - fastdom.defer(callback3); - - raf(function() { - assert(callback1.called); - assert(!callback2.called); - assert(!callback3.called); - raf(function() { - assert(callback2.called); - assert(!callback3.called); - raf(function() { - assert(callback3.called); - done(); - }); - }); - }); - }); - - test('Should run fill empty frames before later work is run.', function(done) { - var fastdom = new FastDom(); - var callback1 = sinon.spy(); - var callback2 = sinon.spy(); - var callback3 = sinon.spy(); - var callback4 = sinon.spy(); - - // Frame 3 - fastdom.defer(3, callback3); - - // Frame 1 - fastdom.defer(callback1); - - // Frame 2 - fastdom.defer(callback2); - - // Frame 4 - fastdom.defer(callback4); - - raf(function() { - assert(callback1.called); - assert(!callback2.called); - assert(!callback3.called); - assert(!callback4.called); - raf(function() { - assert(callback2.called); - assert(!callback3.called); - assert(!callback4.called); - raf(function() { - assert(callback3.called); - assert(!callback4.called); - raf(function() { - assert(callback4.called); - done(); - }); - }); - }); - }); - }); - - test('Should run the next frame even if frame before it errors', function(done) { - var fastdom = new FastDom(); - var rafOld = fastdom.raf; - var error = sinon.stub().throws(); - var callback = sinon.spy(); - - // Wrap requestAnimationFrame method - // so that we can catch any errors - // that may be thrown in the callback - sinon.stub(fastdom, 'raf', function(fn) { - var wrapped = function() { - try { fn(); } catch (e) {} - }; - - rafOld(wrapped); - }); - - fastdom.defer(error); - fastdom.defer(callback); - - raf(function() { - raf(function() { - assert(callback.called, 'The second job was run'); - done(); - }); - }); - }); - - test('Should continue to run future jobs when the last frame errors', function(done) { - var fastdom = new FastDom(); - var rafOld = fastdom.raf; - var error = sinon.stub().throws(); - var callback1 = sinon.spy(); - var callback2 = sinon.spy(); - - // Wrap requestAnimationFrame method - // so that we can catch any errors - // that may be thrown in the callback - sinon.stub(fastdom, 'raf', function(fn) { - var wrapped = function() { - try { fn(); } catch (e) {} - }; - - rafOld(wrapped); - }); - - fastdom.defer(callback1); - fastdom.defer(error); - - setTimeout(function() { - fastdom.defer(callback2); - }, 40); - - raf(function() { - assert(callback1.called, 'the first job was run'); - raf(function() { - setTimeout(function(){ - raf(function() { - assert(callback2.called, 'the third job was run'); - done(); - }); - }, 40); - }); - }); - }); -}); diff --git a/test/test.set.js b/test/test.set.js deleted file mode 100644 index 811c489..0000000 --- a/test/test.set.js +++ /dev/null @@ -1,319 +0,0 @@ - -suite('set', function() { - - test('Should run reads before writes', function(done) { - var fastdom = new FastDom(); - - var read = sinon.spy(function() { - assert(!write.called); - }); - - var write = sinon.spy(function() { - assert(read.called); - done(); - }); - - fastdom.read(read); - fastdom.write(write); - }); - - test('Should call all reads together, followed by all writes', function(done) { - var fastdom = new FastDom(); - var read1 = sinon.spy(); - var read2 = sinon.spy(); - var write1 = sinon.spy(); - var write2 = sinon.spy(); - - // Assign unsorted - fastdom.read(read1); - fastdom.write(write1); - fastdom.read(read2); - fastdom.write(write2); - - // After the queue has been emptied - // check the callbacks were called - // in the correct order. - raf(function() { - assert(read1.calledBefore(read2)); - assert(read2.calledBefore(write1)); - assert(write1.calledBefore(write2)); - done(); - }); - }); - - test('Should call a read in the same frame if scheduled inside a read callback', function(done) { - var fastdom = new FastDom(); - var cb = sinon.spy(); - - fastdom.read(function() { - - // Schedule a callback for *next* frame - raf(cb); - - // Schedule a read callback - // that should be run in the - // current frame checking that - // the RAF callback has not - // yet been fired. - fastdom.read(function() { - assert(!cb.called); - done(); - }); - - // Should not have scheduled a new frame - assert(fastdom.frames.length === 0); - }); - }); - - test('Should call a write in the same frame if scheduled inside a read callback', function(done) { - var fastdom = new FastDom(); - var cb = sinon.spy(); - - fastdom.read(function() { - - // Schedule a callback for *next* frame - raf(cb); - - // Schedule a read callback - // that should be run in the - // current frame checking that - // the RAF callback has not - // yet been fired. - fastdom.write(function() { - assert(!cb.called); - done(); - }); - - // Should not have scheduled a new frame - assert(fastdom.frames.length === 0); - }); - }); - - test('Should call a read in the *next* frame if scheduled inside a write callback', function(done) { - var fastdom = new FastDom(); - var cb = sinon.spy(); - - fastdom.write(function() { - - // Schedule a callback for *next* frame - raf(cb); - - // Schedule a read that should be - // called in the next frame, meaning - // the test callback should have already - // been called. - fastdom.read(function() { - assert(cb.called); - done(); - }); - - // Should have scheduled a new frame - assert(fastdom.frames.length === 1, 'the is one pending frame'); - }); - }); - - test('Should not request a new frame when a write is requested inside a nested read', function(done) { - var fastdom = new FastDom(); - var callback = sinon.spy(); - - fastdom.write(function() { - fastdom.read(function() { - - // Schedule a callback for *next* frame - raf(callback); - - // Schedule a read callback - // that should be run in the - // current frame checking that - // the RAF callback has not - // yet been fired. - fastdom.write(function() { - assert(!callback.called); - done(); - }); - - // Should not have scheduled a new frame - assert(fastdom.frames.length === 0); - }); - }); - }); - - test('Should schedule a new frame when a read is requested in a nested write', function(done) { - var fastdom = new FastDom(); - - fastdom.read(function() { - fastdom.write(function() { - fastdom.read(function(){}); - - // Should have scheduled a new frame - assert(fastdom.frames.length === 1); - done(); - }); - }); - }); - - test('Should run nested reads in the same frame', function(done) { - var fastdom = new FastDom(); - var callback = sinon.spy(); - - fastdom.read(function() { - fastdom.read(function() { - fastdom.read(function() { - fastdom.read(function() { - - // Should not have scheduled a new frame - assert(fastdom.frames.length === 0); - done(); - }); - }); - }); - }); - }); - - test('Should run nested writes in the same frame', function(done) { - var fastdom = new FastDom(); - var callback = sinon.spy(); - - fastdom.write(function() { - fastdom.write(function() { - fastdom.write(function() { - fastdom.write(function() { - - // Should not have scheduled a new frame - assert(fastdom.frames.length === 0); - done(); - }); - }); - }); - }); - }); - - test('Should call a "read" callback with the given context', function(done) { - var fastdom = new FastDom(); - var cb = sinon.spy(); - var ctx = { foo: 'bar' }; - - fastdom.read(function() { - assert.equal(this.foo, 'bar'); - done(); - }, ctx); - }); - - test('Should call a "write" callback with the given context', function(done) { - var fastdom = new FastDom(); - var cb = sinon.spy(); - var ctx = { foo: 'bar' }; - - fastdom.write(function() { - assert.equal(this.foo, 'bar'); - done(); - }, ctx); - }); - - test('Should have empty job hash when batch complete', function(done) { - var fastdom = new FastDom(); - - fastdom.read(function(){}); - fastdom.read(function(){}); - fastdom.write(function(){}); - fastdom.write(function(){}); - - // Check there are four jobs stored - assert.equal(objectLength(fastdom.batch.hash), 4); - - raf(function() { - assert.equal(objectLength(fastdom.batch.hash), 0); - done(); - }); - }); - - test('Should maintain correct context if single method is registered twice', function(done) { - var fastdom = new FastDom(); - var ctx1 = { foo: 'bar' }; - var ctx2 = { bar: 'baz' }; - - function shared(){} - - var spy1 = sinon.spy(shared); - var spy2 = sinon.spy(shared); - - fastdom.read(spy1, ctx1); - fastdom.read(spy2, ctx2); - - raf(function() { - assert(spy1.calledOn(ctx1)); - assert(spy2.calledOn(ctx2)); - done(); - }); - }); - - test('Should run onError handler if one has been registered', function(done) { - var fastdom = new FastDom(); - var err1 = { some: 'error1' }; - var err2 = { some: 'error2' }; - - fastdom.onError = sinon.spy(); - - fastdom.read(function() { - throw err1; - }); - - fastdom.write(function() { - throw err2; - }); - - raf(function() { - assert(fastdom.onError.calledTwice); - assert(fastdom.onError.getCall(0).calledWith(err1)); - assert(fastdom.onError.getCall(1).calledWith(err2)); - done(); - }); - }); - - test('Should stop rAF loop once frame queue is empty', function(done) { - var fastdom = new FastDom(); - var callback = sinon.spy(); - - fastdom.read(callback); - - raf(function() { - assert(callback.called); - assert(fastdom.looping === false); - done(); - }); - }); - - - test('Should continue to flush the queue until empty even if a job errors', function(done) { - var fastdom = new FastDom(); - var read = sinon.spy(); - var write = sinon.spy(); - var flush = fastdom.runBatch; - var error = sinon.stub().throws(); - var errorsThrown = false; - - sinon.stub(fastdom, 'runBatch', function() { - try { - flush.apply(fastdom, arguments); - } catch (e) { - errorsThrown = true; - } - }); - - fastdom.read(read); - fastdom.write(write); - fastdom.read(error); - fastdom.read(read); - fastdom.write(error); - fastdom.write(write); - - raf(function() { - assert(read.calledTwice, 'the callback was called both times'); - assert(write.calledTwice, 'the callback was called both times'); - assert(fastdom.batch.read.length === 0, 'the queue is empty'); - assert(fastdom.batch.write.length === 0, 'the queue is empty'); - assert(errorsThrown, 'real errors were thrown'); - done(); - }); - }); -}); |