hasher
Version:
Hasher is a set of JavaScript functions to control browser history for rich-media websites and applications
750 lines (501 loc) • 18.5 kB
JavaScript
/*jshint white:false, onevar:false, boss:true, noarg:false */
/*global test:false, stop:false, ok:false, hasher:false, module:false, equals:false, expect:false, start:false, same:false, console:false */
/*
*
* Hasher JS Unit test
*
*
* -----------------------------------------------------------------------
*
* WARNING:
* - poor code ahead!
* - don't use as reference!
*
* -----------------------------------------------------------------------
*
* IMPORTANT:
* - unit test doesn't work properly on Chrome <= 6 because of browser bug:
* http://code.google.com/p/chromium/issues/detail?id=45419
*
* -----------------------------------------------------------------------
*
*/
var DELAY_BACK_FORWARD = 80;
var TIMEOUT_BACK_FORWARD = 300;
location.hash = ''; //resets location.hash
/* ==== init and stop ==== */
module('init and stop');
test("init hasher", function(){
stop(2000);
expect(4);
ok(! hasher.initialized.getNumListeners(), "No INIT Listener");
hasher.initialized.add(function(){
ok(true, "INIT dispatched");
hasher.initialized.remove(arguments.callee);
ok(! hasher.initialized.getNumListeners(), "Removed INIT Listener");
start();
});
ok(hasher.initialized.getNumListeners(), "Added INIT Listener");
hasher.init();
});
var stopTest = function(){
stop(2000);
expect(5);
hasher.init(); // should be active to be able to stop
ok(hasher.isActive(), "Is active");
ok(! hasher.stopped.getNumListeners(), "No STOP Listener");
hasher.stopped.add(function(evt){
ok(true, "STOP dispatched");
hasher.stopped.remove(arguments.callee);
ok(! hasher.stopped.getNumListeners(), "Removed STOP Listener");
start();
});
ok(hasher.stopped.getNumListeners(), "Attached STOP Listener");
hasher.stop();
};
var stopFailTest = function(){
stop(2000);
expect(5);
hasher.stop(); //shouldn't be active
ok(! hasher.isActive(), "Is not active");
ok(! hasher.stopped.getNumListeners(), "No STOP Listener");
var handler = function(evt){
ok(false, "STOP dispatched"); //shouldn't happen
hasher.stopped.removeAll();
start();
};
hasher.stopped.add(handler);
ok(hasher.stopped.getNumListeners(), "Attached STOP Listener");
hasher.stop();
hasher.stop();
var delayedStart = function(){
ok(true, 'Didn\'t dispatched STOP event.');
hasher.stopped.removeAll();
ok(! hasher.stopped.getNumListeners(), "Removed STOP Listener");
start();
};
setTimeout(delayedStart, 100);
};
var initTest = function(){
stop(2000);
expect(6);
hasher.stop(); //shouldn't be active
ok(! hasher.isActive(), "Is not active");
ok(! hasher.initialized.getNumListeners(), "No INIT Listener");
hasher.initialized.add(function(evt){
ok(hasher.isActive(), "Is active");
ok(true, "INIT dispatched");
hasher.initialized.remove(arguments.callee);
ok(! hasher.initialized.getNumListeners(), "Removed INIT Listener");
start();
});
ok(hasher.initialized.getNumListeners(), "Attached INIT Listener");
hasher.init();
};
var initFailTest = function(){
stop(2000);
expect(6);
hasher.init(); //should be active
ok(hasher.isActive(), "Is active");
ok(! hasher.initialized.getNumListeners(), "No INIT Listener");
var handler = function(evt){
ok(false, "INIT dispatched"); //shouldn't happen
hasher.initialized.removeAll();
start();
};
hasher.initialized.add(handler);
ok(hasher.initialized.getNumListeners(), "Attached INIT Listener");
hasher.init();
hasher.init();
var delayedStart = function(){
ok(true, 'Didn\'t dispatched INIT event.');
ok(hasher.isActive(), "Is active");
hasher.initialized.removeAll();
ok(! hasher.initialized.getNumListeners(), "Removed INIT Listener");
start();
};
setTimeout(delayedStart, 100);
};
test('stop #1', stopTest);
test('stop fail #1', stopFailTest);
test('stop fail #2', stopFailTest);
test('init #1', initTest);
test('init fail #1', initFailTest);
test('init fail #2', initFailTest);
test('stop #2', stopTest);
test('init #2', initTest);
test('stop #3', stopTest);
/* == */
/* ==== prep code ==== */
var testsHashs = [
'foo',
'dolor',
'sit-amet/ipsum/?dolor=ipsum&maecennas=ullamcor',
'Spëçíãl Çhàrs FTW',
'/asd',
'/asd/qwerty/',
'/asd/qwerty/?foo=bar',
'lorem-ipsum',
'foo%bar%' // [issue #42]
];
/* ==== set/get hash value ==== */
module('set/get hash value');
function doChangeTest(i){
var testName = '#'+ (i + 1);
var hash = testsHashs[i];
var oldHash = (i > 0)? testsHashs[i - 1] : '';
test(testName, function(){
stop(2000);
expect(9);
hasher.init();
ok(! hasher.changed.getNumListeners(), "No CHANGED Listener");
hasher.changed.add(function($newHash, $oldHash){
ok(true, "CHANGED dispatched");
ok(($oldHash !== hasher.getHash()), "Hash value really changed.");
hasher.changed.remove(arguments.callee);
ok( ! hasher.changed.getNumListeners(), "Removed CHANGED Listener");
equals($oldHash, oldHash, "oldHash");
equals(hasher.getHash(), $newHash, "newHash == hasher.getHash()");
start();
});
ok(hasher.changed.getNumListeners() === 1, "Attached CHANGED Listener");
hasher.setHash(hash);
equals(hasher.getHash(), hash, "hasher.setHash() & hasher.getHash()");
var hashArray = hash.split('/');
same(hasher.getHashAsArray(), hashArray, "hasher.getHashAsArray()");
});
}
for(var i=0, t=testsHashs.length; i<t; i++){
doChangeTest(i);
}
/* == */
/* ==== back ==== */
module('back');
function doBackTest(n){
var testName = '#'+(n+1);
var hash = testsHashs[n];
var testFn = function(){
stop((n+1) * TIMEOUT_BACK_FORWARD);
expect(7);
hasher.initialized.removeAll();
hasher.stopped.removeAll();
hasher.changed.removeAll();
ok(!hasher.changed.getNumListeners(), "No CHANGED Listener");
hasher.changed.add(function($newHash, $oldHash){
ok(true, "CHANGED dispatched");
hasher.changed.remove(arguments.callee);
ok(!hasher.changed.getNumListeners(), "Removed CHANGED Listener");
equals(hasher.getHash(), $newHash, "newHash == hasher.getHash()");
equals(hasher.getHash(), hash, "hasher.getHash()");
var hashArray = hash.split('/');
same(hasher.getHashAsArray(), hashArray, "hasher.getHashAsArray()");
start();
});
ok(hasher.changed.getNumListeners(), "Attached CHANGED Listener");
setTimeout(function(){
window.history.back();
}, n * DELAY_BACK_FORWARD);
};
test(testName, testFn);
}
var n = testsHashs.length - 1;
while(n--){
doBackTest(n);
}
/* == */
/* ==== forward ==== */
module('forward');
function doForwardTest(n){
var testName = '#'+(n+1);
var hash = testsHashs[n];
var testFn = function(){
stop((n+1) * TIMEOUT_BACK_FORWARD);
expect(7);
hasher.initialized.removeAll();
hasher.stopped.removeAll();
hasher.changed.removeAll();
ok(!hasher.changed.getNumListeners(), "No CHANGED Listener");
hasher.changed.add(function($newHash, $oldHash){
ok(true, "CHANGED dispatched");
hasher.changed.remove(arguments.callee);
ok(!hasher.changed.getNumListeners(), "Removed CHANGED Listener");
equals(hasher.getHash(), $newHash, "newHash == hasher.getHash()");
equals(hasher.getHash(), hash, "hasher.getHash()");
var hashArray = hash.split('/');
same(hasher.getHashAsArray(), hashArray, "hasher.getHashAsArray()");
start();
});
ok(hasher.changed.getNumListeners(), "Attached CHANGED Listener");
setTimeout(function(){
window.history.forward();
}, n * DELAY_BACK_FORWARD);
};
test(testName, testFn);
}
for(i=1; i < testsHashs.length; i++){
doForwardTest(i);
}
/* == */
/* ==== multiple listeners & changes ==== */
module();
test('multiple listeners & changes', function (){
stop(2000);
expect(25);
function handler1(evt){
ok(true, 'Called Handler #1');
}
function handler2(evt){
ok(true, 'Called Handler #2');
}
function handler3(evt){
ok(true, 'Called Handler #3');
}
hasher.initialized.removeAll();
hasher.stopped.removeAll();
hasher.changed.removeAll();
hasher.init();
ok(!hasher.changed.getNumListeners(), "No CHANGED Listener");
hasher.changed.add(handler1);
hasher.changed.add(handler2);
hasher.changed.add(handler3);
ok(hasher.changed.getNumListeners(), "Has CHANGED Listener");
hasher.setHash('Lorem');
equals(hasher.getHash(), 'Lorem');
hasher.setHash('Lorem/Ipsum');
equals(hasher.getHash(), 'Lorem/Ipsum');
hasher.setHash('Lorem/Dolor&Amet/?foo=bar&ipsum=dolor&n=123&nan=abc123');
equals(hasher.getHash(), 'Lorem/Dolor&Amet/?foo=bar&ipsum=dolor&n=123&nan=abc123');
hasher.setHash('?foo=bar&ipsum=dolor');
equals(hasher.getHash(), '?foo=bar&ipsum=dolor');
ok(! hasher.initialized.getNumListeners(), "No INIT Listener");
hasher.initialized.add(function(e){
ok(false, "INIT event dispatched by accident"); //shouldn't run
});
ok(hasher.initialized.getNumListeners(), "Has INIT Listener");
ok(! hasher.stopped.getNumListeners(), "No STOP Listener");
hasher.stopped.add(function(e){
ok(false, "STOP event dispatched by accident"); //shouldn't run
});
ok(hasher.stopped.getNumListeners(), "Has STOP Listener");
hasher.initialized.removeAll();
ok(! hasher.initialized.getNumListeners(), "No INIT Listener");
hasher.changed.removeAll();
ok(! hasher.changed.getNumListeners(), "No CHANGED Listener");
hasher.stopped.removeAll();
ok(! hasher.stopped.getNumListeners(), "No STOP Listener");
hasher.setHash(''); //resets hash value
start();
});
test('set hash as rest param', function(){
stop(500);
hasher.init();
var oldHash = hasher.getHash();
var hash = 'lorem/ipsum/dolor/sit-amet';
ok(! hasher.changed.getNumListeners(), "No CHANGED Listener");
hasher.changed.add(function($newHash, $oldHash){
ok(true, "CHANGED dispatched");
ok(($oldHash !== hasher.getHash()), "Hash value really changed.");
hasher.changed.remove(arguments.callee);
ok( ! hasher.changed.getNumListeners(), "Removed CHANGED Listener");
equals($oldHash, oldHash, "oldHash");
equals(hasher.getHash(), $newHash, "newHash == hasher.getHash()");
start();
});
ok(hasher.changed.getNumListeners() === 1, "Attached CHANGED Listener");
hasher.setHash('lorem', 'ipsum', 'dolor', 'sit-amet');
equals(hasher.getHash(), hash, "hasher.setHash() & hasher.getHash()");
var hashArray = hash.split('/');
same(hasher.getHashAsArray(), hashArray, "hasher.getHashAsArray()");
});
test('prepend/append/separator', function(){
stop(500);
hasher.init();
hasher.separator = '_';
hasher.prependHash = '=';
hasher.appendHash = '=';
var oldHash = hasher.getHash();
var hash = 'lorem_ipsum_dolor_sit-amet';
ok(! hasher.changed.getNumListeners(), "No CHANGED Listener");
hasher.changed.add(function($newHash, $oldHash){
ok(true, "CHANGED dispatched");
ok(($oldHash !== hasher.getHash()), "Hash value really changed.");
hasher.changed.remove(arguments.callee);
ok( ! hasher.changed.getNumListeners(), "Removed CHANGED Listener");
equals($oldHash, oldHash, "oldHash");
equals(hasher.getHash(), $newHash, "newHash == hasher.getHash()");
start();
});
ok(hasher.changed.getNumListeners() === 1, "Attached CHANGED Listener");
hasher.setHash('lorem', 'ipsum', 'dolor', 'sit-amet');
equals(hasher.getHash(), hash, "hasher.setHash() & hasher.getHash()");
equals(window.location.hash, '#'+ hasher.prependHash + hash + hasher.appendHash, "append & prepend really works");
var hashArray = hash.split('_');
same(hasher.getHashAsArray(), hashArray, "hasher.getHashAsArray()");
//reset defaults
hasher.separator = '/';
hasher.prependHash = '/';
hasher.appendHash = '';
});
test('replaceHash', function(){
stop(500);
expect(13);
var _countReplace = 0,
_countBack = 0,
_olds = ['=lorem_ipsum_dolor_sit-amet=', 'lorem/ipsum/dolor/sit-amet'],
_prevs = ['lorem/ipsum/dolor/sit-amet'],
_nexts = ['foo/bar', 'dolor/123'];
function onReplace($newHash, $oldHash){
ok(true, 'CHANGED dispatched');
hasher.changed.remove(onReplace);
hasher.changed.add(onBack);
ok(($oldHash !== hasher.getHash()), "Hash value really changed.");
equals($oldHash, _olds[_countReplace], "oldHash");
equals(hasher.getHash(), $newHash, "newHash == hasher.getHash()");
_countReplace += 1;
if(_countReplace === 2){
hasher.changed.removeAll();
start();
} else {
setTimeout(function(){
window.history.back();
}, DELAY_BACK_FORWARD);
}
}
function onBack($newHash, $oldHash){
ok(true, 'CHANGED dispatched');
hasher.changed.remove(onBack);
hasher.changed.add(onReplace);
ok(($oldHash !== hasher.getHash()), "Hash value really changed.");
equals($oldHash, _nexts[_countBack], "oldHash");
equals(hasher.getHash(), $newHash, "newHash == hasher.getHash()");
equals(_prevs[_countBack], $newHash, "newHash == _prevs[_countBack]");
_countBack += 1;
setTimeout(function(){
hasher.replaceHash(_nexts[_countReplace]);
}, DELAY_BACK_FORWARD);
}
hasher.changed.add(onReplace);
hasher.replaceHash(_nexts[0]);
});
/* == */
module();
test('multiple redirects [issue #39]', function(){
stop(1500);
expect(9);
hasher.init();
var n = 0;
var hashChangeHandler = function(newHash, prevHash) {
n += 1;
equals(hasher.getHash(), newHash, 'hasher.getHash() === newHash');
if (newHash === 'zero') {
equals(prevHash, '', 'prevHash === ""');
hasher.replaceHash('one');
} else if (newHash === 'one') {
equals(prevHash, 'zero', 'prevHash === "zero"');
hasher.replaceHash('two');
} else {
equals(prevHash, 'one', 'prevHash === "one"');
equals(newHash, 'two' , 'newHash === "two"');
}
};
hasher.replaceHash('');
hasher.changed.add( hashChangeHandler );
hasher.replaceHash('zero');
setTimeout(function(){
hasher.changed.remove( hashChangeHandler );
equals(n, 3, 'only changed 3 times');
equals(window.location.hash, '#/two', 'updated location.hash');
start();
}, 1200);
});
/* ==== */
module();
test('IE8 local + hash query [issue #6]', function(){
stop(1500);
expect(9);
var count = 0;
var hashChangeHandler = function(newHash, oldHash){
ok(newHash !== oldHash, 'hash changed');
ok(newHash.indexOf('?') !== -1, 'has hash query');
count++;
};
hasher.changed.add(hashChangeHandler);
hasher.setHash('foo?lorem=ipsum');
setTimeout(function(){
hasher.setHash('foo?lorem=amet');
setTimeout(function(){
var b1 = hasher.changed.add(function(newHash, oldHash){
b1.detach();
equals(newHash, 'foo?lorem=ipsum', 'new hash');
equals(oldHash, 'foo?lorem=amet', 'old hash');
hasher.changed.remove(hashChangeHandler);
equals(count, 3, 'count');
start();
});
// it's a TRAP, check if encode/decode is working properly
window.history.back();
}, DELAY_BACK_FORWARD);
}, DELAY_BACK_FORWARD);
});
test('appendHash + $ [issue #49]', function(){
stop(2000);
hasher.init();
ok(! hasher.changed.getNumListeners(), "No CHANGED Listener");
var hashChangeHandler = function(newHash, oldHash){
ok(newHash !== oldHash, 'hash changed');
equals(newHash, 'foo$bar', 'new hash');
};
hasher.changed.add(hashChangeHandler);
hasher.appendHash = '';
hasher.setHash('foo$bar');
setTimeout(function(){
hasher.changed.remove(hashChangeHandler);
hasher.changed.add(function(newHash, oldHash){
equals(newHash, 'foo$lorem=amet', 'new hash');
equals(oldHash, 'foo$bar', 'old hash');
hasher.changed.removeAll();
start();
});
setTimeout(function(){
hasher.setHash('foo$lorem=amet');
}, DELAY_BACK_FORWARD);
}, DELAY_BACK_FORWARD);
});
/* ==== raw hashes ==== */
module();
test('raw hashes', function (){
stop(500);
hasher.raw = true;
hasher.init();
var hashChangeHandler = function(newHash, oldHash){
ok(newHash !== oldHash, 'hash changed');
equals(newHash, 'foo%20bar', 'new hash');
};
hasher.changed.add(hashChangeHandler);
hasher.appendHash = '';
hasher.setHash('foo%20bar');
setTimeout(function(){
hasher.changed.remove(hashChangeHandler);
hasher.changed.add(function(newHash, oldHash){
equals(newHash, 'foo%20lorem%3Damet', 'new hash');
equals(oldHash, 'foo%20bar', 'old hash');
hasher.changed.removeAll();
start();
});
setTimeout(function(){
hasher.setHash('foo%20lorem%3Damet');
}, DELAY_BACK_FORWARD);
}, DELAY_BACK_FORWARD);
});
/* ==== dispose ==== */
module();
test('dispose', function(){
stop(500);
expect(3);
ok((hasher), "hasher exists");
hasher.dispose();
ok((! hasher), "! hasher");
ok((hasher == null), "hasher == null");
start();
});
/* == */