unit.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. QUnit.begin = function() {
  2. console.log("Starting test suite");
  3. console.log("================================================\n");
  4. };
  5. QUnit.moduleDone = function(opts) {
  6. if(opts.failed === 0) {
  7. console.log("\u2714 All tests passed in '"+opts.name+"' module");
  8. } else {
  9. console.log("\u2716 "+ opts.failed +" tests failed in '"+opts.name+"' module");
  10. }
  11. };
  12. QUnit.done = function(opts) {
  13. console.log("\n================================================");
  14. console.log("Tests completed in "+opts.runtime+" milliseconds");
  15. console.log(opts.passed + " tests of "+opts.total+" passed, "+opts.failed+" failed.");
  16. };
  17. module('Basics', {
  18. setup:function() {
  19. },
  20. teardown:function() {
  21. }
  22. });
  23. test("globals set up", function() {
  24. ok(window.Modernizr, 'global modernizr object created');
  25. });
  26. test("bind is implemented", function() {
  27. ok(Function.prototype.bind, 'bind is a member of Function.prototype');
  28. var a = function(){
  29. return this.modernizr;
  30. };
  31. a = a.bind({modernizr: 'just awsome'});
  32. equal("just awsome", a(), 'bind works as expected');
  33. // thank you webkit layoutTests
  34. var result;
  35. function F(x, y)
  36. {
  37. result = this + " -> x:" + x + ", y:" + y;
  38. }
  39. G = F.bind("'a'", "'b'");
  40. H = G.bind("'Cannot rebind this!'", "'c'");
  41. G(1,2);
  42. equal(result, "\'a\' -> x:\'b\', y:1");
  43. H(1,2);
  44. equal(result, "\'a\' -> x:\'b\', y:\'c\'");
  45. var f = new F(1,2);
  46. equal(result, "[object Object] -> x:1, y:2");
  47. var g = new G(1,2);
  48. equal(result, "[object Object] -> x:\'b\', y:1");
  49. var h = new H(1,2);
  50. equal(result, "[object Object] -> x:\'b\', y:\'c\'");
  51. ok(f instanceof F, "f instanceof F");
  52. ok(g instanceof F, "g instanceof F");
  53. ok(h instanceof F, "h instanceof F");
  54. // Bound functions don't have a 'prototype' property.
  55. ok("prototype" in F, '"prototype" in F');
  56. // The object passed to bind as 'this' must be callable.
  57. raises(function(){
  58. Function.bind.call(undefined);
  59. });
  60. // Objects that allow call but not construct can be bound, but should throw if used with new.
  61. var abcAt = String.prototype.charAt.bind("abc");
  62. equal(abcAt(1), "b", 'Objects that allow call but not construct can be bound...');
  63. equal(1, Function.bind.length, 'it exists');
  64. });
  65. test("document.documentElement is valid and correct",1, function() {
  66. equal(document.documentElement,document.getElementsByTagName('html')[0]);
  67. });
  68. test("no-js class is gone.", function() {
  69. ok(!/(?:^|\s)no-js(?:^|\s)/.test(document.documentElement.className),
  70. 'no-js class is gone');
  71. ok(/(?:^|\s)js(?:^|\s)/.test(document.documentElement.className),
  72. 'html.js class is present');
  73. ok(/(?:^|\s)\+no-js(?:\s|$)/.test(document.documentElement.className),
  74. 'html.+no-js class is still present');
  75. ok(/(?:^|\s)no-js-(?:\s|$)/.test(document.documentElement.className),
  76. 'html.no-js- class is still present');
  77. ok(/(?:^|\s)i-has-no-js(?:\s|$)/.test(document.documentElement.className),
  78. 'html.i-has-no-js class is still present');
  79. if (document.querySelector){
  80. ok(document.querySelector('html.js') == document.documentElement,
  81. "document.querySelector('html.js') matches.");
  82. }
  83. });
  84. test('html shim worked', function(){
  85. expect(2);
  86. // the exact test we use in the script
  87. var elem = document.getElementsByTagName("section")[0];
  88. elem.id = "html5section";
  89. ok( elem.childNodes.length === 1 , 'unknown elements dont collapse');
  90. elem.style.color = 'red';
  91. ok( /red|#ff0000/i.test(elem.style.color), 'unknown elements are styleable')
  92. });
  93. module('Modernizr classes and bools', {
  94. setup:function() {
  95. },
  96. teardown:function() {
  97. }
  98. });
  99. test('html classes are looking good',function(){
  100. var classes = TEST.trim(document.documentElement.className).split(/\s+/);
  101. var modprops = Object.keys(Modernizr),
  102. newprops = modprops;
  103. // decrement for the properties that are private
  104. for (var i = -1, len = TEST.privates.length; ++i < len; ){
  105. var item = TEST.privates[i];
  106. equal(-1, TEST.inArray(item, classes), 'private Modernizr object '+ item +'should not have matching classes');
  107. equal(-1, TEST.inArray('no-' + item, classes), 'private Modernizr object no-'+item+' should not have matching classes');
  108. }
  109. // decrement for the non-boolean objects
  110. // for (var i = -1, len = TEST.inputs.length; ++i < len; ){
  111. // if (Modernizr[TEST.inputs[i]] != undefined) newprops--;
  112. // }
  113. // TODO decrement for the extraclasses
  114. // decrement for deprecated ones.
  115. $.each( TEST.deprecated, function(key, val){
  116. newprops.splice( TEST.inArray(item, newprops), 1);
  117. });
  118. //equal(classes,newprops,'equal number of classes and global object props');
  119. if (classes.length !== newprops){
  120. //window.console && console.log(classes, newprops);
  121. }
  122. for (var i = 0, len = classes.length, aclass; i <len; i++){
  123. aclass = classes[i];
  124. // Skip js related classes.
  125. if (/^(?:js|\+no-js|no-js-|i-has-no-js)$/.test(aclass)) continue;
  126. if (aclass.indexOf('no-') === 0){
  127. aclass = aclass.replace('no-','');
  128. equal(Modernizr[aclass], false,
  129. aclass + ' is correctly false in the classes and object')
  130. } else {
  131. equal(Modernizr[aclass], true,
  132. aclass + ' is correctly true in the classes and object')
  133. }
  134. }
  135. for (var i = 0, len = classes.length, aclass; i <len; i++){
  136. equal(classes[i],classes[i].toLowerCase(),'all classes are lowerCase.');
  137. }
  138. // Remove fake no-js classes before test.
  139. var docElClass = document.documentElement.className;
  140. $.each(['\\+no-js', 'no-js-', 'i-has-no-js'], function(i, fakeClass) {
  141. docElClass = docElClass.replace(new RegExp('(^|\\s)' + fakeClass + '(\\s|$)', 'g'), '$1$2');
  142. });
  143. equal(/[^\s]no-/.test(docElClass), false, 'whitespace between all classes.');
  144. })
  145. test('Modernizr properties are looking good',function(){
  146. var count = 0,
  147. nobool = TEST.API.concat(TEST.inputs)
  148. .concat(TEST.audvid)
  149. .concat(TEST.privates)
  150. .concat(['textarea']); // due to forms-placeholder.js test
  151. for (var prop in window.Modernizr){
  152. if (Modernizr.hasOwnProperty(prop)){
  153. if (TEST.inArray(prop,nobool) >= 0) continue;
  154. ok(Modernizr[prop] === true || Modernizr[prop] === false,
  155. 'Modernizr.'+prop+' is a straight up boolean');
  156. equal(prop,prop.toLowerCase(),'all properties are lowerCase.')
  157. }
  158. }
  159. })
  160. test('Modernizr.audio and Modernizr.video',function(){
  161. for (var i = -1, len = TEST.audvid.length; ++i < len;){
  162. var prop = TEST.audvid[i];
  163. if (Modernizr[prop].toString() == 'true'){
  164. ok(Modernizr[prop], 'Modernizr.'+prop+' is truthy.');
  165. equal(Modernizr[prop] == true,true, 'Modernizr.'+prop+' is == true')
  166. equal(typeof Modernizr[prop] === 'object',true,'Moderizr.'+prop+' is truly an object');
  167. equal(Modernizr[prop] !== true,true, 'Modernizr.'+prop+' is !== true')
  168. } else {
  169. equal(Modernizr[prop] != true,true, 'Modernizr.'+prop+' is != true')
  170. }
  171. }
  172. });
  173. test('Modernizr results match expected values',function(){
  174. // i'm bringing over a few tests from inside Modernizr.js
  175. equal(!!document.createElement('canvas').getContext,Modernizr.canvas,'canvas test consistent');
  176. equal(!!window.Worker,Modernizr.webworkers,'web workers test consistent')
  177. });
  178. module('Modernizr\'s API methods', {
  179. setup:function() {
  180. },
  181. teardown:function() {
  182. }
  183. });
  184. test('Modernizr.addTest()',22,function(){
  185. var docEl = document.documentElement;
  186. Modernizr.addTest('testtrue',function(){
  187. return true;
  188. });
  189. Modernizr.addTest('testtruthy',function(){
  190. return 100;
  191. });
  192. Modernizr.addTest('testfalse',function(){
  193. return false;
  194. });
  195. Modernizr.addTest('testfalsy',function(){
  196. return undefined;
  197. });
  198. ok(docEl.className.indexOf(' testtrue') >= 0,'positive class added');
  199. equal(Modernizr.testtrue,true,'positive prop added');
  200. ok(docEl.className.indexOf(' testtruthy') >= 0,'positive class added');
  201. equal(Modernizr.testtruthy,100,'truthy value is not casted to straight boolean');
  202. ok(docEl.className.indexOf(' no-testfalse') >= 0,'negative class added');
  203. equal(Modernizr.testfalse,false,'negative prop added');
  204. ok(docEl.className.indexOf(' no-testfalsy') >= 0,'negative class added');
  205. equal(Modernizr.testfalsy,undefined,'falsy value is not casted to straight boolean');
  206. Modernizr.addTest('testcamelCase',function(){
  207. return true;
  208. });
  209. ok(docEl.className.indexOf(' testcamelCase') === -1,
  210. 'camelCase test name toLowerCase()\'d');
  211. // okay new signature for this API! woo
  212. Modernizr.addTest('testboolfalse', false);
  213. ok(~docEl.className.indexOf(' no-testboolfalse'), 'Modernizr.addTest(feature, bool): negative class added');
  214. equal(Modernizr.testboolfalse, false, 'Modernizr.addTest(feature, bool): negative prop added');
  215. Modernizr.addTest('testbooltrue', true);
  216. ok(~docEl.className.indexOf(' testbooltrue'), 'Modernizr.addTest(feature, bool): positive class added');
  217. equal(Modernizr.testbooltrue, true, 'Modernizr.addTest(feature, bool): positive prop added');
  218. Modernizr.addTest({'testobjboolfalse': false,
  219. 'testobjbooltrue' : true });
  220. ok(~docEl.className.indexOf(' no-testobjboolfalse'), 'Modernizr.addTest({feature: bool}): negative class added');
  221. equal(Modernizr.testobjboolfalse, false, 'Modernizr.addTest({feature: bool}): negative prop added');
  222. ok(~docEl.className.indexOf(' testobjbooltrue'), 'Modernizr.addTest({feature: bool}): positive class added');
  223. equal(Modernizr.testobjbooltrue, true, 'Modernizr.addTest({feature: bool}): positive prop added');
  224. Modernizr.addTest({'testobjfnfalse': function(){ return false },
  225. 'testobjfntrue' : function(){ return true } });
  226. ok(~docEl.className.indexOf(' no-testobjfnfalse'), 'Modernizr.addTest({feature: bool}): negative class added');
  227. equal(Modernizr.testobjfnfalse, false, 'Modernizr.addTest({feature: bool}): negative prop added');
  228. ok(~docEl.className.indexOf(' testobjfntrue'), 'Modernizr.addTest({feature: bool}): positive class added');
  229. equal(Modernizr.testobjfntrue, true, 'Modernizr.addTest({feature: bool}): positive prop added');
  230. Modernizr
  231. .addTest('testchainone', true)
  232. .addTest({ testchaintwo: true })
  233. .addTest('testchainthree', function(){ return true; });
  234. ok( Modernizr.testchainone == Modernizr.testchaintwo == Modernizr.testchainthree, 'addTest is chainable');
  235. }); // eo addTest
  236. test('Modernizr.mq: media query testing',function(){
  237. var $html = $('html');
  238. $.mobile = {};
  239. // from jquery mobile
  240. $.mobile.media = (function() {
  241. // TODO: use window.matchMedia once at least one UA implements it
  242. var cache = {},
  243. testDiv = $( "<div id='jquery-mediatest'>" ),
  244. fakeBody = $( "<body>" ).append( testDiv );
  245. return function( query ) {
  246. if ( !( query in cache ) ) {
  247. var styleBlock = document.createElement('style'),
  248. cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }";
  249. //must set type for IE!
  250. styleBlock.type = "text/css";
  251. if (styleBlock.styleSheet){
  252. styleBlock.styleSheet.cssText = cssrule;
  253. }
  254. else {
  255. styleBlock.appendChild(document.createTextNode(cssrule));
  256. }
  257. $html.prepend( fakeBody ).prepend( styleBlock );
  258. cache[ query ] = testDiv.css( "position" ) === "absolute";
  259. fakeBody.add( styleBlock ).remove();
  260. }
  261. return cache[ query ];
  262. };
  263. })();
  264. ok(Modernizr.mq,'Modernizr.mq() doesn\' freak out.');
  265. equal($.mobile.media('only screen'), Modernizr.mq('only screen'),'screen media query matches jQuery mobile\'s result');
  266. equal(Modernizr.mq('only all'), Modernizr.mq('only all'), 'Cache hit matches');
  267. });
  268. test('Modernizr.hasEvent()',function(){
  269. ok(typeof Modernizr.hasEvent == 'function','Modernizr.hasEvent() is a function');
  270. equal(Modernizr.hasEvent('click'), true,'click event is supported');
  271. equal(Modernizr.hasEvent('modernizrcustomevent'), false,'random event is definitely not supported');
  272. /* works fine in webkit but not gecko
  273. equal( Modernizr.hasEvent('resize', window),
  274. !Modernizr.hasEvent('resize', document.createElement('div')),
  275. 'Resize is supported in window but not a div, typically...');
  276. */
  277. });
  278. test('Modernizr.testStyles()',function(){
  279. equal(typeof Modernizr.testStyles, 'function','Modernizr.testStyles() is a function');
  280. var style = '#modernizr{ width: 9px; height: 4px; font-size: 0; color: papayawhip; }';
  281. Modernizr.testStyles(style, function(elem, rule){
  282. equal(style, rule, 'rule passsed back matches what i gave it.')
  283. equal(elem.offsetWidth, 9, 'width was set through the style');
  284. equal(elem.offsetHeight, 4, 'height was set through the style');
  285. equal(elem.id, 'modernizr', 'element is indeed the modernizr element');
  286. });
  287. });
  288. test('Modernizr._[properties]',function(){
  289. equal(6, Modernizr._prefixes.length, 'Modernizr._prefixes has 6 items');
  290. equal(4, Modernizr._domPrefixes.length, 'Modernizr.domPrefixes has 4 items');
  291. });
  292. test('Modernizr.testProp()',function(){
  293. equal(true, Modernizr.testProp('margin'), 'Everyone supports margin');
  294. equal(false, Modernizr.testProp('happiness'), 'Nobody supports the happiness style. :(');
  295. equal(true, Modernizr.testProp('fontSize'), 'Everyone supports fontSize');
  296. equal(false, Modernizr.testProp('font-size'), 'Nobody supports font-size');
  297. equal('pointerEvents' in document.createElement('div').style,
  298. Modernizr.testProp('pointerEvents'),
  299. 'results for `pointer-events` are consistent with a homegrown feature test');
  300. });
  301. test('Modernizr.testAllProps()',function(){
  302. equal(true, Modernizr.testAllProps('margin'), 'Everyone supports margin');
  303. equal(false, Modernizr.testAllProps('happiness'), 'Nobody supports the happiness style. :(');
  304. equal(true, Modernizr.testAllProps('fontSize'), 'Everyone supports fontSize');
  305. equal(false, Modernizr.testAllProps('font-size'), 'Nobody supports font-size');
  306. equal(Modernizr.csstransitions, Modernizr.testAllProps('transition'), 'Modernizr result matches API result: csstransitions');
  307. equal(Modernizr.csscolumns, Modernizr.testAllProps('columnCount'), 'Modernizr result matches API result: csscolumns')
  308. });
  309. test('Modernizr.prefixed() - css and DOM resolving', function(){
  310. // https://gist.github.com/523692
  311. function gimmePrefix(prop, obj){
  312. var prefixes = ['Moz','Khtml','Webkit','O','ms'],
  313. domPrefixes = ['moz','khtml','webkit','o','ms'],
  314. elem = document.createElement('div'),
  315. upper = prop.charAt(0).toUpperCase() + prop.slice(1);
  316. if(!obj) {
  317. if (prop in elem.style)
  318. return prop;
  319. for (var len = prefixes.length; len--; ){
  320. if ((prefixes[len] + upper) in elem.style)
  321. return (prefixes[len] + upper);
  322. }
  323. } else {
  324. if (prop in obj)
  325. return prop;
  326. for (var len = domPrefixes.length; len--; ){
  327. if ((domPrefixes[len] + upper) in obj)
  328. return (domPrefixes[len] + upper);
  329. }
  330. }
  331. return false;
  332. }
  333. var propArr = ['transition', 'backgroundSize', 'boxSizing', 'borderImage',
  334. 'borderRadius', 'boxShadow', 'columnCount'];
  335. var domPropArr = [{ 'prop': 'requestAnimationFrame', 'obj': window },
  336. { 'prop': 'querySelectorAll', 'obj': document },
  337. { 'prop': 'matchesSelector', 'obj': document.createElement('div') }];
  338. for (var i = -1, len = propArr.length; ++i < len; ){
  339. var prop = propArr[i];
  340. equal(Modernizr.prefixed(prop), gimmePrefix(prop), 'results for ' + prop + ' match the homebaked prefix finder');
  341. }
  342. for (var i = -1, len = domPropArr.length; ++i < len; ){
  343. var prop = domPropArr[i];
  344. ok(!!~Modernizr.prefixed(prop.prop, prop.obj, false).toString().indexOf(gimmePrefix(prop.prop, prop.obj)), 'results for ' + prop.prop + ' match the homebaked prefix finder');
  345. }
  346. });
  347. // FIXME: so a few of these are whitelisting for webkit. i'd like to improve that.
  348. test('Modernizr.prefixed autobind', function(){
  349. var rAFName;
  350. // quick sniff to find the local rAF prefixed name.
  351. var vendors = ['ms', 'moz', 'webkit', 'o'];
  352. for(var x = 0; x < vendors.length && !rAFName; ++x) {
  353. rAFName = window[vendors[x]+'RequestAnimationFrame'] && vendors[x]+'RequestAnimationFrame';
  354. }
  355. if (rAFName){
  356. // rAF returns a function
  357. equal(
  358. 'function',
  359. typeof Modernizr.prefixed('requestAnimationFrame', window),
  360. "Modernizr.prefixed('requestAnimationFrame', window) returns a function")
  361. // unless we false it to a string
  362. equal(
  363. rAFName,
  364. Modernizr.prefixed('requestAnimationFrame', window, false),
  365. "Modernizr.prefixed('requestAnimationFrame', window, false) returns a string (the prop name)")
  366. }
  367. if (document.body.webkitMatchesSelector || document.body.mozMatchesSelector){
  368. var fn = Modernizr.prefixed('matchesSelector', HTMLElement.prototype, document.body);
  369. //returns function
  370. equal(
  371. 'function',
  372. typeof fn,
  373. "Modernizr.prefixed('matchesSelector', HTMLElement.prototype, document.body) returns a function");
  374. // fn scoping
  375. equal(
  376. true,
  377. fn('body'),
  378. "Modernizr.prefixed('matchesSelector', HTMLElement.prototype, document.body) is scoped to the body")
  379. }
  380. // Webkit only: are there other objects that are prefixed?
  381. if (window.webkitNotifications){
  382. // should be an object.
  383. equal(
  384. 'object',
  385. typeof Modernizr.prefixed('Notifications', window),
  386. "Modernizr.prefixed('Notifications') returns an object");
  387. }
  388. // Webkit only:
  389. if (typeof document.webkitIsFullScreen !== 'undefined'){
  390. // boolean
  391. equal(
  392. 'boolean',
  393. typeof Modernizr.prefixed('isFullScreen', document),
  394. "Modernizr.prefixed('isFullScreen') returns a boolean");
  395. }
  396. // Moz only:
  397. if (typeof document.mozFullScreen !== 'undefined'){
  398. // boolean
  399. equal(
  400. 'boolean',
  401. typeof Modernizr.prefixed('fullScreen', document),
  402. "Modernizr.prefixed('fullScreen') returns a boolean");
  403. }
  404. // Webkit-only.. takes advantage of Webkit's mixed case of prefixes
  405. if (document.body.style.WebkitAnimation){
  406. // string
  407. equal(
  408. 'string',
  409. typeof Modernizr.prefixed('animation', document.body.style),
  410. "Modernizr.prefixed('animation', document.body.style) returns value of that, as a string");
  411. equal(
  412. animationStyle.toLowerCase(),
  413. Modernizr.prefixed('animation', document.body.style, false).toLowerCase(),
  414. "Modernizr.prefixed('animation', document.body.style, false) returns the (case-normalized) name of the property: webkitanimation");
  415. }
  416. equal(
  417. false,
  418. Modernizr.prefixed('doSomethingAmazing$#$', window),
  419. "Modernizr.prefixed('doSomethingAmazing$#$', window) : Gobbledygook with prefixed(str,obj) returns false");
  420. equal(
  421. false,
  422. Modernizr.prefixed('doSomethingAmazing$#$', window, document.body),
  423. "Modernizr.prefixed('doSomethingAmazing$#$', window) : Gobbledygook with prefixed(str,obj, scope) returns false");
  424. equal(
  425. false,
  426. Modernizr.prefixed('doSomethingAmazing$#$', window, false),
  427. "Modernizr.prefixed('doSomethingAmazing$#$', window) : Gobbledygook with prefixed(str,obj, false) returns false");
  428. });