1(function() { 2 var _ = typeof require == 'function' ? require('..') : window._; 3 var templateSettings; 4 5 QUnit.module('Utility', { 6 7 beforeEach: function() { 8 templateSettings = _.clone(_.templateSettings); 9 }, 10 11 afterEach: function() { 12 _.templateSettings = templateSettings; 13 } 14 15 }); 16 17 if (typeof this == 'object') { 18 QUnit.test('noConflict', function(assert) { 19 var underscore = _.noConflict(); 20 assert.equal(underscore.identity(1), 1); 21 if (typeof require != 'function') { 22 assert.equal(this._, void 0, 'global underscore is removed'); 23 this._ = underscore; 24 } else if (typeof global !== 'undefined') { 25 delete global._; 26 } 27 }); 28 } 29 30 if (typeof require == 'function') { 31 QUnit.test('noConflict (node vm)', function(assert) { 32 assert.expect(2); 33 var done = assert.async(); 34 var fs = require('fs'); 35 var vm = require('vm'); 36 var filename = __dirname + '/../underscore.js'; 37 fs.readFile(filename, function(err, content){ 38 var sandbox = vm.createScript( 39 content + 'this.underscore = this._.noConflict();', 40 filename 41 ); 42 var context = {_: 'oldvalue'}; 43 sandbox.runInNewContext(context); 44 assert.equal(context._, 'oldvalue'); 45 assert.equal(context.underscore.VERSION, _.VERSION); 46 47 done(); 48 }); 49 }); 50 } 51 52 QUnit.test('#750 - Return _ instance.', function(assert) { 53 assert.expect(2); 54 var instance = _([]); 55 assert.strictEqual(_(instance), instance); 56 assert.strictEqual(new _(instance), instance); 57 }); 58 59 QUnit.test('identity', function(assert) { 60 var stooge = {name: 'moe'}; 61 assert.equal(_.identity(stooge), stooge, 'stooge is the same as his identity'); 62 }); 63 64 QUnit.test('constant', function(assert) { 65 var stooge = {name: 'moe'}; 66 assert.equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge'); 67 }); 68 69 QUnit.test('noop', function(assert) { 70 assert.strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined'); 71 }); 72 73 QUnit.test('property', function(assert) { 74 var stooge = {name: 'moe'}; 75 assert.equal(_.property('name')(stooge), 'moe', 'should return the property with the given name'); 76 assert.equal(_.property('name')(null), void 0, 'should return undefined for null values'); 77 assert.equal(_.property('name')(void 0), void 0, 'should return undefined for undefined values'); 78 }); 79 80 QUnit.test('propertyOf', function(assert) { 81 var stoogeRanks = _.propertyOf({curly: 2, moe: 1, larry: 3}); 82 assert.equal(stoogeRanks('curly'), 2, 'should return the property with the given name'); 83 assert.equal(stoogeRanks(null), void 0, 'should return undefined for null values'); 84 assert.equal(stoogeRanks(void 0), void 0, 'should return undefined for undefined values'); 85 86 function MoreStooges() { this.shemp = 87; } 87 MoreStooges.prototype = {curly: 2, moe: 1, larry: 3}; 88 var moreStoogeRanks = _.propertyOf(new MoreStooges()); 89 assert.equal(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain'); 90 91 var nullPropertyOf = _.propertyOf(null); 92 assert.equal(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null'); 93 94 var undefPropertyOf = _.propertyOf(void 0); 95 assert.equal(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined'); 96 }); 97 98 QUnit.test('random', function(assert) { 99 var array = _.range(1000); 100 var min = Math.pow(2, 31); 101 var max = Math.pow(2, 62); 102 103 assert.ok(_.every(array, function() { 104 return _.random(min, max) >= min; 105 }), 'should produce a random number greater than or equal to the minimum number'); 106 107 assert.ok(_.some(array, function() { 108 return _.random(Number.MAX_VALUE) > 0; 109 }), 'should produce a random number when passed `Number.MAX_VALUE`'); 110 }); 111 112 QUnit.test('now', function(assert) { 113 var diff = _.now() - new Date().getTime(); 114 assert.ok(diff <= 0 && diff > -5, 'Produces the correct time in milliseconds');//within 5ms 115 }); 116 117 QUnit.test('uniqueId', function(assert) { 118 var ids = [], i = 0; 119 while (i++ < 100) ids.push(_.uniqueId()); 120 assert.equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); 121 }); 122 123 QUnit.test('times', function(assert) { 124 var vals = []; 125 _.times(3, function(i) { vals.push(i); }); 126 assert.deepEqual(vals, [0, 1, 2], 'is 0 indexed'); 127 // 128 vals = []; 129 _(3).times(function(i) { vals.push(i); }); 130 assert.deepEqual(vals, [0, 1, 2], 'works as a wrapper'); 131 // collects return values 132 assert.deepEqual([0, 1, 2], _.times(3, function(i) { return i; }), 'collects return values'); 133 134 assert.deepEqual(_.times(0, _.identity), []); 135 assert.deepEqual(_.times(-1, _.identity), []); 136 assert.deepEqual(_.times(parseFloat('-Infinity'), _.identity), []); 137 }); 138 139 QUnit.test('mixin', function(assert) { 140 var ret = _.mixin({ 141 myReverse: function(string) { 142 return string.split('').reverse().join(''); 143 } 144 }); 145 assert.equal(ret, _, 'returns the _ object to facilitate chaining'); 146 assert.equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); 147 assert.equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); 148 }); 149 150 QUnit.test('_.escape', function(assert) { 151 assert.equal(_.escape(null), ''); 152 }); 153 154 QUnit.test('_.unescape', function(assert) { 155 var string = 'Curly & Moe'; 156 assert.equal(_.unescape(null), ''); 157 assert.equal(_.unescape(_.escape(string)), string); 158 assert.equal(_.unescape(string), string, 'don\'t unescape unnecessarily'); 159 }); 160 161 // Don't care what they escape them to just that they're escaped and can be unescaped 162 QUnit.test('_.escape & unescape', function(assert) { 163 // test & (&) seperately obviously 164 var escapeCharacters = ['<', '>', '"', '\'', '`']; 165 166 _.each(escapeCharacters, function(escapeChar) { 167 var s = 'a ' + escapeChar + ' string escaped'; 168 var e = _.escape(s); 169 assert.notEqual(s, e, escapeChar + ' is escaped'); 170 assert.equal(s, _.unescape(e), escapeChar + ' can be unescaped'); 171 172 s = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar; 173 e = _.escape(s); 174 175 assert.equal(e.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar); 176 assert.equal(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped'); 177 }); 178 179 // handles multiple escape characters at once 180 var joiner = ' other stuff '; 181 var allEscaped = escapeCharacters.join(joiner); 182 allEscaped += allEscaped; 183 assert.ok(_.every(escapeCharacters, function(escapeChar) { 184 return allEscaped.indexOf(escapeChar) !== -1; 185 }), 'handles multiple characters'); 186 assert.ok(allEscaped.indexOf(joiner) >= 0, 'can escape multiple escape characters at the same time'); 187 188 // test & -> & 189 var str = 'some string & another string & yet another'; 190 var escaped = _.escape(str); 191 192 assert.notStrictEqual(escaped.indexOf('&'), -1, 'handles & aka &'); 193 assert.equal(_.unescape(str), str, 'can unescape &'); 194 }); 195 196 QUnit.test('template', function(assert) { 197 var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); 198 var result = basicTemplate({thing: 'This'}); 199 assert.equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); 200 201 var sansSemicolonTemplate = _.template('A <% this %> B'); 202 assert.equal(sansSemicolonTemplate(), 'A B'); 203 204 var backslashTemplate = _.template('<%= thing %> is \\ridanculous'); 205 assert.equal(backslashTemplate({thing: 'This'}), 'This is \\ridanculous'); 206 207 var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>'); 208 assert.equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); 209 210 var fancyTemplate = _.template('<ul><% ' + 211 ' for (var key in people) { ' + 212 '%><li><%= people[key] %></li><% } %></ul>'); 213 result = fancyTemplate({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}}); 214 assert.equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates'); 215 216 var escapedCharsInJavascriptTemplate = _.template('<ul><% _.each(numbers.split("\\n"), function(item) { %><li><%= item %></li><% }) %></ul>'); 217 result = escapedCharsInJavascriptTemplate({numbers: 'one\ntwo\nthree\nfour'}); 218 assert.equal(result, '<ul><li>one</li><li>two</li><li>three</li><li>four</li></ul>', 'Can use escaped characters (e.g. \\n) in JavaScript'); 219 220 var namespaceCollisionTemplate = _.template('<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %><div class="thumbnail" rel="<%= p %>"></div><% }); %>'); 221 result = namespaceCollisionTemplate({ 222 pageCount: 3, 223 thumbnails: { 224 1: 'p1-thumbnail.gif', 225 2: 'p2-thumbnail.gif', 226 3: 'p3-thumbnail.gif' 227 } 228 }); 229 assert.equal(result, '3 p3-thumbnail.gif <div class="thumbnail" rel="p1-thumbnail.gif"></div><div class="thumbnail" rel="p2-thumbnail.gif"></div><div class="thumbnail" rel="p3-thumbnail.gif"></div>'); 230 231 var noInterpolateTemplate = _.template('<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>'); 232 result = noInterpolateTemplate(); 233 assert.equal(result, '<div><p>Just some text. Hey, I know this is silly but it aids consistency.</p></div>'); 234 235 var quoteTemplate = _.template("It's its, not it's"); 236 assert.equal(quoteTemplate({}), "It's its, not it's"); 237 238 var quoteInStatementAndBody = _.template('<% ' + 239 " if(foo == 'bar'){ " + 240 "%>Statement quotes and 'quotes'.<% } %>"); 241 assert.equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); 242 243 var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); 244 assert.equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); 245 246 var template = _.template('<i><%- value %></i>'); 247 result = template({value: '<script>'}); 248 assert.equal(result, '<i><script></i>'); 249 250 var stooge = { 251 name: 'Moe', 252 template: _.template("I'm <%= this.name %>") 253 }; 254 assert.equal(stooge.template(), "I'm Moe"); 255 256 template = _.template('\n ' + 257 ' <%\n ' + 258 ' // a comment\n ' + 259 ' if (data) { data += 12345; }; %>\n ' + 260 ' <li><%= data %></li>\n ' 261 ); 262 assert.equal(template({data: 12345}).replace(/\s/g, ''), '<li>24690</li>'); 263 264 _.templateSettings = { 265 evaluate: /\{\{([\s\S]+?)\}\}/g, 266 interpolate: /\{\{=([\s\S]+?)\}\}/g 267 }; 268 269 var custom = _.template('<ul>{{ for (var key in people) { }}<li>{{= people[key] }}</li>{{ } }}</ul>'); 270 result = custom({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}}); 271 assert.equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates'); 272 273 var customQuote = _.template("It's its, not it's"); 274 assert.equal(customQuote({}), "It's its, not it's"); 275 276 quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}"); 277 assert.equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); 278 279 _.templateSettings = { 280 evaluate: /<\?([\s\S]+?)\?>/g, 281 interpolate: /<\?=([\s\S]+?)\?>/g 282 }; 283 284 var customWithSpecialChars = _.template('<ul><? for (var key in people) { ?><li><?= people[key] ?></li><? } ?></ul>'); 285 result = customWithSpecialChars({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}}); 286 assert.equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates'); 287 288 var customWithSpecialCharsQuote = _.template("It's its, not it's"); 289 assert.equal(customWithSpecialCharsQuote({}), "It's its, not it's"); 290 291 quoteInStatementAndBody = _.template("<? if(foo == 'bar'){ ?>Statement quotes and 'quotes'.<? } ?>"); 292 assert.equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); 293 294 _.templateSettings = { 295 interpolate: /\{\{(.+?)\}\}/g 296 }; 297 298 var mustache = _.template('Hello {{planet}}!'); 299 assert.equal(mustache({planet: 'World'}), 'Hello World!', 'can mimic mustache.js'); 300 301 var templateWithNull = _.template('a null undefined {{planet}}'); 302 assert.equal(templateWithNull({planet: 'world'}), 'a null undefined world', 'can handle missing escape and evaluate settings'); 303 }); 304 305 QUnit.test('_.template provides the generated function source, when a SyntaxError occurs', function(assert) { 306 var source; 307 try { 308 _.template('<b><%= if x %></b>'); 309 } catch (ex) { 310 source = ex.source; 311 } 312 assert.ok(/__p/.test(source)); 313 }); 314 315 QUnit.test('_.template handles \\u2028 & \\u2029', function(assert) { 316 var tmpl = _.template('<p>\u2028<%= "\\u2028\\u2029" %>\u2029</p>'); 317 assert.strictEqual(tmpl(), '<p>\u2028\u2028\u2029\u2029</p>'); 318 }); 319 320 QUnit.test('result calls functions and returns primitives', function(assert) { 321 var obj = {w: '', x: 'x', y: function(){ return this.x; }}; 322 assert.strictEqual(_.result(obj, 'w'), ''); 323 assert.strictEqual(_.result(obj, 'x'), 'x'); 324 assert.strictEqual(_.result(obj, 'y'), 'x'); 325 assert.strictEqual(_.result(obj, 'z'), void 0); 326 assert.strictEqual(_.result(null, 'x'), void 0); 327 }); 328 329 QUnit.test('result returns a default value if object is null or undefined', function(assert) { 330 assert.strictEqual(_.result(null, 'b', 'default'), 'default'); 331 assert.strictEqual(_.result(void 0, 'c', 'default'), 'default'); 332 assert.strictEqual(_.result(''.match('missing'), 1, 'default'), 'default'); 333 }); 334 335 QUnit.test('result returns a default value if property of object is missing', function(assert) { 336 assert.strictEqual(_.result({d: null}, 'd', 'default'), null); 337 assert.strictEqual(_.result({e: false}, 'e', 'default'), false); 338 }); 339 340 QUnit.test('result only returns the default value if the object does not have the property or is undefined', function(assert) { 341 assert.strictEqual(_.result({}, 'b', 'default'), 'default'); 342 assert.strictEqual(_.result({d: void 0}, 'd', 'default'), 'default'); 343 }); 344 345 QUnit.test('result does not return the default if the property of an object is found in the prototype', function(assert) { 346 var Foo = function(){}; 347 Foo.prototype.bar = 1; 348 assert.strictEqual(_.result(new Foo, 'bar', 2), 1); 349 }); 350 351 QUnit.test('result does use the fallback when the result of invoking the property is undefined', function(assert) { 352 var obj = {a: function() {}}; 353 assert.strictEqual(_.result(obj, 'a', 'failed'), void 0); 354 }); 355 356 QUnit.test('result fallback can use a function', function(assert) { 357 var obj = {a: [1, 2, 3]}; 358 assert.strictEqual(_.result(obj, 'b', _.constant(5)), 5); 359 assert.strictEqual(_.result(obj, 'b', function() { 360 return this.a; 361 }), obj.a, 'called with context'); 362 }); 363 364 QUnit.test('_.templateSettings.variable', function(assert) { 365 var s = '<%=data.x%>'; 366 var data = {x: 'x'}; 367 var tmp = _.template(s, {variable: 'data'}); 368 assert.strictEqual(tmp(data), 'x'); 369 _.templateSettings.variable = 'data'; 370 assert.strictEqual(_.template(s)(data), 'x'); 371 }); 372 373 QUnit.test('#547 - _.templateSettings is unchanged by custom settings.', function(assert) { 374 assert.notOk(_.templateSettings.variable); 375 _.template('', {}, {variable: 'x'}); 376 assert.notOk(_.templateSettings.variable); 377 }); 378 379 QUnit.test('#556 - undefined template variables.', function(assert) { 380 var template = _.template('<%=x%>'); 381 assert.strictEqual(template({x: null}), ''); 382 assert.strictEqual(template({x: void 0}), ''); 383 384 var templateEscaped = _.template('<%-x%>'); 385 assert.strictEqual(templateEscaped({x: null}), ''); 386 assert.strictEqual(templateEscaped({x: void 0}), ''); 387 388 var templateWithProperty = _.template('<%=x.foo%>'); 389 assert.strictEqual(templateWithProperty({x: {}}), ''); 390 assert.strictEqual(templateWithProperty({x: {}}), ''); 391 392 var templateWithPropertyEscaped = _.template('<%-x.foo%>'); 393 assert.strictEqual(templateWithPropertyEscaped({x: {}}), ''); 394 assert.strictEqual(templateWithPropertyEscaped({x: {}}), ''); 395 }); 396 397 QUnit.test('interpolate evaluates code only once.', function(assert) { 398 assert.expect(2); 399 var count = 0; 400 var template = _.template('<%= f() %>'); 401 template({f: function(){ assert.notOk(count++); }}); 402 403 var countEscaped = 0; 404 var templateEscaped = _.template('<%- f() %>'); 405 templateEscaped({f: function(){ assert.notOk(countEscaped++); }}); 406 }); 407 408 QUnit.test('#746 - _.template settings are not modified.', function(assert) { 409 assert.expect(1); 410 var settings = {}; 411 _.template('', null, settings); 412 assert.deepEqual(settings, {}); 413 }); 414 415 QUnit.test('#779 - delimeters are applied to unescaped text.', function(assert) { 416 assert.expect(1); 417 var template = _.template('<<\nx\n>>', null, {evaluate: /<<(.*?)>>/g}); 418 assert.strictEqual(template(), '<<\nx\n>>'); 419 }); 420 421}()); 422